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/index.cjs
CHANGED
|
@@ -291,6 +291,22 @@ var init_asyncStorage = __esm({
|
|
|
291
291
|
}
|
|
292
292
|
});
|
|
293
293
|
|
|
294
|
+
// src/mockOverride.ts
|
|
295
|
+
function resolveMockValue(value, ctx) {
|
|
296
|
+
return typeof value === "function" ? value(ctx) : value;
|
|
297
|
+
}
|
|
298
|
+
function normalizeMockOverrides(mockOverride) {
|
|
299
|
+
if (mockOverride === void 0) {
|
|
300
|
+
return [];
|
|
301
|
+
}
|
|
302
|
+
return Array.isArray(mockOverride) ? mockOverride : [mockOverride];
|
|
303
|
+
}
|
|
304
|
+
var init_mockOverride = __esm({
|
|
305
|
+
"src/mockOverride.ts"() {
|
|
306
|
+
"use strict";
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
294
310
|
// src/replayContext.ts
|
|
295
311
|
function getReplayContext() {
|
|
296
312
|
return replayContextStorage?.getStore() ?? null;
|
|
@@ -366,6 +382,7 @@ function buildMockTree(rootNode) {
|
|
|
366
382
|
counters.set(counterKey, index + 1);
|
|
367
383
|
spans.set(`${counterKey}:${index}`, {
|
|
368
384
|
sourceSpanId: node.sourceSpanId,
|
|
385
|
+
externalSpanId: node.externalSpanId,
|
|
369
386
|
output: node.output,
|
|
370
387
|
outputMeta: node.outputMeta
|
|
371
388
|
});
|
|
@@ -379,57 +396,79 @@ function buildMockTree(rootNode) {
|
|
|
379
396
|
}
|
|
380
397
|
return { spans };
|
|
381
398
|
}
|
|
382
|
-
async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, environment, adaptInputs) {
|
|
399
|
+
async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, environment, adaptInputs) {
|
|
383
400
|
const lease = environment ? serverItem.dbBranchLease : void 0;
|
|
384
401
|
let inputs = [];
|
|
385
402
|
let originalOutput;
|
|
386
403
|
let result;
|
|
387
404
|
let error = null;
|
|
388
|
-
const replayedTraceId = randomUuid();
|
|
389
405
|
const pendingPersistence = [];
|
|
406
|
+
const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
|
|
407
|
+
const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
|
|
390
408
|
try {
|
|
391
|
-
const span = await httpClient.getExternalSpan(
|
|
409
|
+
const span = await httpClient.getExternalSpan(originalSpanId);
|
|
392
410
|
const spanData = span.rawData?.span_data ?? {};
|
|
393
411
|
inputs = deserializeInputs(spanData);
|
|
394
412
|
originalOutput = deserializeOutput(spanData);
|
|
395
413
|
if (adaptInputs) {
|
|
396
414
|
inputs = adaptInputs(inputs, {
|
|
397
|
-
|
|
398
|
-
|
|
415
|
+
originalTraceId,
|
|
416
|
+
originalSpanId,
|
|
417
|
+
// Deprecated aliases for originalTraceId/originalSpanId.
|
|
418
|
+
sourceTraceId: originalTraceId,
|
|
419
|
+
sourceSpanId: originalSpanId
|
|
399
420
|
});
|
|
400
421
|
}
|
|
422
|
+
const hasOverrides = resolvedOverrides.length > 0;
|
|
423
|
+
const needTree = mockStrategy === "all" || mockStrategy === "marked" || hasOverrides;
|
|
424
|
+
const includeOutputs = mockStrategy === "all";
|
|
401
425
|
let mockTree;
|
|
402
|
-
if (
|
|
426
|
+
if (needTree) {
|
|
403
427
|
try {
|
|
404
|
-
const treeResponse = await httpClient.getSpanTree(
|
|
405
|
-
|
|
406
|
-
);
|
|
428
|
+
const treeResponse = await httpClient.getSpanTree(originalSpanId, {
|
|
429
|
+
includeOutputs
|
|
430
|
+
});
|
|
407
431
|
if (treeResponse.root) {
|
|
408
432
|
mockTree = buildMockTree(treeResponse.root);
|
|
409
|
-
} else if (mockStrategy === "all") {
|
|
433
|
+
} else if (mockStrategy === "all" || hasOverrides) {
|
|
410
434
|
throw new BitfabError(
|
|
411
|
-
`Replay mock strategy "
|
|
435
|
+
`Replay mock strategy "${mockStrategy}"${hasOverrides ? " with overrides" : ""} requires a span tree root for original span ${originalSpanId}.`
|
|
412
436
|
);
|
|
413
437
|
} else {
|
|
414
438
|
mockTree = void 0;
|
|
415
439
|
}
|
|
416
440
|
} catch (e) {
|
|
417
|
-
if (mockStrategy === "all") {
|
|
441
|
+
if (mockStrategy === "all" || hasOverrides) {
|
|
418
442
|
throw e;
|
|
419
443
|
}
|
|
420
444
|
mockTree = void 0;
|
|
421
445
|
}
|
|
422
446
|
}
|
|
447
|
+
const outputCache = /* @__PURE__ */ new Map();
|
|
448
|
+
const fetchSpanOutput = mockTree && !includeOutputs ? (externalSpanId) => {
|
|
449
|
+
let pending = outputCache.get(externalSpanId);
|
|
450
|
+
if (!pending) {
|
|
451
|
+
pending = httpClient.getExternalSpan(externalSpanId).then(
|
|
452
|
+
(s) => deserializeOutput(
|
|
453
|
+
s.rawData?.span_data ?? {}
|
|
454
|
+
)
|
|
455
|
+
);
|
|
456
|
+
outputCache.set(externalSpanId, pending);
|
|
457
|
+
}
|
|
458
|
+
return pending;
|
|
459
|
+
} : void 0;
|
|
423
460
|
const maybePromise = runWithReplayContext(
|
|
424
461
|
{
|
|
425
462
|
testRunId,
|
|
426
463
|
traceId: replayedTraceId,
|
|
427
464
|
inputSourceSpanId: span.id,
|
|
428
465
|
inputSourceTraceId: span.externalTraceId,
|
|
429
|
-
sourceBitfabTraceId:
|
|
466
|
+
sourceBitfabTraceId: originalTraceId,
|
|
430
467
|
mockTree,
|
|
431
468
|
callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
|
|
432
469
|
mockStrategy,
|
|
470
|
+
mockOverrides: hasOverrides ? resolvedOverrides : void 0,
|
|
471
|
+
fetchSpanOutput,
|
|
433
472
|
dbBranchLease: lease,
|
|
434
473
|
pendingPersistence
|
|
435
474
|
},
|
|
@@ -454,7 +493,15 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
454
493
|
}
|
|
455
494
|
}
|
|
456
495
|
return {
|
|
457
|
-
|
|
496
|
+
// Written in by replay() from the complete-replay response once the server
|
|
497
|
+
// has minted this replay trace's row. Null until then: the client-side
|
|
498
|
+
// correlation id (replayedTraceId) is never surfaced as the item's traceId.
|
|
499
|
+
traceId: null,
|
|
500
|
+
originalTraceId,
|
|
501
|
+
originalSpanId,
|
|
502
|
+
// Deprecated aliases for originalTraceId/originalSpanId.
|
|
503
|
+
sourceTraceId: originalTraceId,
|
|
504
|
+
sourceSpanId: originalSpanId,
|
|
458
505
|
input: inputs,
|
|
459
506
|
result,
|
|
460
507
|
originalOutput,
|
|
@@ -486,7 +533,7 @@ async function mapWithConcurrency(tasks, maxConcurrency, onSettled) {
|
|
|
486
533
|
await Promise.all(workers);
|
|
487
534
|
return results;
|
|
488
535
|
}
|
|
489
|
-
async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
536
|
+
async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, registeredOverrides = []) {
|
|
490
537
|
if (options?.traceIds !== void 0) {
|
|
491
538
|
if (options.traceIds.length === 0) {
|
|
492
539
|
throw new BitfabError("traceIds must contain at least one trace ID.");
|
|
@@ -526,13 +573,20 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
526
573
|
);
|
|
527
574
|
const mockStrategy = options?.mock ?? "marked";
|
|
528
575
|
const maxConcurrency = options?.maxConcurrency ?? 10;
|
|
576
|
+
const resolvedOverrides = [
|
|
577
|
+
...normalizeMockOverrides(options?.mockOverride),
|
|
578
|
+
...registeredOverrides
|
|
579
|
+
];
|
|
580
|
+
const replayedTraceIds = serverItems.map(() => randomUuid());
|
|
529
581
|
const tasks = serverItems.map(
|
|
530
|
-
(serverItem) => () => processItem(
|
|
582
|
+
(serverItem, index) => () => processItem(
|
|
531
583
|
httpClient,
|
|
532
584
|
serverItem,
|
|
533
585
|
fn,
|
|
534
586
|
testRunId,
|
|
535
587
|
mockStrategy,
|
|
588
|
+
resolvedOverrides,
|
|
589
|
+
replayedTraceIds[index],
|
|
536
590
|
options?.environment,
|
|
537
591
|
options?.adaptInputs
|
|
538
592
|
)
|
|
@@ -559,11 +613,17 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
559
613
|
succeeded,
|
|
560
614
|
errored,
|
|
561
615
|
item: {
|
|
562
|
-
//
|
|
563
|
-
//
|
|
564
|
-
//
|
|
565
|
-
|
|
566
|
-
|
|
616
|
+
// The server replay trace id isn't known until completeReplay
|
|
617
|
+
// runs (below), so it can't be reported mid-run and we never
|
|
618
|
+
// emit the client-side placeholder. originalTraceId (the
|
|
619
|
+
// historical trace) is known now and is what a UI keys on to
|
|
620
|
+
// identify what just settled.
|
|
621
|
+
traceId: null,
|
|
622
|
+
originalTraceId: item.originalTraceId ?? null,
|
|
623
|
+
originalSpanId: item.originalSpanId ?? null,
|
|
624
|
+
// Deprecated aliases for originalTraceId/originalSpanId.
|
|
625
|
+
sourceTraceId: item.originalTraceId ?? null,
|
|
626
|
+
sourceSpanId: item.originalSpanId ?? null,
|
|
567
627
|
input: item.input,
|
|
568
628
|
result: item.result,
|
|
569
629
|
originalOutput: item.originalOutput,
|
|
@@ -581,56 +641,58 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
581
641
|
const completeResult = await httpClient.completeReplay(testRunId);
|
|
582
642
|
const serverTraceIds = completeResult.traceIds;
|
|
583
643
|
const replayTokens = completeResult.tokens;
|
|
584
|
-
if (serverTraceIds
|
|
585
|
-
try {
|
|
586
|
-
console.warn(
|
|
587
|
-
"Bitfab: server did not return replay trace IDs; item.traceId will be null (server upgrade required for verdict persistence)"
|
|
588
|
-
);
|
|
589
|
-
} catch {
|
|
590
|
-
}
|
|
591
|
-
for (const item of resultItems) {
|
|
592
|
-
item.traceId = null;
|
|
593
|
-
}
|
|
594
|
-
} else {
|
|
644
|
+
if (serverTraceIds !== void 0) {
|
|
595
645
|
const missing = [];
|
|
596
646
|
let completedCount = 0;
|
|
597
|
-
for (
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
if (mapped !== void 0) {
|
|
607
|
-
item.tokens = replayTokens?.[mapped] ?? null;
|
|
647
|
+
for (let index = 0; index < resultItems.length; index += 1) {
|
|
648
|
+
const item = resultItems[index];
|
|
649
|
+
const localId = replayedTraceIds[index];
|
|
650
|
+
const mapped = localId ? serverTraceIds[localId] : void 0;
|
|
651
|
+
item.traceId = mapped ?? null;
|
|
652
|
+
if (item.error === null) {
|
|
653
|
+
completedCount += 1;
|
|
654
|
+
if (mapped === void 0) {
|
|
655
|
+
missing.push(localId ?? item.originalTraceId);
|
|
608
656
|
}
|
|
609
|
-
|
|
657
|
+
}
|
|
658
|
+
if (mapped !== void 0) {
|
|
659
|
+
item.tokens = replayTokens?.[mapped] ?? null;
|
|
610
660
|
}
|
|
611
661
|
}
|
|
612
|
-
if (missing.length
|
|
662
|
+
if (completedCount > 0 && missing.length === completedCount) {
|
|
613
663
|
const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
664
|
+
throw new BitfabError(
|
|
665
|
+
`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.`
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
if (missing.length > 0) {
|
|
619
669
|
try {
|
|
620
670
|
console.error(
|
|
621
|
-
`Bitfab: server has no persisted trace for ${missing.length} of ${completedCount} completed replay item(s) (testRunId ${testRunId})
|
|
671
|
+
`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.`
|
|
622
672
|
);
|
|
623
673
|
} catch {
|
|
624
674
|
}
|
|
625
675
|
}
|
|
626
676
|
}
|
|
627
|
-
const
|
|
677
|
+
const result = {
|
|
628
678
|
items: resultItems,
|
|
629
679
|
testRunId,
|
|
630
680
|
testRunUrl: `${serviceUrl}${testRunUrl}`
|
|
631
681
|
};
|
|
632
|
-
await writeReplayResultFile(
|
|
633
|
-
|
|
682
|
+
await writeReplayResultFile(result);
|
|
683
|
+
try {
|
|
684
|
+
options?.onProgress?.({
|
|
685
|
+
type: "complete",
|
|
686
|
+
testRunId,
|
|
687
|
+
completed: total,
|
|
688
|
+
total,
|
|
689
|
+
succeeded,
|
|
690
|
+
errored,
|
|
691
|
+
result
|
|
692
|
+
});
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
return result;
|
|
634
696
|
}
|
|
635
697
|
async function writeReplayResultFile(result) {
|
|
636
698
|
const resultPath = typeof process !== "undefined" ? process.env?.BITFAB_REPLAY_RESULT_PATH : void 0;
|
|
@@ -659,6 +721,7 @@ var init_replay = __esm({
|
|
|
659
721
|
"src/replay.ts"() {
|
|
660
722
|
"use strict";
|
|
661
723
|
init_errors();
|
|
724
|
+
init_mockOverride();
|
|
662
725
|
init_randomUuid();
|
|
663
726
|
init_replayContext();
|
|
664
727
|
init_serialize();
|
|
@@ -692,7 +755,7 @@ __export(index_exports, {
|
|
|
692
755
|
module.exports = __toCommonJS(index_exports);
|
|
693
756
|
|
|
694
757
|
// src/version.generated.ts
|
|
695
|
-
var __version__ = "0.
|
|
758
|
+
var __version__ = "0.29.1";
|
|
696
759
|
|
|
697
760
|
// src/constants.ts
|
|
698
761
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
@@ -1121,9 +1184,14 @@ var HttpClient = class {
|
|
|
1121
1184
|
/**
|
|
1122
1185
|
* Fetch the span tree for a root span.
|
|
1123
1186
|
* Blocking GET request.
|
|
1187
|
+
*
|
|
1188
|
+
* Pass `includeOutputs: false` for a payload-free tree (structure +
|
|
1189
|
+
* `externalSpanId` only), so recorded outputs are fetched lazily per mocked
|
|
1190
|
+
* span instead of all up front. Omit it (default eager) for `mock: "all"`.
|
|
1124
1191
|
*/
|
|
1125
|
-
async getSpanTree(externalSpanId) {
|
|
1126
|
-
const
|
|
1192
|
+
async getSpanTree(externalSpanId, options) {
|
|
1193
|
+
const query = options?.includeOutputs === false ? "?includeOutputs=false" : "";
|
|
1194
|
+
const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}${query}`;
|
|
1127
1195
|
const controller = new AbortController();
|
|
1128
1196
|
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
1129
1197
|
try {
|
|
@@ -2709,6 +2777,9 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2709
2777
|
}
|
|
2710
2778
|
};
|
|
2711
2779
|
|
|
2780
|
+
// src/client.ts
|
|
2781
|
+
init_mockOverride();
|
|
2782
|
+
|
|
2712
2783
|
// src/openaiAgentSdk.ts
|
|
2713
2784
|
var BitfabOpenAIAgentHandler = class {
|
|
2714
2785
|
constructor(config) {
|
|
@@ -3524,6 +3595,12 @@ var Bitfab = class {
|
|
|
3524
3595
|
constructor(config) {
|
|
3525
3596
|
/** Gate the empty-key warning to fire at most once. */
|
|
3526
3597
|
this.apiKeyWarned = false;
|
|
3598
|
+
/**
|
|
3599
|
+
* Mock overrides registered via {@link Bitfab.registerMockOverride}, applied
|
|
3600
|
+
* to every `replay` on this client (after any per-call `mockOverride`). In
|
|
3601
|
+
* registration order; first matcher wins within this list.
|
|
3602
|
+
*/
|
|
3603
|
+
this.mockOverrides = [];
|
|
3527
3604
|
this.apiKeyConfig = config.apiKey;
|
|
3528
3605
|
this.serviceUrl = config.serviceUrl ?? DEFAULT_SERVICE_URL;
|
|
3529
3606
|
this.timeout = config.timeout ?? 12e4;
|
|
@@ -4108,7 +4185,7 @@ var Bitfab = class {
|
|
|
4108
4185
|
dbSnapshotUsage: {
|
|
4109
4186
|
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
4110
4187
|
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
4111
|
-
|
|
4188
|
+
originalTraceId: replayCtx.sourceBitfabTraceId,
|
|
4112
4189
|
accessed: replayCtx.dbSnapshotAccessed === true
|
|
4113
4190
|
}
|
|
4114
4191
|
}
|
|
@@ -4136,24 +4213,77 @@ var Bitfab = class {
|
|
|
4136
4213
|
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
4137
4214
|
const callIndex = counters.get(counterKey) ?? 0;
|
|
4138
4215
|
counters.set(counterKey, callIndex + 1);
|
|
4139
|
-
const
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
if (
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4216
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
4217
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
4218
|
+
const emitMock = (output) => {
|
|
4219
|
+
void sendSpan({ result: output, mocked: true });
|
|
4220
|
+
if (fnReturnsPromise) {
|
|
4221
|
+
return Promise.resolve(output);
|
|
4222
|
+
}
|
|
4223
|
+
return output;
|
|
4224
|
+
};
|
|
4225
|
+
const emitMockAsync = (pending) => {
|
|
4226
|
+
if (!fnReturnsPromise) {
|
|
4227
|
+
throw new BitfabError(
|
|
4228
|
+
`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.`
|
|
4229
|
+
);
|
|
4230
|
+
}
|
|
4231
|
+
return (async () => {
|
|
4232
|
+
const output = await pending;
|
|
4151
4233
|
void sendSpan({ result: output, mocked: true });
|
|
4152
|
-
if (fnReturnsPromise) {
|
|
4153
|
-
return Promise.resolve(output);
|
|
4154
|
-
}
|
|
4155
4234
|
return output;
|
|
4235
|
+
})();
|
|
4236
|
+
};
|
|
4237
|
+
const resolveRecordedOutput = () => {
|
|
4238
|
+
const hasInlineOutput = mockSpan?.output !== void 0 || mockSpan?.outputMeta !== void 0;
|
|
4239
|
+
if (!hasInlineOutput && replayCtxForMock.fetchSpanOutput && mockSpan?.externalSpanId) {
|
|
4240
|
+
return replayCtxForMock.fetchSpanOutput(mockSpan.externalSpanId);
|
|
4241
|
+
}
|
|
4242
|
+
if (!mockSpan) {
|
|
4243
|
+
return Promise.reject(
|
|
4244
|
+
new BitfabError(
|
|
4245
|
+
`No recorded span to source output for "${traceFunctionKey}".`
|
|
4246
|
+
)
|
|
4247
|
+
);
|
|
4156
4248
|
}
|
|
4249
|
+
let output = mockSpan.output;
|
|
4250
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
4251
|
+
output = deserializeValue({
|
|
4252
|
+
json: mockSpan.output,
|
|
4253
|
+
meta: mockSpan.outputMeta
|
|
4254
|
+
});
|
|
4255
|
+
}
|
|
4256
|
+
return output;
|
|
4257
|
+
};
|
|
4258
|
+
if (replayCtxForMock.mockOverrides?.length) {
|
|
4259
|
+
const nodeMeta = {
|
|
4260
|
+
traceFunctionKey,
|
|
4261
|
+
spanName: baseSpanParams.spanName,
|
|
4262
|
+
type: options.type ?? "custom",
|
|
4263
|
+
originalSpanId: mockSpan?.sourceSpanId
|
|
4264
|
+
};
|
|
4265
|
+
const override = replayCtxForMock.mockOverrides.find(
|
|
4266
|
+
(o) => o.match(nodeMeta)
|
|
4267
|
+
);
|
|
4268
|
+
if (override) {
|
|
4269
|
+
const injected = resolveMockValue(override.value, {
|
|
4270
|
+
node: nodeMeta,
|
|
4271
|
+
inputs: args,
|
|
4272
|
+
getOriginalOutput: () => Promise.resolve(resolveRecordedOutput())
|
|
4273
|
+
});
|
|
4274
|
+
if (injected instanceof Promise) {
|
|
4275
|
+
return emitMockAsync(injected);
|
|
4276
|
+
}
|
|
4277
|
+
return emitMock(injected);
|
|
4278
|
+
}
|
|
4279
|
+
}
|
|
4280
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
4281
|
+
if (shouldMock && mockSpan) {
|
|
4282
|
+
const recorded = resolveRecordedOutput();
|
|
4283
|
+
if (recorded instanceof Promise) {
|
|
4284
|
+
return emitMockAsync(recorded);
|
|
4285
|
+
}
|
|
4286
|
+
return emitMock(recorded);
|
|
4157
4287
|
}
|
|
4158
4288
|
}
|
|
4159
4289
|
const recordSpan = (result) => {
|
|
@@ -4359,8 +4489,11 @@ var Bitfab = class {
|
|
|
4359
4489
|
...params.dbSnapshotUsage.snapshotTimestamp && {
|
|
4360
4490
|
snapshot_timestamp: params.dbSnapshotUsage.snapshotTimestamp
|
|
4361
4491
|
},
|
|
4362
|
-
...params.dbSnapshotUsage.
|
|
4363
|
-
|
|
4492
|
+
...params.dbSnapshotUsage.originalTraceId && {
|
|
4493
|
+
original_trace_id: params.dbSnapshotUsage.originalTraceId,
|
|
4494
|
+
// Deprecated wire alias, kept so this SDK still reports usage
|
|
4495
|
+
// against servers that predate the rename.
|
|
4496
|
+
source_trace_id: params.dbSnapshotUsage.originalTraceId
|
|
4364
4497
|
},
|
|
4365
4498
|
accessed: params.dbSnapshotUsage.accessed
|
|
4366
4499
|
};
|
|
@@ -4433,26 +4566,14 @@ var Bitfab = class {
|
|
|
4433
4566
|
...params.mocked && { mocked: true }
|
|
4434
4567
|
});
|
|
4435
4568
|
}
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
* invocation records a trace tied to the test run. The plain-callable form
|
|
4445
|
-
* is how handler-instrumented workflows (LangGraph/LangChain, Claude Agent
|
|
4446
|
-
* SDK) replay - those record traces under a key with no `withSpan`-wrapped
|
|
4447
|
-
* root in the app.
|
|
4448
|
-
*
|
|
4449
|
-
* @param traceFunctionKey - The trace function key to replay
|
|
4450
|
-
* @param fn - The function to run recorded inputs through
|
|
4451
|
-
* @param options - Optional replay options. When `traceIds` is passed,
|
|
4452
|
-
* `limit` is ignored (with a warning): an explicit ID list already
|
|
4453
|
-
* determines how many traces replay.
|
|
4454
|
-
* @returns ReplayResult with items, testRunId, and testRunUrl
|
|
4455
|
-
*/
|
|
4569
|
+
registerMockOverride(overrideOrMatch, value) {
|
|
4570
|
+
const override = typeof overrideOrMatch === "function" ? { match: overrideOrMatch, value } : overrideOrMatch;
|
|
4571
|
+
this.mockOverrides.push(override);
|
|
4572
|
+
}
|
|
4573
|
+
/** Remove all overrides registered via {@link registerMockOverride}. */
|
|
4574
|
+
clearMockOverrides() {
|
|
4575
|
+
this.mockOverrides.length = 0;
|
|
4576
|
+
}
|
|
4456
4577
|
async replay(traceFunctionKey, fn, options) {
|
|
4457
4578
|
const wrappedKey = fn._bitfabTraceFunctionKey;
|
|
4458
4579
|
let replayFn = fn;
|
|
@@ -4473,7 +4594,8 @@ var Bitfab = class {
|
|
|
4473
4594
|
this.serviceUrl,
|
|
4474
4595
|
traceFunctionKey,
|
|
4475
4596
|
replayFn,
|
|
4476
|
-
options
|
|
4597
|
+
options,
|
|
4598
|
+
this.mockOverrides
|
|
4477
4599
|
);
|
|
4478
4600
|
}
|
|
4479
4601
|
};
|