bitfab 0.28.11 → 0.29.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/dist/{chunk-ZBWTCBVQ.js → chunk-RTPEBWVO.js} +102 -45
- package/dist/chunk-RTPEBWVO.js.map +1 -0
- package/dist/{chunk-5E4BUIYA.js → chunk-YZU6WFG2.js} +113 -55
- package/dist/chunk-YZU6WFG2.js.map +1 -0
- package/dist/index.cjs +218 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +165 -15
- package/dist/index.d.ts +165 -15
- package/dist/index.js +2 -2
- package/dist/node.cjs +218 -96
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +2 -2
- package/dist/{replay-IEE4RY57.js → replay-SKW5A7II.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-5E4BUIYA.js.map +0 -1
- package/dist/chunk-ZBWTCBVQ.js.map +0 -1
- /package/dist/{replay-IEE4RY57.js.map → replay-SKW5A7II.js.map} +0 -0
package/dist/node.cjs
CHANGED
|
@@ -298,6 +298,22 @@ var init_randomUuid = __esm({
|
|
|
298
298
|
}
|
|
299
299
|
});
|
|
300
300
|
|
|
301
|
+
// src/mockOverride.ts
|
|
302
|
+
function resolveMockValue(value, ctx) {
|
|
303
|
+
return typeof value === "function" ? value(ctx) : value;
|
|
304
|
+
}
|
|
305
|
+
function normalizeMockOverrides(mockOverride) {
|
|
306
|
+
if (mockOverride === void 0) {
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
return Array.isArray(mockOverride) ? mockOverride : [mockOverride];
|
|
310
|
+
}
|
|
311
|
+
var init_mockOverride = __esm({
|
|
312
|
+
"src/mockOverride.ts"() {
|
|
313
|
+
"use strict";
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
301
317
|
// src/replayContext.ts
|
|
302
318
|
function getReplayContext() {
|
|
303
319
|
return replayContextStorage?.getStore() ?? null;
|
|
@@ -373,6 +389,7 @@ function buildMockTree(rootNode) {
|
|
|
373
389
|
counters.set(counterKey, index + 1);
|
|
374
390
|
spans.set(`${counterKey}:${index}`, {
|
|
375
391
|
sourceSpanId: node.sourceSpanId,
|
|
392
|
+
externalSpanId: node.externalSpanId,
|
|
376
393
|
output: node.output,
|
|
377
394
|
outputMeta: node.outputMeta
|
|
378
395
|
});
|
|
@@ -386,57 +403,79 @@ function buildMockTree(rootNode) {
|
|
|
386
403
|
}
|
|
387
404
|
return { spans };
|
|
388
405
|
}
|
|
389
|
-
async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, environment, adaptInputs) {
|
|
406
|
+
async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, environment, adaptInputs) {
|
|
390
407
|
const lease = environment ? serverItem.dbBranchLease : void 0;
|
|
391
408
|
let inputs = [];
|
|
392
409
|
let originalOutput;
|
|
393
410
|
let result;
|
|
394
411
|
let error = null;
|
|
395
|
-
const replayedTraceId = randomUuid();
|
|
396
412
|
const pendingPersistence = [];
|
|
413
|
+
const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
|
|
414
|
+
const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
|
|
397
415
|
try {
|
|
398
|
-
const span = await httpClient.getExternalSpan(
|
|
416
|
+
const span = await httpClient.getExternalSpan(originalSpanId);
|
|
399
417
|
const spanData = span.rawData?.span_data ?? {};
|
|
400
418
|
inputs = deserializeInputs(spanData);
|
|
401
419
|
originalOutput = deserializeOutput(spanData);
|
|
402
420
|
if (adaptInputs) {
|
|
403
421
|
inputs = adaptInputs(inputs, {
|
|
404
|
-
|
|
405
|
-
|
|
422
|
+
originalTraceId,
|
|
423
|
+
originalSpanId,
|
|
424
|
+
// Deprecated aliases for originalTraceId/originalSpanId.
|
|
425
|
+
sourceTraceId: originalTraceId,
|
|
426
|
+
sourceSpanId: originalSpanId
|
|
406
427
|
});
|
|
407
428
|
}
|
|
429
|
+
const hasOverrides = resolvedOverrides.length > 0;
|
|
430
|
+
const needTree = mockStrategy === "all" || mockStrategy === "marked" || hasOverrides;
|
|
431
|
+
const includeOutputs = mockStrategy === "all";
|
|
408
432
|
let mockTree;
|
|
409
|
-
if (
|
|
433
|
+
if (needTree) {
|
|
410
434
|
try {
|
|
411
|
-
const treeResponse = await httpClient.getSpanTree(
|
|
412
|
-
|
|
413
|
-
);
|
|
435
|
+
const treeResponse = await httpClient.getSpanTree(originalSpanId, {
|
|
436
|
+
includeOutputs
|
|
437
|
+
});
|
|
414
438
|
if (treeResponse.root) {
|
|
415
439
|
mockTree = buildMockTree(treeResponse.root);
|
|
416
|
-
} else if (mockStrategy === "all") {
|
|
440
|
+
} else if (mockStrategy === "all" || hasOverrides) {
|
|
417
441
|
throw new BitfabError(
|
|
418
|
-
`Replay mock strategy "
|
|
442
|
+
`Replay mock strategy "${mockStrategy}"${hasOverrides ? " with overrides" : ""} requires a span tree root for original span ${originalSpanId}.`
|
|
419
443
|
);
|
|
420
444
|
} else {
|
|
421
445
|
mockTree = void 0;
|
|
422
446
|
}
|
|
423
447
|
} catch (e) {
|
|
424
|
-
if (mockStrategy === "all") {
|
|
448
|
+
if (mockStrategy === "all" || hasOverrides) {
|
|
425
449
|
throw e;
|
|
426
450
|
}
|
|
427
451
|
mockTree = void 0;
|
|
428
452
|
}
|
|
429
453
|
}
|
|
454
|
+
const outputCache = /* @__PURE__ */ new Map();
|
|
455
|
+
const fetchSpanOutput = mockTree && !includeOutputs ? (externalSpanId) => {
|
|
456
|
+
let pending = outputCache.get(externalSpanId);
|
|
457
|
+
if (!pending) {
|
|
458
|
+
pending = httpClient.getExternalSpan(externalSpanId).then(
|
|
459
|
+
(s) => deserializeOutput(
|
|
460
|
+
s.rawData?.span_data ?? {}
|
|
461
|
+
)
|
|
462
|
+
);
|
|
463
|
+
outputCache.set(externalSpanId, pending);
|
|
464
|
+
}
|
|
465
|
+
return pending;
|
|
466
|
+
} : void 0;
|
|
430
467
|
const maybePromise = runWithReplayContext(
|
|
431
468
|
{
|
|
432
469
|
testRunId,
|
|
433
470
|
traceId: replayedTraceId,
|
|
434
471
|
inputSourceSpanId: span.id,
|
|
435
472
|
inputSourceTraceId: span.externalTraceId,
|
|
436
|
-
sourceBitfabTraceId:
|
|
473
|
+
sourceBitfabTraceId: originalTraceId,
|
|
437
474
|
mockTree,
|
|
438
475
|
callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
|
|
439
476
|
mockStrategy,
|
|
477
|
+
mockOverrides: hasOverrides ? resolvedOverrides : void 0,
|
|
478
|
+
fetchSpanOutput,
|
|
440
479
|
dbBranchLease: lease,
|
|
441
480
|
pendingPersistence
|
|
442
481
|
},
|
|
@@ -461,7 +500,15 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
461
500
|
}
|
|
462
501
|
}
|
|
463
502
|
return {
|
|
464
|
-
|
|
503
|
+
// Written in by replay() from the complete-replay response once the server
|
|
504
|
+
// has minted this replay trace's row. Null until then: the client-side
|
|
505
|
+
// correlation id (replayedTraceId) is never surfaced as the item's traceId.
|
|
506
|
+
traceId: null,
|
|
507
|
+
originalTraceId,
|
|
508
|
+
originalSpanId,
|
|
509
|
+
// Deprecated aliases for originalTraceId/originalSpanId.
|
|
510
|
+
sourceTraceId: originalTraceId,
|
|
511
|
+
sourceSpanId: originalSpanId,
|
|
465
512
|
input: inputs,
|
|
466
513
|
result,
|
|
467
514
|
originalOutput,
|
|
@@ -493,7 +540,7 @@ async function mapWithConcurrency(tasks, maxConcurrency, onSettled) {
|
|
|
493
540
|
await Promise.all(workers);
|
|
494
541
|
return results;
|
|
495
542
|
}
|
|
496
|
-
async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
543
|
+
async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, registeredOverrides = []) {
|
|
497
544
|
if (options?.traceIds !== void 0) {
|
|
498
545
|
if (options.traceIds.length === 0) {
|
|
499
546
|
throw new BitfabError("traceIds must contain at least one trace ID.");
|
|
@@ -533,13 +580,20 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
533
580
|
);
|
|
534
581
|
const mockStrategy = options?.mock ?? "marked";
|
|
535
582
|
const maxConcurrency = options?.maxConcurrency ?? 10;
|
|
583
|
+
const resolvedOverrides = [
|
|
584
|
+
...normalizeMockOverrides(options?.mockOverride),
|
|
585
|
+
...registeredOverrides
|
|
586
|
+
];
|
|
587
|
+
const replayedTraceIds = serverItems.map(() => randomUuid());
|
|
536
588
|
const tasks = serverItems.map(
|
|
537
|
-
(serverItem) => () => processItem(
|
|
589
|
+
(serverItem, index) => () => processItem(
|
|
538
590
|
httpClient,
|
|
539
591
|
serverItem,
|
|
540
592
|
fn,
|
|
541
593
|
testRunId,
|
|
542
594
|
mockStrategy,
|
|
595
|
+
resolvedOverrides,
|
|
596
|
+
replayedTraceIds[index],
|
|
543
597
|
options?.environment,
|
|
544
598
|
options?.adaptInputs
|
|
545
599
|
)
|
|
@@ -566,11 +620,17 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
566
620
|
succeeded,
|
|
567
621
|
errored,
|
|
568
622
|
item: {
|
|
569
|
-
//
|
|
570
|
-
//
|
|
571
|
-
//
|
|
572
|
-
|
|
573
|
-
|
|
623
|
+
// The server replay trace id isn't known until completeReplay
|
|
624
|
+
// runs (below), so it can't be reported mid-run and we never
|
|
625
|
+
// emit the client-side placeholder. originalTraceId (the
|
|
626
|
+
// historical trace) is known now and is what a UI keys on to
|
|
627
|
+
// identify what just settled.
|
|
628
|
+
traceId: null,
|
|
629
|
+
originalTraceId: item.originalTraceId ?? null,
|
|
630
|
+
originalSpanId: item.originalSpanId ?? null,
|
|
631
|
+
// Deprecated aliases for originalTraceId/originalSpanId.
|
|
632
|
+
sourceTraceId: item.originalTraceId ?? null,
|
|
633
|
+
sourceSpanId: item.originalSpanId ?? null,
|
|
574
634
|
input: item.input,
|
|
575
635
|
result: item.result,
|
|
576
636
|
originalOutput: item.originalOutput,
|
|
@@ -588,56 +648,58 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
588
648
|
const completeResult = await httpClient.completeReplay(testRunId);
|
|
589
649
|
const serverTraceIds = completeResult.traceIds;
|
|
590
650
|
const replayTokens = completeResult.tokens;
|
|
591
|
-
if (serverTraceIds
|
|
592
|
-
try {
|
|
593
|
-
console.warn(
|
|
594
|
-
"Bitfab: server did not return replay trace IDs; item.traceId will be null (server upgrade required for verdict persistence)"
|
|
595
|
-
);
|
|
596
|
-
} catch {
|
|
597
|
-
}
|
|
598
|
-
for (const item of resultItems) {
|
|
599
|
-
item.traceId = null;
|
|
600
|
-
}
|
|
601
|
-
} else {
|
|
651
|
+
if (serverTraceIds !== void 0) {
|
|
602
652
|
const missing = [];
|
|
603
653
|
let completedCount = 0;
|
|
604
|
-
for (
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (mapped !== void 0) {
|
|
614
|
-
item.tokens = replayTokens?.[mapped] ?? null;
|
|
654
|
+
for (let index = 0; index < resultItems.length; index += 1) {
|
|
655
|
+
const item = resultItems[index];
|
|
656
|
+
const localId = replayedTraceIds[index];
|
|
657
|
+
const mapped = localId ? serverTraceIds[localId] : void 0;
|
|
658
|
+
item.traceId = mapped ?? null;
|
|
659
|
+
if (item.error === null) {
|
|
660
|
+
completedCount += 1;
|
|
661
|
+
if (mapped === void 0) {
|
|
662
|
+
missing.push(localId ?? item.originalTraceId);
|
|
615
663
|
}
|
|
616
|
-
|
|
664
|
+
}
|
|
665
|
+
if (mapped !== void 0) {
|
|
666
|
+
item.tokens = replayTokens?.[mapped] ?? null;
|
|
617
667
|
}
|
|
618
668
|
}
|
|
619
|
-
if (missing.length
|
|
669
|
+
if (completedCount > 0 && missing.length === completedCount) {
|
|
620
670
|
const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
671
|
+
throw new BitfabError(
|
|
672
|
+
`Replay completed but the server has no persisted trace for any of the ${completedCount} completed item(s) (testRunId ${testRunId}).${serverCount} Trace uploads were awaited, so either the uploads failed (check for "Bitfab: Failed to create" errors above) or the replayed function is not wrapped with withSpan.`
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
if (missing.length > 0) {
|
|
626
676
|
try {
|
|
627
677
|
console.error(
|
|
628
|
-
`Bitfab: server has no persisted trace for ${missing.length} of ${completedCount} completed replay item(s) (testRunId ${testRunId})
|
|
678
|
+
`Bitfab: server has no persisted trace for ${missing.length} of ${completedCount} completed replay item(s) (testRunId ${testRunId}). Their replay token usage is unavailable and they cannot be labeled.`
|
|
629
679
|
);
|
|
630
680
|
} catch {
|
|
631
681
|
}
|
|
632
682
|
}
|
|
633
683
|
}
|
|
634
|
-
const
|
|
684
|
+
const result = {
|
|
635
685
|
items: resultItems,
|
|
636
686
|
testRunId,
|
|
637
687
|
testRunUrl: `${serviceUrl}${testRunUrl}`
|
|
638
688
|
};
|
|
639
|
-
await writeReplayResultFile(
|
|
640
|
-
|
|
689
|
+
await writeReplayResultFile(result);
|
|
690
|
+
try {
|
|
691
|
+
options?.onProgress?.({
|
|
692
|
+
type: "complete",
|
|
693
|
+
testRunId,
|
|
694
|
+
completed: total,
|
|
695
|
+
total,
|
|
696
|
+
succeeded,
|
|
697
|
+
errored,
|
|
698
|
+
result
|
|
699
|
+
});
|
|
700
|
+
} catch {
|
|
701
|
+
}
|
|
702
|
+
return result;
|
|
641
703
|
}
|
|
642
704
|
async function writeReplayResultFile(result) {
|
|
643
705
|
const resultPath = typeof process !== "undefined" ? process.env?.BITFAB_REPLAY_RESULT_PATH : void 0;
|
|
@@ -666,6 +728,7 @@ var init_replay = __esm({
|
|
|
666
728
|
"src/replay.ts"() {
|
|
667
729
|
"use strict";
|
|
668
730
|
init_errors();
|
|
731
|
+
init_mockOverride();
|
|
669
732
|
init_randomUuid();
|
|
670
733
|
init_replayContext();
|
|
671
734
|
init_serialize();
|
|
@@ -706,7 +769,7 @@ registerAsyncLocalStorageClass(
|
|
|
706
769
|
);
|
|
707
770
|
|
|
708
771
|
// src/version.generated.ts
|
|
709
|
-
var __version__ = "0.
|
|
772
|
+
var __version__ = "0.29.1";
|
|
710
773
|
|
|
711
774
|
// src/constants.ts
|
|
712
775
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
@@ -1135,9 +1198,14 @@ var HttpClient = class {
|
|
|
1135
1198
|
/**
|
|
1136
1199
|
* Fetch the span tree for a root span.
|
|
1137
1200
|
* Blocking GET request.
|
|
1201
|
+
*
|
|
1202
|
+
* Pass `includeOutputs: false` for a payload-free tree (structure +
|
|
1203
|
+
* `externalSpanId` only), so recorded outputs are fetched lazily per mocked
|
|
1204
|
+
* span instead of all up front. Omit it (default eager) for `mock: "all"`.
|
|
1138
1205
|
*/
|
|
1139
|
-
async getSpanTree(externalSpanId) {
|
|
1140
|
-
const
|
|
1206
|
+
async getSpanTree(externalSpanId, options) {
|
|
1207
|
+
const query = options?.includeOutputs === false ? "?includeOutputs=false" : "";
|
|
1208
|
+
const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}${query}`;
|
|
1141
1209
|
const controller = new AbortController();
|
|
1142
1210
|
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
1143
1211
|
try {
|
|
@@ -2723,6 +2791,9 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2723
2791
|
}
|
|
2724
2792
|
};
|
|
2725
2793
|
|
|
2794
|
+
// src/client.ts
|
|
2795
|
+
init_mockOverride();
|
|
2796
|
+
|
|
2726
2797
|
// src/openaiAgentSdk.ts
|
|
2727
2798
|
var BitfabOpenAIAgentHandler = class {
|
|
2728
2799
|
constructor(config) {
|
|
@@ -3538,6 +3609,12 @@ var Bitfab = class {
|
|
|
3538
3609
|
constructor(config) {
|
|
3539
3610
|
/** Gate the empty-key warning to fire at most once. */
|
|
3540
3611
|
this.apiKeyWarned = false;
|
|
3612
|
+
/**
|
|
3613
|
+
* Mock overrides registered via {@link Bitfab.registerMockOverride}, applied
|
|
3614
|
+
* to every `replay` on this client (after any per-call `mockOverride`). In
|
|
3615
|
+
* registration order; first matcher wins within this list.
|
|
3616
|
+
*/
|
|
3617
|
+
this.mockOverrides = [];
|
|
3541
3618
|
this.apiKeyConfig = config.apiKey;
|
|
3542
3619
|
this.serviceUrl = config.serviceUrl ?? DEFAULT_SERVICE_URL;
|
|
3543
3620
|
this.timeout = config.timeout ?? 12e4;
|
|
@@ -4122,7 +4199,7 @@ var Bitfab = class {
|
|
|
4122
4199
|
dbSnapshotUsage: {
|
|
4123
4200
|
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
4124
4201
|
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
4125
|
-
|
|
4202
|
+
originalTraceId: replayCtx.sourceBitfabTraceId,
|
|
4126
4203
|
accessed: replayCtx.dbSnapshotAccessed === true
|
|
4127
4204
|
}
|
|
4128
4205
|
}
|
|
@@ -4150,24 +4227,77 @@ var Bitfab = class {
|
|
|
4150
4227
|
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
4151
4228
|
const callIndex = counters.get(counterKey) ?? 0;
|
|
4152
4229
|
counters.set(counterKey, callIndex + 1);
|
|
4153
|
-
const
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
if (
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4230
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
4231
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
4232
|
+
const emitMock = (output) => {
|
|
4233
|
+
void sendSpan({ result: output, mocked: true });
|
|
4234
|
+
if (fnReturnsPromise) {
|
|
4235
|
+
return Promise.resolve(output);
|
|
4236
|
+
}
|
|
4237
|
+
return output;
|
|
4238
|
+
};
|
|
4239
|
+
const emitMockAsync = (pending) => {
|
|
4240
|
+
if (!fnReturnsPromise) {
|
|
4241
|
+
throw new BitfabError(
|
|
4242
|
+
`Cannot mock synchronous span "${traceFunctionKey}" with an asynchronously-resolved value (lazy recorded-output fetch or an async value function). Make the wrapped function async, or use mock: "all" so recorded outputs are fetched eagerly.`
|
|
4243
|
+
);
|
|
4244
|
+
}
|
|
4245
|
+
return (async () => {
|
|
4246
|
+
const output = await pending;
|
|
4165
4247
|
void sendSpan({ result: output, mocked: true });
|
|
4166
|
-
if (fnReturnsPromise) {
|
|
4167
|
-
return Promise.resolve(output);
|
|
4168
|
-
}
|
|
4169
4248
|
return output;
|
|
4249
|
+
})();
|
|
4250
|
+
};
|
|
4251
|
+
const resolveRecordedOutput = () => {
|
|
4252
|
+
const hasInlineOutput = mockSpan?.output !== void 0 || mockSpan?.outputMeta !== void 0;
|
|
4253
|
+
if (!hasInlineOutput && replayCtxForMock.fetchSpanOutput && mockSpan?.externalSpanId) {
|
|
4254
|
+
return replayCtxForMock.fetchSpanOutput(mockSpan.externalSpanId);
|
|
4255
|
+
}
|
|
4256
|
+
if (!mockSpan) {
|
|
4257
|
+
return Promise.reject(
|
|
4258
|
+
new BitfabError(
|
|
4259
|
+
`No recorded span to source output for "${traceFunctionKey}".`
|
|
4260
|
+
)
|
|
4261
|
+
);
|
|
4170
4262
|
}
|
|
4263
|
+
let output = mockSpan.output;
|
|
4264
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
4265
|
+
output = deserializeValue({
|
|
4266
|
+
json: mockSpan.output,
|
|
4267
|
+
meta: mockSpan.outputMeta
|
|
4268
|
+
});
|
|
4269
|
+
}
|
|
4270
|
+
return output;
|
|
4271
|
+
};
|
|
4272
|
+
if (replayCtxForMock.mockOverrides?.length) {
|
|
4273
|
+
const nodeMeta = {
|
|
4274
|
+
traceFunctionKey,
|
|
4275
|
+
spanName: baseSpanParams.spanName,
|
|
4276
|
+
type: options.type ?? "custom",
|
|
4277
|
+
originalSpanId: mockSpan?.sourceSpanId
|
|
4278
|
+
};
|
|
4279
|
+
const override = replayCtxForMock.mockOverrides.find(
|
|
4280
|
+
(o) => o.match(nodeMeta)
|
|
4281
|
+
);
|
|
4282
|
+
if (override) {
|
|
4283
|
+
const injected = resolveMockValue(override.value, {
|
|
4284
|
+
node: nodeMeta,
|
|
4285
|
+
inputs: args,
|
|
4286
|
+
getOriginalOutput: () => Promise.resolve(resolveRecordedOutput())
|
|
4287
|
+
});
|
|
4288
|
+
if (injected instanceof Promise) {
|
|
4289
|
+
return emitMockAsync(injected);
|
|
4290
|
+
}
|
|
4291
|
+
return emitMock(injected);
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
4295
|
+
if (shouldMock && mockSpan) {
|
|
4296
|
+
const recorded = resolveRecordedOutput();
|
|
4297
|
+
if (recorded instanceof Promise) {
|
|
4298
|
+
return emitMockAsync(recorded);
|
|
4299
|
+
}
|
|
4300
|
+
return emitMock(recorded);
|
|
4171
4301
|
}
|
|
4172
4302
|
}
|
|
4173
4303
|
const recordSpan = (result) => {
|
|
@@ -4373,8 +4503,11 @@ var Bitfab = class {
|
|
|
4373
4503
|
...params.dbSnapshotUsage.snapshotTimestamp && {
|
|
4374
4504
|
snapshot_timestamp: params.dbSnapshotUsage.snapshotTimestamp
|
|
4375
4505
|
},
|
|
4376
|
-
...params.dbSnapshotUsage.
|
|
4377
|
-
|
|
4506
|
+
...params.dbSnapshotUsage.originalTraceId && {
|
|
4507
|
+
original_trace_id: params.dbSnapshotUsage.originalTraceId,
|
|
4508
|
+
// Deprecated wire alias, kept so this SDK still reports usage
|
|
4509
|
+
// against servers that predate the rename.
|
|
4510
|
+
source_trace_id: params.dbSnapshotUsage.originalTraceId
|
|
4378
4511
|
},
|
|
4379
4512
|
accessed: params.dbSnapshotUsage.accessed
|
|
4380
4513
|
};
|
|
@@ -4447,26 +4580,14 @@ var Bitfab = class {
|
|
|
4447
4580
|
...params.mocked && { mocked: true }
|
|
4448
4581
|
});
|
|
4449
4582
|
}
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
* invocation records a trace tied to the test run. The plain-callable form
|
|
4459
|
-
* is how handler-instrumented workflows (LangGraph/LangChain, Claude Agent
|
|
4460
|
-
* SDK) replay - those record traces under a key with no `withSpan`-wrapped
|
|
4461
|
-
* root in the app.
|
|
4462
|
-
*
|
|
4463
|
-
* @param traceFunctionKey - The trace function key to replay
|
|
4464
|
-
* @param fn - The function to run recorded inputs through
|
|
4465
|
-
* @param options - Optional replay options. When `traceIds` is passed,
|
|
4466
|
-
* `limit` is ignored (with a warning): an explicit ID list already
|
|
4467
|
-
* determines how many traces replay.
|
|
4468
|
-
* @returns ReplayResult with items, testRunId, and testRunUrl
|
|
4469
|
-
*/
|
|
4583
|
+
registerMockOverride(overrideOrMatch, value) {
|
|
4584
|
+
const override = typeof overrideOrMatch === "function" ? { match: overrideOrMatch, value } : overrideOrMatch;
|
|
4585
|
+
this.mockOverrides.push(override);
|
|
4586
|
+
}
|
|
4587
|
+
/** Remove all overrides registered via {@link registerMockOverride}. */
|
|
4588
|
+
clearMockOverrides() {
|
|
4589
|
+
this.mockOverrides.length = 0;
|
|
4590
|
+
}
|
|
4470
4591
|
async replay(traceFunctionKey, fn, options) {
|
|
4471
4592
|
const wrappedKey = fn._bitfabTraceFunctionKey;
|
|
4472
4593
|
let replayFn = fn;
|
|
@@ -4487,7 +4608,8 @@ var Bitfab = class {
|
|
|
4487
4608
|
this.serviceUrl,
|
|
4488
4609
|
traceFunctionKey,
|
|
4489
4610
|
replayFn,
|
|
4490
|
-
options
|
|
4611
|
+
options,
|
|
4612
|
+
this.mockOverrides
|
|
4491
4613
|
);
|
|
4492
4614
|
}
|
|
4493
4615
|
};
|