@stackables/bridge 1.5.0 → 1.6.0
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/build/ExecutionTree.d.ts +49 -0
- package/build/ExecutionTree.d.ts.map +1 -1
- package/build/ExecutionTree.js +130 -31
- package/build/bridge-transform.d.ts +33 -0
- package/build/bridge-transform.d.ts.map +1 -1
- package/build/bridge-transform.js +46 -1
- package/build/index.d.ts +2 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/package.json +1 -1
package/build/ExecutionTree.d.ts
CHANGED
|
@@ -11,6 +11,45 @@ type Trunk = {
|
|
|
11
11
|
field: string;
|
|
12
12
|
instance?: number;
|
|
13
13
|
};
|
|
14
|
+
/** Trace verbosity level */
|
|
15
|
+
export type TraceLevel = "basic" | "full";
|
|
16
|
+
/** A single recorded tool invocation. */
|
|
17
|
+
export type ToolTrace = {
|
|
18
|
+
/** Tool name as resolved (e.g. "hereGeo", "std.upperCase") */
|
|
19
|
+
tool: string;
|
|
20
|
+
/** The function that was called (e.g. "httpCall", "upperCase") */
|
|
21
|
+
fn: string;
|
|
22
|
+
/** Input object passed to the tool function (only in "full" level) */
|
|
23
|
+
input?: Record<string, any>;
|
|
24
|
+
/** Resolved output (only in "full" level, on success) */
|
|
25
|
+
output?: any;
|
|
26
|
+
/** Error message (present when the tool threw) */
|
|
27
|
+
error?: string;
|
|
28
|
+
/** Wall-clock duration in milliseconds */
|
|
29
|
+
durationMs: number;
|
|
30
|
+
/** Monotonic timestamp (ms) relative to the first trace in the request */
|
|
31
|
+
startedAt: number;
|
|
32
|
+
};
|
|
33
|
+
/** Shared trace collector — one per request, passed through the tree. */
|
|
34
|
+
export declare class TraceCollector {
|
|
35
|
+
readonly traces: ToolTrace[];
|
|
36
|
+
readonly level: TraceLevel;
|
|
37
|
+
private readonly epoch;
|
|
38
|
+
constructor(level?: TraceLevel);
|
|
39
|
+
/** Returns ms since the collector was created */
|
|
40
|
+
now(): number;
|
|
41
|
+
record(trace: ToolTrace): void;
|
|
42
|
+
/** Build a trace entry, omitting input/output for basic level. */
|
|
43
|
+
entry(base: {
|
|
44
|
+
tool: string;
|
|
45
|
+
fn: string;
|
|
46
|
+
startedAt: number;
|
|
47
|
+
durationMs: number;
|
|
48
|
+
input?: Record<string, any>;
|
|
49
|
+
output?: any;
|
|
50
|
+
error?: string;
|
|
51
|
+
}): ToolTrace;
|
|
52
|
+
}
|
|
14
53
|
export declare class ExecutionTree {
|
|
15
54
|
trunk: Trunk;
|
|
16
55
|
private instructions;
|
|
@@ -22,6 +61,8 @@ export declare class ExecutionTree {
|
|
|
22
61
|
private toolDepCache;
|
|
23
62
|
private toolDefCache;
|
|
24
63
|
private pipeHandleMap;
|
|
64
|
+
/** Shared trace collector — present only when tracing is enabled. */
|
|
65
|
+
tracer?: TraceCollector;
|
|
25
66
|
constructor(trunk: Trunk, instructions: Instruction[], toolFns?: ToolMap | undefined, context?: Record<string, any> | undefined, parent?: ExecutionTree | undefined);
|
|
26
67
|
/** Derive tool name from a trunk */
|
|
27
68
|
private getToolName;
|
|
@@ -38,7 +79,15 @@ export declare class ExecutionTree {
|
|
|
38
79
|
private resolveToolDep;
|
|
39
80
|
schedule(target: Trunk): any;
|
|
40
81
|
shadow(): ExecutionTree;
|
|
82
|
+
/** Returns collected traces (empty array when tracing is disabled). */
|
|
83
|
+
getTraces(): ToolTrace[];
|
|
41
84
|
private pullSingle;
|
|
85
|
+
/**
|
|
86
|
+
* Infer the cost of resolving a NodeRef.
|
|
87
|
+
* Cost 0: memory reads (input, context, const) — no I/O.
|
|
88
|
+
* Cost 1: everything else (tool calls, pipes, defines) — may involve network.
|
|
89
|
+
*/
|
|
90
|
+
private inferCost;
|
|
42
91
|
pull(refs: NodeRef[]): Promise<any>;
|
|
43
92
|
push(args: Record<string, any>): void;
|
|
44
93
|
/** Eagerly schedule tools targeted by forced (<-!) wires. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExecutionTree.d.ts","sourceRoot":"","sources":["../src/ExecutionTree.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,MAAM,EAEN,WAAW,EACX,OAAO,EAGP,OAAO,EAER,MAAM,YAAY,CAAC;AAGpB,gFAAgF;AAChF,UAAU,IAAI;IACZ,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,KAAK,KAAK,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"ExecutionTree.d.ts","sourceRoot":"","sources":["../src/ExecutionTree.ts"],"names":[],"mappings":"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"}
|
package/build/ExecutionTree.js
CHANGED
|
@@ -17,6 +17,40 @@ function sameTrunk(a, b) {
|
|
|
17
17
|
function pathEquals(a, b) {
|
|
18
18
|
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
19
19
|
}
|
|
20
|
+
/** Shared trace collector — one per request, passed through the tree. */
|
|
21
|
+
export class TraceCollector {
|
|
22
|
+
traces = [];
|
|
23
|
+
level;
|
|
24
|
+
epoch = performance.now();
|
|
25
|
+
constructor(level = "full") {
|
|
26
|
+
this.level = level;
|
|
27
|
+
}
|
|
28
|
+
/** Returns ms since the collector was created */
|
|
29
|
+
now() {
|
|
30
|
+
return Math.round((performance.now() - this.epoch) * 100) / 100;
|
|
31
|
+
}
|
|
32
|
+
record(trace) {
|
|
33
|
+
this.traces.push(trace);
|
|
34
|
+
}
|
|
35
|
+
/** Build a trace entry, omitting input/output for basic level. */
|
|
36
|
+
entry(base) {
|
|
37
|
+
if (this.level === "basic") {
|
|
38
|
+
const t = { tool: base.tool, fn: base.fn, durationMs: base.durationMs, startedAt: base.startedAt };
|
|
39
|
+
if (base.error)
|
|
40
|
+
t.error = base.error;
|
|
41
|
+
return t;
|
|
42
|
+
}
|
|
43
|
+
// full
|
|
44
|
+
const t = { tool: base.tool, fn: base.fn, durationMs: base.durationMs, startedAt: base.startedAt };
|
|
45
|
+
if (base.input)
|
|
46
|
+
t.input = structuredClone(base.input);
|
|
47
|
+
if (base.error)
|
|
48
|
+
t.error = base.error;
|
|
49
|
+
else if (base.output !== undefined)
|
|
50
|
+
t.output = base.output;
|
|
51
|
+
return t;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
20
54
|
/** Set a value at a nested path, creating intermediate objects/arrays as needed */
|
|
21
55
|
function setNested(obj, path, value) {
|
|
22
56
|
for (let i = 0; i < path.length - 1; i++) {
|
|
@@ -42,6 +76,8 @@ export class ExecutionTree {
|
|
|
42
76
|
toolDepCache = new Map();
|
|
43
77
|
toolDefCache = new Map();
|
|
44
78
|
pipeHandleMap;
|
|
79
|
+
/** Shared trace collector — present only when tracing is enabled. */
|
|
80
|
+
tracer;
|
|
45
81
|
constructor(trunk, instructions, toolFns, context, parent) {
|
|
46
82
|
this.trunk = trunk;
|
|
47
83
|
this.instructions = instructions;
|
|
@@ -230,10 +266,19 @@ export class ExecutionTree {
|
|
|
230
266
|
throw new Error(`Tool function "${toolDef.fn}" not registered`);
|
|
231
267
|
// on error: wrap the tool call with fallback from onError wire
|
|
232
268
|
const onErrorWire = toolDef.wires.find((w) => w.kind === "onError");
|
|
269
|
+
const tracer = this.tracer;
|
|
270
|
+
const traceStart = tracer?.now();
|
|
233
271
|
try {
|
|
234
|
-
|
|
272
|
+
const result = await fn(input);
|
|
273
|
+
if (tracer && traceStart != null) {
|
|
274
|
+
tracer.record(tracer.entry({ tool: toolName, fn: toolDef.fn, input, output: result, durationMs: Math.round((tracer.now() - traceStart) * 100) / 100, startedAt: traceStart }));
|
|
275
|
+
}
|
|
276
|
+
return result;
|
|
235
277
|
}
|
|
236
278
|
catch (err) {
|
|
279
|
+
if (tracer && traceStart != null) {
|
|
280
|
+
tracer.record(tracer.entry({ tool: toolName, fn: toolDef.fn, input, error: err.message, durationMs: Math.round((tracer.now() - traceStart) * 100) / 100, startedAt: traceStart }));
|
|
281
|
+
}
|
|
237
282
|
if (!onErrorWire)
|
|
238
283
|
throw err;
|
|
239
284
|
if ("value" in onErrorWire)
|
|
@@ -304,10 +349,19 @@ export class ExecutionTree {
|
|
|
304
349
|
throw new Error(`Tool function "${toolDef.fn}" not registered`);
|
|
305
350
|
// on error: wrap the tool call with fallback from onError wire
|
|
306
351
|
const onErrorWire = toolDef.wires.find((w) => w.kind === "onError");
|
|
352
|
+
const tracer = this.tracer;
|
|
353
|
+
const traceStart = tracer?.now();
|
|
307
354
|
try {
|
|
308
|
-
|
|
355
|
+
const result = await fn(input);
|
|
356
|
+
if (tracer && traceStart != null) {
|
|
357
|
+
tracer.record(tracer.entry({ tool: toolName, fn: toolDef.fn, input, output: result, durationMs: Math.round((tracer.now() - traceStart) * 100) / 100, startedAt: traceStart }));
|
|
358
|
+
}
|
|
359
|
+
return result;
|
|
309
360
|
}
|
|
310
361
|
catch (err) {
|
|
362
|
+
if (tracer && traceStart != null) {
|
|
363
|
+
tracer.record(tracer.entry({ tool: toolName, fn: toolDef.fn, input, error: err.message, durationMs: Math.round((tracer.now() - traceStart) * 100) / 100, startedAt: traceStart }));
|
|
364
|
+
}
|
|
311
365
|
if (!onErrorWire)
|
|
312
366
|
throw err;
|
|
313
367
|
if ("value" in onErrorWire)
|
|
@@ -318,7 +372,21 @@ export class ExecutionTree {
|
|
|
318
372
|
// Direct tool function lookup by name (simple or dotted)
|
|
319
373
|
const directFn = this.lookupToolFn(toolName);
|
|
320
374
|
if (directFn) {
|
|
321
|
-
|
|
375
|
+
const tracer = this.tracer;
|
|
376
|
+
const traceStart = tracer?.now();
|
|
377
|
+
try {
|
|
378
|
+
const result = await directFn(input);
|
|
379
|
+
if (tracer && traceStart != null) {
|
|
380
|
+
tracer.record(tracer.entry({ tool: toolName, fn: toolName, input, output: result, durationMs: Math.round((tracer.now() - traceStart) * 100) / 100, startedAt: traceStart }));
|
|
381
|
+
}
|
|
382
|
+
return result;
|
|
383
|
+
}
|
|
384
|
+
catch (err) {
|
|
385
|
+
if (tracer && traceStart != null) {
|
|
386
|
+
tracer.record(tracer.entry({ tool: toolName, fn: toolName, input, error: err.message, durationMs: Math.round((tracer.now() - traceStart) * 100) / 100, startedAt: traceStart }));
|
|
387
|
+
}
|
|
388
|
+
throw err;
|
|
389
|
+
}
|
|
322
390
|
}
|
|
323
391
|
// Define pass-through: synthetic trunks created by define inlining
|
|
324
392
|
// act as data containers — bridge wires set their values, no tool needed.
|
|
@@ -329,7 +397,13 @@ export class ExecutionTree {
|
|
|
329
397
|
})();
|
|
330
398
|
}
|
|
331
399
|
shadow() {
|
|
332
|
-
|
|
400
|
+
const child = new ExecutionTree(this.trunk, this.instructions, this.toolFns, undefined, this);
|
|
401
|
+
child.tracer = this.tracer;
|
|
402
|
+
return child;
|
|
403
|
+
}
|
|
404
|
+
/** Returns collected traces (empty array when tracing is disabled). */
|
|
405
|
+
getTraces() {
|
|
406
|
+
return this.tracer?.traces ?? [];
|
|
333
407
|
}
|
|
334
408
|
async pullSingle(ref) {
|
|
335
409
|
const key = trunkKey(ref);
|
|
@@ -359,37 +433,62 @@ export class ExecutionTree {
|
|
|
359
433
|
}
|
|
360
434
|
return result;
|
|
361
435
|
}
|
|
436
|
+
/**
|
|
437
|
+
* Infer the cost of resolving a NodeRef.
|
|
438
|
+
* Cost 0: memory reads (input, context, const) — no I/O.
|
|
439
|
+
* Cost 1: everything else (tool calls, pipes, defines) — may involve network.
|
|
440
|
+
*/
|
|
441
|
+
inferCost(ref) {
|
|
442
|
+
// Input args, context, and const live in the state map — free reads
|
|
443
|
+
if (ref.module === SELF_MODULE) {
|
|
444
|
+
const key = trunkKey(ref);
|
|
445
|
+
// Already resolved in state? Free.
|
|
446
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
447
|
+
let cursor = this;
|
|
448
|
+
while (cursor) {
|
|
449
|
+
if (cursor.state[key] !== undefined)
|
|
450
|
+
return 0;
|
|
451
|
+
cursor = cursor.parent;
|
|
452
|
+
}
|
|
453
|
+
// Input/context/const trunks are always cost 0
|
|
454
|
+
if (ref.type === "Context" || ref.type === "Const")
|
|
455
|
+
return 0;
|
|
456
|
+
// Input args trunk: _:Query:fieldName (same as this.trunk) — cost 0
|
|
457
|
+
if (ref.module === SELF_MODULE && ref.type === this.trunk.type && ref.field === this.trunk.field && !ref.element)
|
|
458
|
+
return 0;
|
|
459
|
+
}
|
|
460
|
+
return 1;
|
|
461
|
+
}
|
|
362
462
|
async pull(refs) {
|
|
363
463
|
if (refs.length === 1)
|
|
364
464
|
return this.pullSingle(refs[0]);
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
//
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
};
|
|
383
|
-
for (const ref of refs) {
|
|
384
|
-
this.pullSingle(ref).then((value) => {
|
|
385
|
-
if (!hasValue && value != null) {
|
|
386
|
-
hasValue = true;
|
|
387
|
-
resolve(value);
|
|
388
|
-
}
|
|
389
|
-
settle();
|
|
390
|
-
}, (err) => { errors.push(err); settle(); });
|
|
465
|
+
// Cost-sorted sequential evaluation with short-circuit.
|
|
466
|
+
//
|
|
467
|
+
// Sort by inferred cost (stable — preserves declaration order within
|
|
468
|
+
// the same cost tier). This means:
|
|
469
|
+
// • || chains (both sources are tools = same cost) → left-to-right
|
|
470
|
+
// • Overdefinition with mixed costs → cheapest first
|
|
471
|
+
//
|
|
472
|
+
// Evaluate sequentially. Return the first non-null value.
|
|
473
|
+
// If all return null/undefined → return undefined (lets || fire).
|
|
474
|
+
// If all throw → throw AggregateError (lets ?? fire).
|
|
475
|
+
const sorted = [...refs].sort((a, b) => this.inferCost(a) - this.inferCost(b));
|
|
476
|
+
const errors = [];
|
|
477
|
+
for (const ref of sorted) {
|
|
478
|
+
try {
|
|
479
|
+
const value = await this.pullSingle(ref);
|
|
480
|
+
if (value != null)
|
|
481
|
+
return value; // Short-circuit: found data
|
|
391
482
|
}
|
|
392
|
-
|
|
483
|
+
catch (err) {
|
|
484
|
+
errors.push(err);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
// All resolved to null/undefined, or all threw
|
|
488
|
+
if (errors.length === refs.length) {
|
|
489
|
+
throw new AggregateError(errors, "All sources failed");
|
|
490
|
+
}
|
|
491
|
+
return undefined;
|
|
393
492
|
}
|
|
394
493
|
push(args) {
|
|
395
494
|
this.state[trunkKey(this.trunk)] = args;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type GraphQLSchema } from "graphql";
|
|
2
|
+
import { type ToolTrace, type TraceLevel } from "./ExecutionTree.js";
|
|
2
3
|
import type { Instruction, ToolMap } from "./types.js";
|
|
3
4
|
export type BridgeOptions = {
|
|
4
5
|
/** Tool functions available to the engine.
|
|
@@ -9,8 +10,40 @@ export type BridgeOptions = {
|
|
|
9
10
|
/** Optional function to reshape/restrict the GQL context before it reaches bridge files.
|
|
10
11
|
* By default the full context is exposed via `with context`. */
|
|
11
12
|
contextMapper?: (context: any) => Record<string, any>;
|
|
13
|
+
/** Enable tool-call tracing.
|
|
14
|
+
* - `true` or `"full"`: record tool, fn, input, output/error, timing
|
|
15
|
+
* - `"basic"`: record tool, fn, timing, error (no input/output) */
|
|
16
|
+
trace?: boolean | TraceLevel;
|
|
12
17
|
};
|
|
13
18
|
/** Instructions can be a static array or a function that selects per-request */
|
|
14
19
|
export type InstructionSource = Instruction[] | ((context: any) => Instruction[]);
|
|
15
20
|
export declare function bridgeTransform(schema: GraphQLSchema, instructions: InstructionSource, options?: BridgeOptions): GraphQLSchema;
|
|
21
|
+
/**
|
|
22
|
+
* Read traces that were collected during the current request.
|
|
23
|
+
* Pass the GraphQL context object; returns an empty array when tracing is
|
|
24
|
+
* disabled or no traces were recorded.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getBridgeTraces(context: any): ToolTrace[];
|
|
27
|
+
/**
|
|
28
|
+
* Envelop-compatible plugin for GraphQL Yoga (or any Envelop-based server).
|
|
29
|
+
* When bridge tracing is enabled, this plugin copies the recorded traces into
|
|
30
|
+
* the GraphQL response `extensions.traces` field.
|
|
31
|
+
*
|
|
32
|
+
* Usage:
|
|
33
|
+
* ```ts
|
|
34
|
+
* createYoga({ schema, plugins: [useBridgeTracing()] })
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function useBridgeTracing(): {
|
|
38
|
+
onExecute({ args }: {
|
|
39
|
+
args: {
|
|
40
|
+
contextValue: any;
|
|
41
|
+
};
|
|
42
|
+
}): {
|
|
43
|
+
onExecuteDone({ result, setResult, }: {
|
|
44
|
+
result: any;
|
|
45
|
+
setResult: (r: any) => void;
|
|
46
|
+
}): void;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
16
49
|
//# sourceMappingURL=bridge-transform.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-transform.d.ts","sourceRoot":"","sources":["../src/bridge-transform.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"bridge-transform.d.ts","sourceRoot":"","sources":["../src/bridge-transform.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAiC,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEpG,OAAO,KAAK,EAAE,WAAW,EAAc,OAAO,EAAE,MAAM,YAAY,CAAC;AAGnE,MAAM,MAAM,aAAa,GAAG;IAC1B;;;kDAG8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;qEACiE;IACjE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtD;;wEAEoE;IACpE,KAAK,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;CAC9B,CAAC;AAEF,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GACzB,WAAW,EAAE,GACb,CAAC,CAAC,OAAO,EAAE,GAAG,KAAK,WAAW,EAAE,CAAC,CAAC;AAEtC,wBAAgB,eAAe,CAC7B,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,iBAAiB,EAC/B,OAAO,CAAC,EAAE,aAAa,GACtB,aAAa,CAuFf;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,EAAE,CAEzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB;wBAER;QAAE,IAAI,EAAE;YAAE,YAAY,EAAE,GAAG,CAAA;SAAE,CAAA;KAAE;8CAK5C;YACD,MAAM,EAAE,GAAG,CAAC;YACZ,SAAS,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;SAC7B;;EAeR"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { MapperKind, mapSchema } from "@graphql-tools/utils";
|
|
2
2
|
import { GraphQLList, GraphQLNonNull, defaultFieldResolver, } from "graphql";
|
|
3
|
-
import { ExecutionTree } from "./ExecutionTree.js";
|
|
3
|
+
import { ExecutionTree, TraceCollector } from "./ExecutionTree.js";
|
|
4
4
|
import { builtinTools } from "./tools/index.js";
|
|
5
5
|
import { SELF_MODULE } from "./types.js";
|
|
6
6
|
export function bridgeTransform(schema, instructions, options) {
|
|
7
7
|
const userTools = options?.tools;
|
|
8
8
|
const contextMapper = options?.contextMapper;
|
|
9
|
+
const tracing = options?.trace ?? false;
|
|
10
|
+
const traceLevel = tracing === true ? "full" : tracing === false ? false : tracing;
|
|
9
11
|
return mapSchema(schema, {
|
|
10
12
|
[MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
|
|
11
13
|
let array = false;
|
|
@@ -36,6 +38,11 @@ export function bridgeTransform(schema, instructions, options) {
|
|
|
36
38
|
...(userTools ?? {}),
|
|
37
39
|
};
|
|
38
40
|
source = new ExecutionTree(trunk, activeInstructions, allTools, bridgeContext);
|
|
41
|
+
if (traceLevel) {
|
|
42
|
+
source.tracer = new TraceCollector(traceLevel);
|
|
43
|
+
// Stash tracer on GQL context so the tracing plugin can read it
|
|
44
|
+
context.__bridgeTracer = source.tracer;
|
|
45
|
+
}
|
|
39
46
|
}
|
|
40
47
|
if (source instanceof ExecutionTree &&
|
|
41
48
|
args &&
|
|
@@ -60,3 +67,41 @@ export function bridgeTransform(schema, instructions, options) {
|
|
|
60
67
|
},
|
|
61
68
|
});
|
|
62
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Read traces that were collected during the current request.
|
|
72
|
+
* Pass the GraphQL context object; returns an empty array when tracing is
|
|
73
|
+
* disabled or no traces were recorded.
|
|
74
|
+
*/
|
|
75
|
+
export function getBridgeTraces(context) {
|
|
76
|
+
return context?.__bridgeTracer?.traces ?? [];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Envelop-compatible plugin for GraphQL Yoga (or any Envelop-based server).
|
|
80
|
+
* When bridge tracing is enabled, this plugin copies the recorded traces into
|
|
81
|
+
* the GraphQL response `extensions.traces` field.
|
|
82
|
+
*
|
|
83
|
+
* Usage:
|
|
84
|
+
* ```ts
|
|
85
|
+
* createYoga({ schema, plugins: [useBridgeTracing()] })
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export function useBridgeTracing() {
|
|
89
|
+
return {
|
|
90
|
+
onExecute({ args }) {
|
|
91
|
+
return {
|
|
92
|
+
onExecuteDone({ result, setResult, }) {
|
|
93
|
+
const traces = getBridgeTraces(args.contextValue);
|
|
94
|
+
if (traces.length > 0 && result && "data" in result) {
|
|
95
|
+
setResult({
|
|
96
|
+
...result,
|
|
97
|
+
extensions: {
|
|
98
|
+
...(result.extensions ?? {}),
|
|
99
|
+
traces,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
package/build/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { parseBridge } from "./bridge-format.js";
|
|
2
|
-
export { bridgeTransform } from "./bridge-transform.js";
|
|
2
|
+
export { bridgeTransform, getBridgeTraces, useBridgeTracing } from "./bridge-transform.js";
|
|
3
3
|
export type { BridgeOptions, InstructionSource } from "./bridge-transform.js";
|
|
4
4
|
export { builtinTools, std, createHttpCall } from "./tools/index.js";
|
|
5
|
+
export type { ToolTrace, TraceLevel } from "./ExecutionTree.js";
|
|
5
6
|
export type { CacheStore, ConstDef, Instruction, ToolCallFn, ToolDef, ToolMap } from "./types.js";
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC3F,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACrE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
|
package/build/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { parseBridge } from "./bridge-format.js";
|
|
2
|
-
export { bridgeTransform } from "./bridge-transform.js";
|
|
2
|
+
export { bridgeTransform, getBridgeTraces, useBridgeTracing } from "./bridge-transform.js";
|
|
3
3
|
export { builtinTools, std, createHttpCall } from "./tools/index.js";
|