Skip to content

Commit 84b327e

Browse files
Copilotaarne
andauthored
refactor(bridge-core): extract gate helpers from resolveWiresAsync (#85)
* Initial plan * refactor: extract gate helpers from resolveWiresAsync and add unit tests Co-authored-by: aarne <82001+aarne@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: aarne <82001+aarne@users.noreply.github.com>
1 parent c7a69d9 commit 84b327e

2 files changed

Lines changed: 336 additions & 37 deletions

File tree

packages/bridge-core/src/resolveWires.ts

Lines changed: 99 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ import type { MaybePromise, TreeContext } from "./tree-types.ts";
1414
import { isFatalError, isPromise, applyControlFlow, BridgeAbortError } from "./tree-types.ts";
1515
import { coerceConstant, getSimplePullRef } from "./tree-utils.ts";
1616

17+
// ── Wire type helpers ────────────────────────────────────────────────────────
18+
19+
/**
20+
* A non-constant wire — any Wire variant that carries gate modifiers
21+
* (`falsyFallback`, `nullishFallbackRef`, `catchFallback`, etc.).
22+
* Excludes the `{ value: string; to: NodeRef }` constant wire which has no
23+
* modifier slots.
24+
*/
25+
type WireWithGates = Exclude<Wire, { value: string }>;
26+
1727
// ── Public entry point ──────────────────────────────────────────────────────
1828

1929
/**
@@ -66,8 +76,8 @@ async function resolveWiresAsync(
6676
ctx: TreeContext,
6777
wires: Wire[],
6878
pullChain?: Set<string>,
69-
): Promise<any> {
70-
let lastError: any;
79+
): Promise<unknown> {
80+
let lastError: unknown;
7181

7282
for (const w of wires) {
7383
// Abort discipline — yield immediately if client disconnected
@@ -77,45 +87,24 @@ async function resolveWiresAsync(
7787
if ("value" in w) return coerceConstant(w.value);
7888

7989
try {
80-
// --- Layer 1: Execution ---
81-
let resolvedValue = await evaluateWireSource(ctx, w, pullChain);
82-
83-
// --- Layer 2a: Falsy Gate (||) ---
84-
if (!resolvedValue && w.falsyFallbackRefs?.length) {
85-
for (const ref of w.falsyFallbackRefs) {
86-
resolvedValue = await ctx.pullSingle(ref, pullChain);
87-
if (resolvedValue) break;
88-
}
89-
}
90+
// Layer 1: Execution
91+
let value = await evaluateWireSource(ctx, w, pullChain);
9092

91-
if (!resolvedValue) {
92-
if (w.falsyControl) {
93-
resolvedValue = applyControlFlow(w.falsyControl);
94-
} else if (w.falsyFallback != null) {
95-
resolvedValue = coerceConstant(w.falsyFallback);
96-
}
97-
}
93+
// Layer 2a: Falsy Gate (||)
94+
value = await applyFalsyGate(ctx, w, value, pullChain);
9895

99-
// --- Layer 2b: Nullish Gate (??) ---
100-
if (resolvedValue == null) {
101-
if (w.nullishControl) {
102-
resolvedValue = applyControlFlow(w.nullishControl);
103-
} else if (w.nullishFallbackRef) {
104-
resolvedValue = await ctx.pullSingle(w.nullishFallbackRef, pullChain);
105-
} else if (w.nullishFallback != null) {
106-
resolvedValue = coerceConstant(w.nullishFallback);
107-
}
108-
}
96+
// Layer 2b: Nullish Gate (??)
97+
value = await applyNullishGate(ctx, w, value, pullChain);
10998

110-
// --- Overdefinition Boundary ---
111-
if (resolvedValue != null) return resolvedValue;
112-
} catch (err: any) {
113-
// --- Layer 3: Catch ---
99+
// Overdefinition Boundary
100+
if (value != null) return value;
101+
} catch (err: unknown) {
102+
// Layer 3: Catch Gate
114103
if (isFatalError(err)) throw err;
115-
if (w.catchControl) return applyControlFlow(w.catchControl);
116-
if (w.catchFallbackRef)
117-
return ctx.pullSingle(w.catchFallbackRef, pullChain);
118-
if (w.catchFallback != null) return coerceConstant(w.catchFallback);
104+
105+
const recoveredValue = await applyCatchGate(ctx, w, pullChain);
106+
if (recoveredValue != null) return recoveredValue;
107+
119108
lastError = err;
120109
}
121110
}
@@ -124,6 +113,79 @@ async function resolveWiresAsync(
124113
return undefined;
125114
}
126115

116+
// ── Layer 2a: Falsy Gate (||) ────────────────────────────────────────────────
117+
118+
/**
119+
* Apply the Falsy Gate (Layer 2a) to a resolved value.
120+
*
121+
* If the value is already truthy the gate is a no-op. Otherwise the gate
122+
* walks `falsyFallbackRefs` (chained `||` refs) in order, returning the first
123+
* truthy result. If none yields a truthy value, `falsyControl` or
124+
* `falsyFallback` is tried as a last resort.
125+
*/
126+
export async function applyFalsyGate(
127+
ctx: TreeContext,
128+
w: WireWithGates,
129+
value: unknown,
130+
pullChain?: Set<string>,
131+
): Promise<unknown> {
132+
if (value) return value; // already truthy — gate is closed
133+
134+
if (w.falsyFallbackRefs?.length) {
135+
for (const ref of w.falsyFallbackRefs) {
136+
const fallback = await ctx.pullSingle(ref, pullChain);
137+
if (fallback) return fallback;
138+
}
139+
}
140+
141+
if (w.falsyControl) return applyControlFlow(w.falsyControl);
142+
if (w.falsyFallback != null) return coerceConstant(w.falsyFallback);
143+
return value;
144+
}
145+
146+
// ── Layer 2b: Nullish Gate (??) ──────────────────────────────────────────────
147+
148+
/**
149+
* Apply the Nullish Gate (Layer 2b) to a resolved value.
150+
*
151+
* If the value is non-nullish the gate is a no-op. Otherwise `nullishControl`,
152+
* `nullishFallbackRef`, or `nullishFallback` is applied (in priority order).
153+
*/
154+
export async function applyNullishGate(
155+
ctx: TreeContext,
156+
w: WireWithGates,
157+
value: unknown,
158+
pullChain?: Set<string>,
159+
): Promise<unknown> {
160+
if (value != null) return value; // non-nullish — gate is closed
161+
162+
if (w.nullishControl) return applyControlFlow(w.nullishControl);
163+
if (w.nullishFallbackRef) return ctx.pullSingle(w.nullishFallbackRef, pullChain);
164+
if (w.nullishFallback != null) return coerceConstant(w.nullishFallback);
165+
return value;
166+
}
167+
168+
// ── Layer 3: Catch Gate ──────────────────────────────────────────────────────
169+
170+
/**
171+
* Apply the Catch Gate (Layer 3) after an error has been thrown by the
172+
* execution layer.
173+
*
174+
* Returns the recovered value if the wire supplies a catch handler, or
175+
* `undefined` if the error should be stored as `lastError` so the loop can
176+
* continue to the next wire.
177+
*/
178+
export async function applyCatchGate(
179+
ctx: TreeContext,
180+
w: WireWithGates,
181+
pullChain?: Set<string>,
182+
): Promise<unknown> {
183+
if (w.catchControl) return applyControlFlow(w.catchControl);
184+
if (w.catchFallbackRef) return ctx.pullSingle(w.catchFallbackRef, pullChain);
185+
if (w.catchFallback != null) return coerceConstant(w.catchFallback);
186+
return undefined;
187+
}
188+
127189
// ── Layer 1: Wire source evaluation ─────────────────────────────────────────
128190

129191
/**

0 commit comments

Comments
 (0)