@stackables/bridge-core 0.0.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/build/{ExecutionTree.d.ts → bridge-core/src/ExecutionTree.d.ts} +14 -13
- package/build/bridge-core/src/ExecutionTree.d.ts.map +1 -0
- package/build/{ExecutionTree.js → bridge-core/src/ExecutionTree.js} +155 -93
- package/build/{execute-bridge.d.ts → bridge-core/src/execute-bridge.d.ts} +19 -7
- package/build/bridge-core/src/execute-bridge.d.ts.map +1 -0
- package/build/{execute-bridge.js → bridge-core/src/execute-bridge.js} +13 -6
- package/build/{index.d.ts → bridge-core/src/index.d.ts} +4 -2
- package/build/bridge-core/src/index.d.ts.map +1 -0
- package/build/{index.js → bridge-core/src/index.js} +5 -1
- package/build/bridge-core/src/merge-documents.d.ts +25 -0
- package/build/bridge-core/src/merge-documents.d.ts.map +1 -0
- package/build/bridge-core/src/merge-documents.js +91 -0
- package/build/bridge-core/src/tools/index.d.ts.map +1 -0
- package/build/bridge-core/src/tools/internal.d.ts.map +1 -0
- package/build/{types.d.ts → bridge-core/src/types.d.ts} +28 -1
- package/build/bridge-core/src/types.d.ts.map +1 -0
- package/build/{types.js → bridge-core/src/types.js} +1 -0
- package/build/bridge-core/src/utils.d.ts.map +1 -0
- package/build/bridge-core/src/version-check.d.ts +64 -0
- package/build/bridge-core/src/version-check.d.ts.map +1 -0
- package/build/bridge-core/src/version-check.js +205 -0
- package/build/bridge-stdlib/src/index.d.ts +34 -0
- package/build/bridge-stdlib/src/index.d.ts.map +1 -0
- package/build/bridge-stdlib/src/index.js +40 -0
- package/build/bridge-stdlib/src/tools/arrays.d.ts +28 -0
- package/build/bridge-stdlib/src/tools/arrays.d.ts.map +1 -0
- package/build/bridge-stdlib/src/tools/arrays.js +50 -0
- package/build/bridge-stdlib/src/tools/audit.d.ts +36 -0
- package/build/bridge-stdlib/src/tools/audit.d.ts.map +1 -0
- package/build/bridge-stdlib/src/tools/audit.js +39 -0
- package/build/bridge-stdlib/src/tools/http-call.d.ts +35 -0
- package/build/bridge-stdlib/src/tools/http-call.d.ts.map +1 -0
- package/build/bridge-stdlib/src/tools/http-call.js +118 -0
- package/build/bridge-stdlib/src/tools/strings.d.ts +13 -0
- package/build/bridge-stdlib/src/tools/strings.d.ts.map +1 -0
- package/build/bridge-stdlib/src/tools/strings.js +12 -0
- package/build/bridge-types/src/index.d.ts +63 -0
- package/build/bridge-types/src/index.d.ts.map +1 -0
- package/build/bridge-types/src/index.js +8 -0
- package/package.json +6 -4
- package/build/ExecutionTree.d.ts.map +0 -1
- package/build/execute-bridge.d.ts.map +0 -1
- package/build/index.d.ts.map +0 -1
- package/build/tools/index.d.ts.map +0 -1
- package/build/tools/internal.d.ts.map +0 -1
- package/build/types.d.ts.map +0 -1
- package/build/utils.d.ts.map +0 -1
- /package/build/{tools → bridge-core/src/tools}/index.d.ts +0 -0
- /package/build/{tools → bridge-core/src/tools}/index.js +0 -0
- /package/build/{tools → bridge-core/src/tools}/internal.d.ts +0 -0
- /package/build/{tools → bridge-core/src/tools}/internal.js +0 -0
- /package/build/{utils.d.ts → bridge-core/src/utils.d.ts} +0 -0
- /package/build/{utils.js → bridge-core/src/utils.js} +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[](https://github.com/stackables/bridge)
|
|
2
|
+
|
|
3
|
+
# The Bridge Runtime
|
|
4
|
+
|
|
5
|
+
The lightweight runtime engine for [The Bridge](https://github.com/stackables/bridge).
|
|
6
|
+
|
|
7
|
+
This is **The Engine** — it takes pre-compiled bridge instructions (a JSON AST) and executes them. No parser, no GraphQL, no heavy dependencies. If you're deploying to a Cloudflare Worker, a Vercel Edge function, or any environment where bundle size matters, this is the package you want in production.
|
|
8
|
+
|
|
9
|
+
## Installing
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @stackables/bridge-core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## When to Use This
|
|
16
|
+
|
|
17
|
+
The most common pattern is the **Ahead-of-Time (AOT) workflow**: compile your `.bridge` files to JSON during CI/CD, then ship only the instructions + this engine to production. The parser and its dependencies never touch your production bundle.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { executeBridge } from "@stackables/bridge-core";
|
|
21
|
+
import instructions from "./compiled-bridge.json" assert { type: "json" };
|
|
22
|
+
|
|
23
|
+
const { data } = await executeBridge({
|
|
24
|
+
instructions,
|
|
25
|
+
operation: "Query.searchTrains",
|
|
26
|
+
input: { from: "Bern", to: "Zürich" },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
console.log(data);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Options
|
|
33
|
+
|
|
34
|
+
| Option | What it does |
|
|
35
|
+
| -------------- | -------------------------------------------------------- |
|
|
36
|
+
| `instructions` | Pre-compiled bridge instructions (from the compiler) |
|
|
37
|
+
| `operation` | Which bridge to run, e.g. `"Query.myField"` |
|
|
38
|
+
| `input` | Input arguments — like GraphQL field args |
|
|
39
|
+
| `tools` | Your custom tool functions (merged with built-in `std`) |
|
|
40
|
+
| `context` | Shared data available via `with context` in bridge files |
|
|
41
|
+
| `trace` | Tool-call tracing: `"off"`, `"basic"`, or `"full"` |
|
|
42
|
+
| `logger` | Plug in pino, winston, console — whatever you use |
|
|
43
|
+
| `signal` | Pass an `AbortSignal` to cancel execution mid-flight |
|
|
44
|
+
|
|
45
|
+
Returns `{ data, traces }`.
|
|
46
|
+
|
|
47
|
+
## Part of the Bridge Ecosystem
|
|
48
|
+
|
|
49
|
+
| Package | What it does |
|
|
50
|
+
| ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
|
|
51
|
+
| [`@stackables/bridge`](https://www.npmjs.com/package/@stackables/bridge) | **The All-in-One** — everything in a single install |
|
|
52
|
+
| [`@stackables/bridge-compiler`](https://www.npmjs.com/package/@stackables/bridge-compiler) | **The Parser** — turns `.bridge` text into the instructions this engine runs |
|
|
53
|
+
| [`@stackables/bridge-graphql`](https://www.npmjs.com/package/@stackables/bridge-graphql) | **The Adapter** — wires bridges into a GraphQL schema |
|
|
54
|
+
| [`@stackables/bridge-stdlib`](https://www.npmjs.com/package/@stackables/bridge-stdlib) | **The Standard Library** — httpCall, strings, arrays, and more |
|
|
55
|
+
| [`@stackables/bridge-types`](https://www.npmjs.com/package/@stackables/bridge-types) | **Shared Types** — `ToolCallFn`, `ToolMap`, `CacheStore` |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Bridge,
|
|
1
|
+
import type { Bridge, BridgeDocument, ToolMap } from "./types.ts";
|
|
2
2
|
/** Fatal panic error — bypasses all error boundaries (`?.` and `catch`). */
|
|
3
3
|
export declare class BridgePanicError extends Error {
|
|
4
4
|
constructor(message: string);
|
|
@@ -7,6 +7,8 @@ export declare class BridgePanicError extends Error {
|
|
|
7
7
|
export declare class BridgeAbortError extends Error {
|
|
8
8
|
constructor(message?: string);
|
|
9
9
|
}
|
|
10
|
+
/** Maximum shadow-tree nesting depth before a BridgePanicError is thrown. */
|
|
11
|
+
export declare const MAX_EXECUTION_DEPTH = 30;
|
|
10
12
|
/**
|
|
11
13
|
* Structured logger interface for Bridge engine events.
|
|
12
14
|
* Accepts any compatible logger: pino, winston, bunyan, `console`, etc.
|
|
@@ -74,7 +76,7 @@ export declare class TraceCollector {
|
|
|
74
76
|
}
|
|
75
77
|
export declare class ExecutionTree {
|
|
76
78
|
trunk: Trunk;
|
|
77
|
-
private
|
|
79
|
+
private document;
|
|
78
80
|
private context?;
|
|
79
81
|
private parent?;
|
|
80
82
|
state: Record<string, any>;
|
|
@@ -82,6 +84,12 @@ export declare class ExecutionTree {
|
|
|
82
84
|
private toolDepCache;
|
|
83
85
|
private toolDefCache;
|
|
84
86
|
private pipeHandleMap;
|
|
87
|
+
/**
|
|
88
|
+
* Maps trunk keys to `@version` strings from handle bindings.
|
|
89
|
+
* Populated in the constructor so `schedule()` can prefer versioned
|
|
90
|
+
* tool lookups (e.g. `std.str.toLowerCase@999.1`) over the default.
|
|
91
|
+
*/
|
|
92
|
+
private handleVersionMap;
|
|
85
93
|
/** Promise that resolves when all critical `force` handles have settled. */
|
|
86
94
|
private forcedExecution?;
|
|
87
95
|
/** Shared trace collector — present only when tracing is enabled. */
|
|
@@ -91,7 +99,9 @@ export declare class ExecutionTree {
|
|
|
91
99
|
/** External abort signal — cancels execution when triggered. */
|
|
92
100
|
signal?: AbortSignal;
|
|
93
101
|
private toolFns?;
|
|
94
|
-
|
|
102
|
+
/** Shadow-tree nesting depth (0 for root). */
|
|
103
|
+
private depth;
|
|
104
|
+
constructor(trunk: Trunk, document: BridgeDocument, toolFns?: ToolMap, context?: Record<string, any> | undefined, parent?: ExecutionTree | undefined);
|
|
95
105
|
/** Derive tool name from a trunk */
|
|
96
106
|
private getToolName;
|
|
97
107
|
/** Deep-lookup a tool function by dotted name (e.g. "std.str.toUpperCase").
|
|
@@ -105,7 +115,7 @@ export declare class ExecutionTree {
|
|
|
105
115
|
private resolveToolSource;
|
|
106
116
|
/** Call a tool dependency (cached per request) */
|
|
107
117
|
private resolveToolDep;
|
|
108
|
-
schedule(target: Trunk): any;
|
|
118
|
+
schedule(target: Trunk, pullChain?: Set<string>): any;
|
|
109
119
|
/**
|
|
110
120
|
* Invoke a tool function, recording both an OpenTelemetry span and (when
|
|
111
121
|
* tracing is enabled) a ToolTrace entry. All three tool-call sites in the
|
|
@@ -116,15 +126,6 @@ export declare class ExecutionTree {
|
|
|
116
126
|
/** Returns collected traces (empty array when tracing is disabled). */
|
|
117
127
|
getTraces(): ToolTrace[];
|
|
118
128
|
private pullSingle;
|
|
119
|
-
pull(refs: NodeRef[]): Promise<any>;
|
|
120
|
-
/**
|
|
121
|
-
* Safe execution pull: wraps individual safe-flagged pulls in try/catch.
|
|
122
|
-
* Wires with `safe: true` swallow errors and return undefined.
|
|
123
|
-
* Non-safe wires propagate errors normally.
|
|
124
|
-
*/
|
|
125
|
-
pullSafe(pulls: Extract<Wire, {
|
|
126
|
-
from: NodeRef;
|
|
127
|
-
}>[]): Promise<any>;
|
|
128
129
|
push(args: Record<string, any>): void;
|
|
129
130
|
/** Store the aggregated promise for critical forced handles so
|
|
130
131
|
* `response()` can await it exactly once per bridge execution. */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExecutionTree.d.ts","sourceRoot":"","sources":["../../../src/ExecutionTree.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,8 @@ export class BridgeAbortError extends Error {
|
|
|
20
20
|
const CONTINUE_SYM = Symbol.for("BRIDGE_CONTINUE");
|
|
21
21
|
/** Sentinel for `break` — halt array iteration */
|
|
22
22
|
const BREAK_SYM = Symbol.for("BRIDGE_BREAK");
|
|
23
|
+
/** Maximum shadow-tree nesting depth before a BridgePanicError is thrown. */
|
|
24
|
+
export const MAX_EXECUTION_DEPTH = 30;
|
|
23
25
|
const otelTracer = trace.getTracer("@stackables/bridge");
|
|
24
26
|
const otelMeter = metrics.getMeter("@stackables/bridge");
|
|
25
27
|
const toolCallCounter = otelMeter.createCounter("bridge.tool.calls", {
|
|
@@ -154,7 +156,7 @@ function setNested(obj, path, value) {
|
|
|
154
156
|
}
|
|
155
157
|
export class ExecutionTree {
|
|
156
158
|
trunk;
|
|
157
|
-
|
|
159
|
+
document;
|
|
158
160
|
context;
|
|
159
161
|
parent;
|
|
160
162
|
state = {};
|
|
@@ -162,6 +164,12 @@ export class ExecutionTree {
|
|
|
162
164
|
toolDepCache = new Map();
|
|
163
165
|
toolDefCache = new Map();
|
|
164
166
|
pipeHandleMap;
|
|
167
|
+
/**
|
|
168
|
+
* Maps trunk keys to `@version` strings from handle bindings.
|
|
169
|
+
* Populated in the constructor so `schedule()` can prefer versioned
|
|
170
|
+
* tool lookups (e.g. `std.str.toLowerCase@999.1`) over the default.
|
|
171
|
+
*/
|
|
172
|
+
handleVersionMap = new Map();
|
|
165
173
|
/** Promise that resolves when all critical `force` handles have settled. */
|
|
166
174
|
forcedExecution;
|
|
167
175
|
/** Shared trace collector — present only when tracing is enabled. */
|
|
@@ -171,16 +179,52 @@ export class ExecutionTree {
|
|
|
171
179
|
/** External abort signal — cancels execution when triggered. */
|
|
172
180
|
signal;
|
|
173
181
|
toolFns;
|
|
174
|
-
|
|
182
|
+
/** Shadow-tree nesting depth (0 for root). */
|
|
183
|
+
depth;
|
|
184
|
+
constructor(trunk, document, toolFns, context, parent) {
|
|
175
185
|
this.trunk = trunk;
|
|
176
|
-
this.
|
|
186
|
+
this.document = document;
|
|
177
187
|
this.context = context;
|
|
178
188
|
this.parent = parent;
|
|
189
|
+
this.depth = parent ? parent.depth + 1 : 0;
|
|
190
|
+
if (this.depth > MAX_EXECUTION_DEPTH) {
|
|
191
|
+
throw new BridgePanicError(`Maximum execution depth exceeded (${this.depth}) at ${trunkKey(trunk)}. Check for infinite recursion or circular array mappings.`);
|
|
192
|
+
}
|
|
179
193
|
this.toolFns = { internal, ...(toolFns ?? {}) };
|
|
194
|
+
const instructions = document.instructions;
|
|
180
195
|
this.bridge = instructions.find((i) => i.kind === "bridge" && i.type === trunk.type && i.field === trunk.field);
|
|
181
196
|
if (this.bridge?.pipeHandles) {
|
|
182
197
|
this.pipeHandleMap = new Map(this.bridge.pipeHandles.map((ph) => [ph.key, ph]));
|
|
183
198
|
}
|
|
199
|
+
// Build handle→version map from bridge handle bindings
|
|
200
|
+
if (this.bridge) {
|
|
201
|
+
const instanceCounters = new Map();
|
|
202
|
+
for (const h of this.bridge.handles) {
|
|
203
|
+
if (h.kind !== "tool")
|
|
204
|
+
continue;
|
|
205
|
+
const name = h.name;
|
|
206
|
+
const lastDot = name.lastIndexOf(".");
|
|
207
|
+
let module, field, counterKey, type;
|
|
208
|
+
if (lastDot !== -1) {
|
|
209
|
+
module = name.substring(0, lastDot);
|
|
210
|
+
field = name.substring(lastDot + 1);
|
|
211
|
+
counterKey = `${module}:${field}`;
|
|
212
|
+
type = this.trunk.type;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
module = SELF_MODULE;
|
|
216
|
+
field = name;
|
|
217
|
+
counterKey = `Tools:${name}`;
|
|
218
|
+
type = "Tools";
|
|
219
|
+
}
|
|
220
|
+
const instance = (instanceCounters.get(counterKey) ?? 0) + 1;
|
|
221
|
+
instanceCounters.set(counterKey, instance);
|
|
222
|
+
if (h.version) {
|
|
223
|
+
const key = trunkKey({ module, type, field, instance });
|
|
224
|
+
this.handleVersionMap.set(key, h.version);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
184
228
|
if (context) {
|
|
185
229
|
this.state[trunkKey({ module: SELF_MODULE, type: "Context", field: "context" })] = context;
|
|
186
230
|
}
|
|
@@ -221,7 +265,35 @@ export class ExecutionTree {
|
|
|
221
265
|
return current;
|
|
222
266
|
// Fall back to flat key (e.g. "hereapi.geocode" as a literal property name)
|
|
223
267
|
const flat = this.toolFns?.[name];
|
|
224
|
-
|
|
268
|
+
if (typeof flat === "function")
|
|
269
|
+
return flat;
|
|
270
|
+
// Try versioned namespace keys (e.g. "std.str@999.1" → { toLowerCase })
|
|
271
|
+
// For "std.str.toLowerCase@999.1", check:
|
|
272
|
+
// toolFns["std.str@999.1"]?.toLowerCase
|
|
273
|
+
// toolFns["std@999.1"]?.str?.toLowerCase
|
|
274
|
+
const atIdx = name.lastIndexOf("@");
|
|
275
|
+
if (atIdx > 0) {
|
|
276
|
+
const baseName = name.substring(0, atIdx);
|
|
277
|
+
const version = name.substring(atIdx + 1);
|
|
278
|
+
const nameParts = baseName.split(".");
|
|
279
|
+
for (let i = nameParts.length - 1; i >= 1; i--) {
|
|
280
|
+
const nsKey = nameParts.slice(0, i).join(".") + "@" + version;
|
|
281
|
+
const remainder = nameParts.slice(i);
|
|
282
|
+
let ns = this.toolFns?.[nsKey];
|
|
283
|
+
if (ns != null && typeof ns === "object") {
|
|
284
|
+
for (const part of remainder) {
|
|
285
|
+
if (ns == null || typeof ns !== "object") {
|
|
286
|
+
ns = undefined;
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
ns = ns[part];
|
|
290
|
+
}
|
|
291
|
+
if (typeof ns === "function")
|
|
292
|
+
return ns;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return undefined;
|
|
225
297
|
}
|
|
226
298
|
// Try root level first
|
|
227
299
|
const fn = this.toolFns?.[name];
|
|
@@ -239,7 +311,7 @@ export class ExecutionTree {
|
|
|
239
311
|
resolveToolDefByName(name) {
|
|
240
312
|
if (this.toolDefCache.has(name))
|
|
241
313
|
return this.toolDefCache.get(name) ?? undefined;
|
|
242
|
-
const toolDefs = this.instructions.filter((i) => i.kind === "tool");
|
|
314
|
+
const toolDefs = this.document.instructions.filter((i) => i.kind === "tool");
|
|
243
315
|
const base = toolDefs.find((t) => t.name === name);
|
|
244
316
|
if (!base) {
|
|
245
317
|
this.toolDefCache.set(name, null);
|
|
@@ -322,7 +394,6 @@ export class ExecutionTree {
|
|
|
322
394
|
let value;
|
|
323
395
|
if (dep.kind === "context") {
|
|
324
396
|
// Walk the full parent chain for context
|
|
325
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
326
397
|
let cursor = this;
|
|
327
398
|
while (cursor && value === undefined) {
|
|
328
399
|
value = cursor.context;
|
|
@@ -336,7 +407,6 @@ export class ExecutionTree {
|
|
|
336
407
|
type: "Const",
|
|
337
408
|
field: "const",
|
|
338
409
|
});
|
|
339
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
340
410
|
let cursor = this;
|
|
341
411
|
while (cursor && value === undefined) {
|
|
342
412
|
value = cursor.state[constKey];
|
|
@@ -383,7 +453,7 @@ export class ExecutionTree {
|
|
|
383
453
|
this.toolDepCache.set(toolName, promise);
|
|
384
454
|
return promise;
|
|
385
455
|
}
|
|
386
|
-
schedule(target) {
|
|
456
|
+
schedule(target, pullChain) {
|
|
387
457
|
// Delegate to parent (shadow trees don't schedule directly) unless
|
|
388
458
|
// the target fork has bridge wires sourced from element data,
|
|
389
459
|
// or a __local binding whose source chain touches element data.
|
|
@@ -409,7 +479,7 @@ export class ExecutionTree {
|
|
|
409
479
|
return (this.bridge?.wires.some((iw) => sameTrunk(iw.to, srcTrunk) && "from" in iw && !!iw.from.element) ?? false);
|
|
410
480
|
});
|
|
411
481
|
if (!hasElementSource && !hasTransitiveElementSource) {
|
|
412
|
-
return this.parent.schedule(target);
|
|
482
|
+
return this.parent.schedule(target, pullChain);
|
|
413
483
|
}
|
|
414
484
|
}
|
|
415
485
|
return (async () => {
|
|
@@ -449,7 +519,7 @@ export class ExecutionTree {
|
|
|
449
519
|
}
|
|
450
520
|
const groupEntries = Array.from(wireGroups.entries());
|
|
451
521
|
const resolved = await Promise.all(groupEntries.map(async ([, group]) => {
|
|
452
|
-
const value = await this.resolveWires(group);
|
|
522
|
+
const value = await this.resolveWires(group, pullChain);
|
|
453
523
|
return [group[0].to.path, value];
|
|
454
524
|
}));
|
|
455
525
|
for (const [path, value] of resolved) {
|
|
@@ -478,8 +548,21 @@ export class ExecutionTree {
|
|
|
478
548
|
return this.resolveToolSource(onErrorWire.source, toolDef);
|
|
479
549
|
}
|
|
480
550
|
}
|
|
481
|
-
// Direct tool function lookup by name (simple or dotted)
|
|
482
|
-
|
|
551
|
+
// Direct tool function lookup by name (simple or dotted).
|
|
552
|
+
// When the handle carries a @version tag, try the versioned key first
|
|
553
|
+
// (e.g. "std.str.toLowerCase@999.1") so user-injected overrides win.
|
|
554
|
+
// For pipe forks, fall back to the baseTrunk's version since forks
|
|
555
|
+
// use synthetic instance numbers (100000+).
|
|
556
|
+
const handleVersion = this.handleVersionMap.get(trunkKey(target)) ??
|
|
557
|
+
(baseTrunk
|
|
558
|
+
? this.handleVersionMap.get(trunkKey(baseTrunk))
|
|
559
|
+
: undefined);
|
|
560
|
+
let directFn = handleVersion
|
|
561
|
+
? this.lookupToolFn(`${toolName}@${handleVersion}`)
|
|
562
|
+
: undefined;
|
|
563
|
+
if (!directFn) {
|
|
564
|
+
directFn = this.lookupToolFn(toolName);
|
|
565
|
+
}
|
|
483
566
|
if (directFn) {
|
|
484
567
|
return this.callTool(toolName, toolName, directFn, input);
|
|
485
568
|
}
|
|
@@ -574,7 +657,7 @@ export class ExecutionTree {
|
|
|
574
657
|
});
|
|
575
658
|
}
|
|
576
659
|
shadow() {
|
|
577
|
-
const child = new ExecutionTree(this.trunk, this.
|
|
660
|
+
const child = new ExecutionTree(this.trunk, this.document, this.toolFns, undefined, this);
|
|
578
661
|
child.tracer = this.tracer;
|
|
579
662
|
child.logger = this.logger;
|
|
580
663
|
child.signal = this.signal;
|
|
@@ -584,17 +667,23 @@ export class ExecutionTree {
|
|
|
584
667
|
getTraces() {
|
|
585
668
|
return this.tracer?.traces ?? [];
|
|
586
669
|
}
|
|
587
|
-
async pullSingle(ref) {
|
|
670
|
+
async pullSingle(ref, pullChain = new Set()) {
|
|
588
671
|
const key = trunkKey(ref);
|
|
672
|
+
// ── Cycle detection ─────────────────────────────────────────────
|
|
673
|
+
// If this exact key is already in our active pull chain, it is a
|
|
674
|
+
// circular dependency that would deadlock (await-on-self).
|
|
675
|
+
if (pullChain.has(key)) {
|
|
676
|
+
throw new BridgePanicError(`Circular dependency detected: "${key}" depends on itself`);
|
|
677
|
+
}
|
|
589
678
|
// Walk the full parent chain — shadow trees may be nested multiple levels
|
|
590
679
|
let value = undefined;
|
|
591
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
592
680
|
let cursor = this;
|
|
593
681
|
while (cursor && value === undefined) {
|
|
594
682
|
value = cursor.state[key];
|
|
595
683
|
cursor = cursor.parent;
|
|
596
684
|
}
|
|
597
685
|
if (value === undefined) {
|
|
686
|
+
const nextChain = new Set(pullChain).add(key);
|
|
598
687
|
// ── Lazy define field resolution ────────────────────────────────
|
|
599
688
|
// For define trunks (__define_in_* / __define_out_*) with a specific
|
|
600
689
|
// field path, resolve ONLY the wire(s) targeting that field instead
|
|
@@ -604,10 +693,10 @@ export class ExecutionTree {
|
|
|
604
693
|
if (ref.path.length > 0 && ref.module.startsWith("__define_")) {
|
|
605
694
|
const fieldWires = this.bridge?.wires.filter((w) => sameTrunk(w.to, ref) && pathEquals(w.to.path, ref.path)) ?? [];
|
|
606
695
|
if (fieldWires.length > 0) {
|
|
607
|
-
return this.resolveWires(fieldWires);
|
|
696
|
+
return this.resolveWires(fieldWires, nextChain);
|
|
608
697
|
}
|
|
609
698
|
}
|
|
610
|
-
this.state[key] = this.schedule(ref);
|
|
699
|
+
this.state[key] = this.schedule(ref, nextChain);
|
|
611
700
|
value = this.state[key];
|
|
612
701
|
}
|
|
613
702
|
// Always await in case the stored value is a Promise (e.g. from schedule()).
|
|
@@ -640,64 +729,6 @@ export class ExecutionTree {
|
|
|
640
729
|
}
|
|
641
730
|
return result;
|
|
642
731
|
}
|
|
643
|
-
async pull(refs) {
|
|
644
|
-
if (refs.length === 1)
|
|
645
|
-
return this.pullSingle(refs[0]);
|
|
646
|
-
// Strict left-to-right sequential evaluation with short-circuit.
|
|
647
|
-
// We respect the exact fallback priority authored by the developer.
|
|
648
|
-
const errors = [];
|
|
649
|
-
for (const ref of refs) {
|
|
650
|
-
try {
|
|
651
|
-
const value = await this.pullSingle(ref);
|
|
652
|
-
if (value != null)
|
|
653
|
-
return value; // Short-circuit: found data
|
|
654
|
-
}
|
|
655
|
-
catch (err) {
|
|
656
|
-
errors.push(err);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
// All resolved to null/undefined, or all threw
|
|
660
|
-
if (errors.length === refs.length) {
|
|
661
|
-
throw new AggregateError(errors, "All sources failed");
|
|
662
|
-
}
|
|
663
|
-
return undefined;
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* Safe execution pull: wraps individual safe-flagged pulls in try/catch.
|
|
667
|
-
* Wires with `safe: true` swallow errors and return undefined.
|
|
668
|
-
* Non-safe wires propagate errors normally.
|
|
669
|
-
*/
|
|
670
|
-
async pullSafe(pulls) {
|
|
671
|
-
if (pulls.length === 1) {
|
|
672
|
-
const w = pulls[0];
|
|
673
|
-
if (w.safe) {
|
|
674
|
-
try {
|
|
675
|
-
return await this.pullSingle(w.from);
|
|
676
|
-
}
|
|
677
|
-
catch {
|
|
678
|
-
return undefined;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
return this.pullSingle(w.from);
|
|
682
|
-
}
|
|
683
|
-
const errors = [];
|
|
684
|
-
for (const w of pulls) {
|
|
685
|
-
try {
|
|
686
|
-
const value = w.safe
|
|
687
|
-
? await this.pullSingle(w.from).catch(() => undefined)
|
|
688
|
-
: await this.pullSingle(w.from);
|
|
689
|
-
if (value != null)
|
|
690
|
-
return value;
|
|
691
|
-
}
|
|
692
|
-
catch (err) {
|
|
693
|
-
errors.push(err);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
if (errors.length === pulls.length) {
|
|
697
|
-
throw new AggregateError(errors, "All sources failed");
|
|
698
|
-
}
|
|
699
|
-
return undefined;
|
|
700
|
-
}
|
|
701
732
|
push(args) {
|
|
702
733
|
this.state[trunkKey(this.trunk)] = args;
|
|
703
734
|
}
|
|
@@ -769,7 +800,7 @@ export class ExecutionTree {
|
|
|
769
800
|
* After layers 1–2b, the overdefinition boundary (`!= null`) decides whether
|
|
770
801
|
* to return or continue to the next wire.
|
|
771
802
|
*/
|
|
772
|
-
async resolveWires(wires) {
|
|
803
|
+
async resolveWires(wires, pullChain) {
|
|
773
804
|
let lastError;
|
|
774
805
|
for (const w of wires) {
|
|
775
806
|
// Constant wire — always wins, no modifiers
|
|
@@ -779,16 +810,16 @@ export class ExecutionTree {
|
|
|
779
810
|
// --- Layer 1: Execution ---
|
|
780
811
|
let resolvedValue;
|
|
781
812
|
if ("cond" in w) {
|
|
782
|
-
const condValue = await this.pullSingle(w.cond);
|
|
813
|
+
const condValue = await this.pullSingle(w.cond, pullChain);
|
|
783
814
|
if (condValue) {
|
|
784
815
|
if (w.thenRef !== undefined)
|
|
785
|
-
resolvedValue = await this.pullSingle(w.thenRef);
|
|
816
|
+
resolvedValue = await this.pullSingle(w.thenRef, pullChain);
|
|
786
817
|
else if (w.thenValue !== undefined)
|
|
787
818
|
resolvedValue = coerceConstant(w.thenValue);
|
|
788
819
|
}
|
|
789
820
|
else {
|
|
790
821
|
if (w.elseRef !== undefined)
|
|
791
|
-
resolvedValue = await this.pullSingle(w.elseRef);
|
|
822
|
+
resolvedValue = await this.pullSingle(w.elseRef, pullChain);
|
|
792
823
|
else if (w.elseValue !== undefined)
|
|
793
824
|
resolvedValue = coerceConstant(w.elseValue);
|
|
794
825
|
}
|
|
@@ -796,23 +827,23 @@ export class ExecutionTree {
|
|
|
796
827
|
else if ("condAnd" in w) {
|
|
797
828
|
const { leftRef, rightRef, rightValue, safe: isSafe, rightSafe, } = w.condAnd;
|
|
798
829
|
const leftVal = isSafe
|
|
799
|
-
? await this.pullSingle(leftRef).catch((e) => {
|
|
830
|
+
? await this.pullSingle(leftRef, pullChain).catch((e) => {
|
|
800
831
|
if (isFatalError(e))
|
|
801
832
|
throw e;
|
|
802
833
|
return undefined;
|
|
803
834
|
})
|
|
804
|
-
: await this.pullSingle(leftRef);
|
|
835
|
+
: await this.pullSingle(leftRef, pullChain);
|
|
805
836
|
if (!leftVal) {
|
|
806
837
|
resolvedValue = false;
|
|
807
838
|
}
|
|
808
839
|
else if (rightRef !== undefined) {
|
|
809
840
|
const rightVal = rightSafe
|
|
810
|
-
? await this.pullSingle(rightRef).catch((e) => {
|
|
841
|
+
? await this.pullSingle(rightRef, pullChain).catch((e) => {
|
|
811
842
|
if (isFatalError(e))
|
|
812
843
|
throw e;
|
|
813
844
|
return undefined;
|
|
814
845
|
})
|
|
815
|
-
: await this.pullSingle(rightRef);
|
|
846
|
+
: await this.pullSingle(rightRef, pullChain);
|
|
816
847
|
resolvedValue = Boolean(rightVal);
|
|
817
848
|
}
|
|
818
849
|
else if (rightValue !== undefined) {
|
|
@@ -825,23 +856,23 @@ export class ExecutionTree {
|
|
|
825
856
|
else if ("condOr" in w) {
|
|
826
857
|
const { leftRef, rightRef, rightValue, safe: isSafe, rightSafe, } = w.condOr;
|
|
827
858
|
const leftVal = isSafe
|
|
828
|
-
? await this.pullSingle(leftRef).catch((e) => {
|
|
859
|
+
? await this.pullSingle(leftRef, pullChain).catch((e) => {
|
|
829
860
|
if (isFatalError(e))
|
|
830
861
|
throw e;
|
|
831
862
|
return undefined;
|
|
832
863
|
})
|
|
833
|
-
: await this.pullSingle(leftRef);
|
|
864
|
+
: await this.pullSingle(leftRef, pullChain);
|
|
834
865
|
if (leftVal) {
|
|
835
866
|
resolvedValue = true;
|
|
836
867
|
}
|
|
837
868
|
else if (rightRef !== undefined) {
|
|
838
869
|
const rightVal = rightSafe
|
|
839
|
-
? await this.pullSingle(rightRef).catch((e) => {
|
|
870
|
+
? await this.pullSingle(rightRef, pullChain).catch((e) => {
|
|
840
871
|
if (isFatalError(e))
|
|
841
872
|
throw e;
|
|
842
873
|
return undefined;
|
|
843
874
|
})
|
|
844
|
-
: await this.pullSingle(rightRef);
|
|
875
|
+
: await this.pullSingle(rightRef, pullChain);
|
|
845
876
|
resolvedValue = Boolean(rightVal);
|
|
846
877
|
}
|
|
847
878
|
else if (rightValue !== undefined) {
|
|
@@ -854,7 +885,7 @@ export class ExecutionTree {
|
|
|
854
885
|
else if ("from" in w) {
|
|
855
886
|
if (w.safe) {
|
|
856
887
|
try {
|
|
857
|
-
resolvedValue = await this.pullSingle(w.from);
|
|
888
|
+
resolvedValue = await this.pullSingle(w.from, pullChain);
|
|
858
889
|
}
|
|
859
890
|
catch (err) {
|
|
860
891
|
if (isFatalError(err))
|
|
@@ -863,7 +894,7 @@ export class ExecutionTree {
|
|
|
863
894
|
}
|
|
864
895
|
}
|
|
865
896
|
else {
|
|
866
|
-
resolvedValue = await this.pullSingle(w.from);
|
|
897
|
+
resolvedValue = await this.pullSingle(w.from, pullChain);
|
|
867
898
|
}
|
|
868
899
|
}
|
|
869
900
|
else {
|
|
@@ -874,7 +905,7 @@ export class ExecutionTree {
|
|
|
874
905
|
for (const ref of w.falsyFallbackRefs) {
|
|
875
906
|
// Assign the fallback value regardless of whether it is truthy or falsy.
|
|
876
907
|
// e.g. `false || 0` will correctly update resolvedValue to `0`.
|
|
877
|
-
resolvedValue = await this.pullSingle(ref);
|
|
908
|
+
resolvedValue = await this.pullSingle(ref, pullChain);
|
|
878
909
|
// If it is truthy, we are done! Short-circuit the || chain.
|
|
879
910
|
if (resolvedValue)
|
|
880
911
|
break;
|
|
@@ -894,7 +925,7 @@ export class ExecutionTree {
|
|
|
894
925
|
resolvedValue = applyControlFlow(w.nullishControl);
|
|
895
926
|
}
|
|
896
927
|
else if (w.nullishFallbackRef) {
|
|
897
|
-
resolvedValue = await this.pullSingle(w.nullishFallbackRef);
|
|
928
|
+
resolvedValue = await this.pullSingle(w.nullishFallbackRef, pullChain);
|
|
898
929
|
}
|
|
899
930
|
else if (w.nullishFallback != null) {
|
|
900
931
|
resolvedValue = coerceConstant(w.nullishFallback);
|
|
@@ -911,7 +942,7 @@ export class ExecutionTree {
|
|
|
911
942
|
if (w.catchControl)
|
|
912
943
|
return applyControlFlow(w.catchControl);
|
|
913
944
|
if (w.catchFallbackRef)
|
|
914
|
-
return this.pullSingle(w.catchFallbackRef);
|
|
945
|
+
return this.pullSingle(w.catchFallbackRef, pullChain);
|
|
915
946
|
if (w.catchFallback != null)
|
|
916
947
|
return coerceConstant(w.catchFallback);
|
|
917
948
|
lastError = err;
|
|
@@ -1024,9 +1055,40 @@ export class ExecutionTree {
|
|
|
1024
1055
|
`Ensure at least one wire targets the output (e.g. \`o.field <- ...\`).`);
|
|
1025
1056
|
}
|
|
1026
1057
|
const result = {};
|
|
1058
|
+
// Resolves a single output field at `prefix` — either via an exact-match
|
|
1059
|
+
// wire (leaf), or by collecting sub-fields from deeper wires (nested object).
|
|
1060
|
+
const resolveField = async (prefix) => {
|
|
1061
|
+
const exactWires = bridge.wires.filter((w) => w.to.module === SELF_MODULE &&
|
|
1062
|
+
w.to.type === type &&
|
|
1063
|
+
w.to.field === field &&
|
|
1064
|
+
pathEquals(w.to.path, prefix));
|
|
1065
|
+
if (exactWires.length > 0) {
|
|
1066
|
+
return this.resolveWires(exactWires);
|
|
1067
|
+
}
|
|
1068
|
+
// No exact wire — gather sub-field names from deeper-path wires
|
|
1069
|
+
// (e.g. `o.why { .temperature <- ... }` produces path ["why","temperature"])
|
|
1070
|
+
const subFields = new Set();
|
|
1071
|
+
for (const wire of bridge.wires) {
|
|
1072
|
+
const p = wire.to.path;
|
|
1073
|
+
if (wire.to.module === SELF_MODULE &&
|
|
1074
|
+
wire.to.type === type &&
|
|
1075
|
+
wire.to.field === field &&
|
|
1076
|
+
p.length > prefix.length &&
|
|
1077
|
+
prefix.every((seg, i) => p[i] === seg)) {
|
|
1078
|
+
subFields.add(p[prefix.length]);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (subFields.size === 0)
|
|
1082
|
+
return undefined;
|
|
1083
|
+
const obj = {};
|
|
1084
|
+
await Promise.all([...subFields].map(async (sub) => {
|
|
1085
|
+
obj[sub] = await resolveField([...prefix, sub]);
|
|
1086
|
+
}));
|
|
1087
|
+
return obj;
|
|
1088
|
+
};
|
|
1027
1089
|
await Promise.all([
|
|
1028
1090
|
...[...outputFields].map(async (name) => {
|
|
1029
|
-
result[name] = await
|
|
1091
|
+
result[name] = await resolveField([name]);
|
|
1030
1092
|
}),
|
|
1031
1093
|
...forcePromises,
|
|
1032
1094
|
]);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Logger, ToolTrace, TraceLevel } from "./ExecutionTree.ts";
|
|
2
|
-
import type {
|
|
2
|
+
import type { BridgeDocument, ToolMap } from "./types.ts";
|
|
3
3
|
export type ExecuteBridgeOptions = {
|
|
4
|
-
/** Parsed bridge
|
|
5
|
-
|
|
4
|
+
/** Parsed bridge document (from `parseBridge` or `parseBridgeDiagnostics`). */
|
|
5
|
+
document: BridgeDocument;
|
|
6
6
|
/**
|
|
7
7
|
* Which bridge to execute, as `"Type.field"`.
|
|
8
8
|
* Mirrors the `bridge Type.field { ... }` declaration.
|
|
@@ -11,7 +11,19 @@ export type ExecuteBridgeOptions = {
|
|
|
11
11
|
operation: string;
|
|
12
12
|
/** Input arguments — equivalent to GraphQL field arguments. */
|
|
13
13
|
input?: Record<string, unknown>;
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Tool functions available to the engine.
|
|
16
|
+
*
|
|
17
|
+
* Supports namespaced nesting: `{ myNamespace: { myTool } }`.
|
|
18
|
+
* The built-in `std` namespace is always included; user tools are
|
|
19
|
+
* merged on top (shallow).
|
|
20
|
+
*
|
|
21
|
+
* To provide a specific version of std (e.g. when the bridge file
|
|
22
|
+
* targets an older major), use a versioned namespace key:
|
|
23
|
+
* ```ts
|
|
24
|
+
* tools: { "std@1.5": oldStdNamespace }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
15
27
|
tools?: ToolMap;
|
|
16
28
|
/** Context available via `with context as ctx` inside the bridge. */
|
|
17
29
|
context?: Record<string, unknown>;
|
|
@@ -41,12 +53,12 @@ export type ExecuteBridgeResult<T = unknown> = {
|
|
|
41
53
|
*
|
|
42
54
|
* @example
|
|
43
55
|
* ```ts
|
|
44
|
-
* import {
|
|
56
|
+
* import { parseBridge, executeBridge } from "@stackables/bridge";
|
|
45
57
|
* import { readFileSync } from "node:fs";
|
|
46
58
|
*
|
|
47
|
-
* const
|
|
59
|
+
* const document = parseBridge(readFileSync("my.bridge", "utf8"));
|
|
48
60
|
* const { data } = await executeBridge({
|
|
49
|
-
*
|
|
61
|
+
* document,
|
|
50
62
|
* operation: "Query.myField",
|
|
51
63
|
* input: { city: "Berlin" },
|
|
52
64
|
* });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute-bridge.d.ts","sourceRoot":"","sources":["../../../src/execute-bridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAQ1D,MAAM,MAAM,oBAAoB,GAAG;IACjC,+EAA+E;IAC/E,QAAQ,EAAE,cAAc,CAAC;IACzB;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,CAAC,GAAG,OAAO,IAAI;IAC7C,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,aAAa,CAAC,CAAC,GAAG,OAAO,EAC7C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAyCjC"}
|