@bitfab/sdk 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
|
@@ -282,6 +282,22 @@ var init_asyncStorage = __esm({
|
|
|
282
282
|
}
|
|
283
283
|
});
|
|
284
284
|
|
|
285
|
+
// src/mockOverride.ts
|
|
286
|
+
function resolveMockValue(value, ctx) {
|
|
287
|
+
return typeof value === "function" ? value(ctx) : value;
|
|
288
|
+
}
|
|
289
|
+
function normalizeMockOverrides(mockOverride) {
|
|
290
|
+
if (mockOverride === void 0) {
|
|
291
|
+
return [];
|
|
292
|
+
}
|
|
293
|
+
return Array.isArray(mockOverride) ? mockOverride : [mockOverride];
|
|
294
|
+
}
|
|
295
|
+
var init_mockOverride = __esm({
|
|
296
|
+
"src/mockOverride.ts"() {
|
|
297
|
+
"use strict";
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
285
301
|
// src/replayContext.ts
|
|
286
302
|
function getReplayContext() {
|
|
287
303
|
return replayContextStorage?.getStore() ?? null;
|
|
@@ -357,6 +373,7 @@ function buildMockTree(rootNode) {
|
|
|
357
373
|
counters.set(counterKey, index + 1);
|
|
358
374
|
spans.set(`${counterKey}:${index}`, {
|
|
359
375
|
sourceSpanId: node.sourceSpanId,
|
|
376
|
+
externalSpanId: node.externalSpanId,
|
|
360
377
|
output: node.output,
|
|
361
378
|
outputMeta: node.outputMeta
|
|
362
379
|
});
|
|
@@ -370,57 +387,79 @@ function buildMockTree(rootNode) {
|
|
|
370
387
|
}
|
|
371
388
|
return { spans };
|
|
372
389
|
}
|
|
373
|
-
async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, environment, adaptInputs) {
|
|
390
|
+
async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, environment, adaptInputs) {
|
|
374
391
|
const lease = environment ? serverItem.dbBranchLease : void 0;
|
|
375
392
|
let inputs = [];
|
|
376
393
|
let originalOutput;
|
|
377
394
|
let result;
|
|
378
395
|
let error = null;
|
|
379
|
-
const replayedTraceId = randomUuid();
|
|
380
396
|
const pendingPersistence = [];
|
|
397
|
+
const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
|
|
398
|
+
const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
|
|
381
399
|
try {
|
|
382
|
-
const span = await httpClient.getExternalSpan(
|
|
400
|
+
const span = await httpClient.getExternalSpan(originalSpanId);
|
|
383
401
|
const spanData = span.rawData?.span_data ?? {};
|
|
384
402
|
inputs = deserializeInputs(spanData);
|
|
385
403
|
originalOutput = deserializeOutput(spanData);
|
|
386
404
|
if (adaptInputs) {
|
|
387
405
|
inputs = adaptInputs(inputs, {
|
|
388
|
-
|
|
389
|
-
|
|
406
|
+
originalTraceId,
|
|
407
|
+
originalSpanId,
|
|
408
|
+
// Deprecated aliases for originalTraceId/originalSpanId.
|
|
409
|
+
sourceTraceId: originalTraceId,
|
|
410
|
+
sourceSpanId: originalSpanId
|
|
390
411
|
});
|
|
391
412
|
}
|
|
413
|
+
const hasOverrides = resolvedOverrides.length > 0;
|
|
414
|
+
const needTree = mockStrategy === "all" || mockStrategy === "marked" || hasOverrides;
|
|
415
|
+
const includeOutputs = mockStrategy === "all";
|
|
392
416
|
let mockTree;
|
|
393
|
-
if (
|
|
417
|
+
if (needTree) {
|
|
394
418
|
try {
|
|
395
|
-
const treeResponse = await httpClient.getSpanTree(
|
|
396
|
-
|
|
397
|
-
);
|
|
419
|
+
const treeResponse = await httpClient.getSpanTree(originalSpanId, {
|
|
420
|
+
includeOutputs
|
|
421
|
+
});
|
|
398
422
|
if (treeResponse.root) {
|
|
399
423
|
mockTree = buildMockTree(treeResponse.root);
|
|
400
|
-
} else if (mockStrategy === "all") {
|
|
424
|
+
} else if (mockStrategy === "all" || hasOverrides) {
|
|
401
425
|
throw new BitfabError(
|
|
402
|
-
`Replay mock strategy "
|
|
426
|
+
`Replay mock strategy "${mockStrategy}"${hasOverrides ? " with overrides" : ""} requires a span tree root for original span ${originalSpanId}.`
|
|
403
427
|
);
|
|
404
428
|
} else {
|
|
405
429
|
mockTree = void 0;
|
|
406
430
|
}
|
|
407
431
|
} catch (e) {
|
|
408
|
-
if (mockStrategy === "all") {
|
|
432
|
+
if (mockStrategy === "all" || hasOverrides) {
|
|
409
433
|
throw e;
|
|
410
434
|
}
|
|
411
435
|
mockTree = void 0;
|
|
412
436
|
}
|
|
413
437
|
}
|
|
438
|
+
const outputCache = /* @__PURE__ */ new Map();
|
|
439
|
+
const fetchSpanOutput = mockTree && !includeOutputs ? (externalSpanId) => {
|
|
440
|
+
let pending = outputCache.get(externalSpanId);
|
|
441
|
+
if (!pending) {
|
|
442
|
+
pending = httpClient.getExternalSpan(externalSpanId).then(
|
|
443
|
+
(s) => deserializeOutput(
|
|
444
|
+
s.rawData?.span_data ?? {}
|
|
445
|
+
)
|
|
446
|
+
);
|
|
447
|
+
outputCache.set(externalSpanId, pending);
|
|
448
|
+
}
|
|
449
|
+
return pending;
|
|
450
|
+
} : void 0;
|
|
414
451
|
const maybePromise = runWithReplayContext(
|
|
415
452
|
{
|
|
416
453
|
testRunId,
|
|
417
454
|
traceId: replayedTraceId,
|
|
418
455
|
inputSourceSpanId: span.id,
|
|
419
456
|
inputSourceTraceId: span.externalTraceId,
|
|
420
|
-
sourceBitfabTraceId:
|
|
457
|
+
sourceBitfabTraceId: originalTraceId,
|
|
421
458
|
mockTree,
|
|
422
459
|
callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
|
|
423
460
|
mockStrategy,
|
|
461
|
+
mockOverrides: hasOverrides ? resolvedOverrides : void 0,
|
|
462
|
+
fetchSpanOutput,
|
|
424
463
|
dbBranchLease: lease,
|
|
425
464
|
pendingPersistence
|
|
426
465
|
},
|
|
@@ -445,7 +484,15 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
445
484
|
}
|
|
446
485
|
}
|
|
447
486
|
return {
|
|
448
|
-
|
|
487
|
+
// Written in by replay() from the complete-replay response once the server
|
|
488
|
+
// has minted this replay trace's row. Null until then: the client-side
|
|
489
|
+
// correlation id (replayedTraceId) is never surfaced as the item's traceId.
|
|
490
|
+
traceId: null,
|
|
491
|
+
originalTraceId,
|
|
492
|
+
originalSpanId,
|
|
493
|
+
// Deprecated aliases for originalTraceId/originalSpanId.
|
|
494
|
+
sourceTraceId: originalTraceId,
|
|
495
|
+
sourceSpanId: originalSpanId,
|
|
449
496
|
input: inputs,
|
|
450
497
|
result,
|
|
451
498
|
originalOutput,
|
|
@@ -477,7 +524,7 @@ async function mapWithConcurrency(tasks, maxConcurrency, onSettled) {
|
|
|
477
524
|
await Promise.all(workers);
|
|
478
525
|
return results;
|
|
479
526
|
}
|
|
480
|
-
async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
527
|
+
async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, registeredOverrides = []) {
|
|
481
528
|
if (options?.traceIds !== void 0) {
|
|
482
529
|
if (options.traceIds.length === 0) {
|
|
483
530
|
throw new BitfabError("traceIds must contain at least one trace ID.");
|
|
@@ -517,13 +564,20 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
517
564
|
);
|
|
518
565
|
const mockStrategy = options?.mock ?? "marked";
|
|
519
566
|
const maxConcurrency = options?.maxConcurrency ?? 10;
|
|
567
|
+
const resolvedOverrides = [
|
|
568
|
+
...normalizeMockOverrides(options?.mockOverride),
|
|
569
|
+
...registeredOverrides
|
|
570
|
+
];
|
|
571
|
+
const replayedTraceIds = serverItems.map(() => randomUuid());
|
|
520
572
|
const tasks = serverItems.map(
|
|
521
|
-
(serverItem) => () => processItem(
|
|
573
|
+
(serverItem, index) => () => processItem(
|
|
522
574
|
httpClient,
|
|
523
575
|
serverItem,
|
|
524
576
|
fn,
|
|
525
577
|
testRunId,
|
|
526
578
|
mockStrategy,
|
|
579
|
+
resolvedOverrides,
|
|
580
|
+
replayedTraceIds[index],
|
|
527
581
|
options?.environment,
|
|
528
582
|
options?.adaptInputs
|
|
529
583
|
)
|
|
@@ -550,11 +604,17 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
550
604
|
succeeded,
|
|
551
605
|
errored,
|
|
552
606
|
item: {
|
|
553
|
-
//
|
|
554
|
-
//
|
|
555
|
-
//
|
|
556
|
-
|
|
557
|
-
|
|
607
|
+
// The server replay trace id isn't known until completeReplay
|
|
608
|
+
// runs (below), so it can't be reported mid-run and we never
|
|
609
|
+
// emit the client-side placeholder. originalTraceId (the
|
|
610
|
+
// historical trace) is known now and is what a UI keys on to
|
|
611
|
+
// identify what just settled.
|
|
612
|
+
traceId: null,
|
|
613
|
+
originalTraceId: item.originalTraceId ?? null,
|
|
614
|
+
originalSpanId: item.originalSpanId ?? null,
|
|
615
|
+
// Deprecated aliases for originalTraceId/originalSpanId.
|
|
616
|
+
sourceTraceId: item.originalTraceId ?? null,
|
|
617
|
+
sourceSpanId: item.originalSpanId ?? null,
|
|
558
618
|
input: item.input,
|
|
559
619
|
result: item.result,
|
|
560
620
|
originalOutput: item.originalOutput,
|
|
@@ -572,56 +632,58 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
572
632
|
const completeResult = await httpClient.completeReplay(testRunId);
|
|
573
633
|
const serverTraceIds = completeResult.traceIds;
|
|
574
634
|
const replayTokens = completeResult.tokens;
|
|
575
|
-
if (serverTraceIds
|
|
576
|
-
try {
|
|
577
|
-
console.warn(
|
|
578
|
-
"Bitfab: server did not return replay trace IDs; item.traceId will be null (server upgrade required for verdict persistence)"
|
|
579
|
-
);
|
|
580
|
-
} catch {
|
|
581
|
-
}
|
|
582
|
-
for (const item of resultItems) {
|
|
583
|
-
item.traceId = null;
|
|
584
|
-
}
|
|
585
|
-
} else {
|
|
635
|
+
if (serverTraceIds !== void 0) {
|
|
586
636
|
const missing = [];
|
|
587
637
|
let completedCount = 0;
|
|
588
|
-
for (
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
if (mapped !== void 0) {
|
|
598
|
-
item.tokens = replayTokens?.[mapped] ?? null;
|
|
638
|
+
for (let index = 0; index < resultItems.length; index += 1) {
|
|
639
|
+
const item = resultItems[index];
|
|
640
|
+
const localId = replayedTraceIds[index];
|
|
641
|
+
const mapped = localId ? serverTraceIds[localId] : void 0;
|
|
642
|
+
item.traceId = mapped ?? null;
|
|
643
|
+
if (item.error === null) {
|
|
644
|
+
completedCount += 1;
|
|
645
|
+
if (mapped === void 0) {
|
|
646
|
+
missing.push(localId ?? item.originalTraceId);
|
|
599
647
|
}
|
|
600
|
-
|
|
648
|
+
}
|
|
649
|
+
if (mapped !== void 0) {
|
|
650
|
+
item.tokens = replayTokens?.[mapped] ?? null;
|
|
601
651
|
}
|
|
602
652
|
}
|
|
603
|
-
if (missing.length
|
|
653
|
+
if (completedCount > 0 && missing.length === completedCount) {
|
|
604
654
|
const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
655
|
+
throw new BitfabError(
|
|
656
|
+
`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.`
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
if (missing.length > 0) {
|
|
610
660
|
try {
|
|
611
661
|
console.error(
|
|
612
|
-
`Bitfab: server has no persisted trace for ${missing.length} of ${completedCount} completed replay item(s) (testRunId ${testRunId})
|
|
662
|
+
`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.`
|
|
613
663
|
);
|
|
614
664
|
} catch {
|
|
615
665
|
}
|
|
616
666
|
}
|
|
617
667
|
}
|
|
618
|
-
const
|
|
668
|
+
const result = {
|
|
619
669
|
items: resultItems,
|
|
620
670
|
testRunId,
|
|
621
671
|
testRunUrl: `${serviceUrl}${testRunUrl}`
|
|
622
672
|
};
|
|
623
|
-
await writeReplayResultFile(
|
|
624
|
-
|
|
673
|
+
await writeReplayResultFile(result);
|
|
674
|
+
try {
|
|
675
|
+
options?.onProgress?.({
|
|
676
|
+
type: "complete",
|
|
677
|
+
testRunId,
|
|
678
|
+
completed: total,
|
|
679
|
+
total,
|
|
680
|
+
succeeded,
|
|
681
|
+
errored,
|
|
682
|
+
result
|
|
683
|
+
});
|
|
684
|
+
} catch {
|
|
685
|
+
}
|
|
686
|
+
return result;
|
|
625
687
|
}
|
|
626
688
|
async function writeReplayResultFile(result) {
|
|
627
689
|
const resultPath = typeof process !== "undefined" ? process.env?.BITFAB_REPLAY_RESULT_PATH : void 0;
|
|
@@ -650,6 +712,7 @@ var init_replay = __esm({
|
|
|
650
712
|
"src/replay.ts"() {
|
|
651
713
|
"use strict";
|
|
652
714
|
init_errors();
|
|
715
|
+
init_mockOverride();
|
|
653
716
|
init_randomUuid();
|
|
654
717
|
init_replayContext();
|
|
655
718
|
init_serialize();
|
|
@@ -683,7 +746,7 @@ __export(index_exports, {
|
|
|
683
746
|
module.exports = __toCommonJS(index_exports);
|
|
684
747
|
|
|
685
748
|
// src/version.generated.ts
|
|
686
|
-
var __version__ = "0.
|
|
749
|
+
var __version__ = "0.29.1";
|
|
687
750
|
|
|
688
751
|
// src/constants.ts
|
|
689
752
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
@@ -1112,9 +1175,14 @@ var HttpClient = class {
|
|
|
1112
1175
|
/**
|
|
1113
1176
|
* Fetch the span tree for a root span.
|
|
1114
1177
|
* Blocking GET request.
|
|
1178
|
+
*
|
|
1179
|
+
* Pass `includeOutputs: false` for a payload-free tree (structure +
|
|
1180
|
+
* `externalSpanId` only), so recorded outputs are fetched lazily per mocked
|
|
1181
|
+
* span instead of all up front. Omit it (default eager) for `mock: "all"`.
|
|
1115
1182
|
*/
|
|
1116
|
-
async getSpanTree(externalSpanId) {
|
|
1117
|
-
const
|
|
1183
|
+
async getSpanTree(externalSpanId, options) {
|
|
1184
|
+
const query = options?.includeOutputs === false ? "?includeOutputs=false" : "";
|
|
1185
|
+
const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}${query}`;
|
|
1118
1186
|
const controller = new AbortController();
|
|
1119
1187
|
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
1120
1188
|
try {
|
|
@@ -2700,6 +2768,9 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2700
2768
|
}
|
|
2701
2769
|
};
|
|
2702
2770
|
|
|
2771
|
+
// src/client.ts
|
|
2772
|
+
init_mockOverride();
|
|
2773
|
+
|
|
2703
2774
|
// src/openaiAgentSdk.ts
|
|
2704
2775
|
var BitfabOpenAIAgentHandler = class {
|
|
2705
2776
|
constructor(config) {
|
|
@@ -3515,6 +3586,12 @@ var Bitfab = class {
|
|
|
3515
3586
|
constructor(config) {
|
|
3516
3587
|
/** Gate the empty-key warning to fire at most once. */
|
|
3517
3588
|
this.apiKeyWarned = false;
|
|
3589
|
+
/**
|
|
3590
|
+
* Mock overrides registered via {@link Bitfab.registerMockOverride}, applied
|
|
3591
|
+
* to every `replay` on this client (after any per-call `mockOverride`). In
|
|
3592
|
+
* registration order; first matcher wins within this list.
|
|
3593
|
+
*/
|
|
3594
|
+
this.mockOverrides = [];
|
|
3518
3595
|
this.apiKeyConfig = config.apiKey;
|
|
3519
3596
|
this.serviceUrl = config.serviceUrl ?? DEFAULT_SERVICE_URL;
|
|
3520
3597
|
this.timeout = config.timeout ?? 12e4;
|
|
@@ -4099,7 +4176,7 @@ var Bitfab = class {
|
|
|
4099
4176
|
dbSnapshotUsage: {
|
|
4100
4177
|
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
4101
4178
|
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
4102
|
-
|
|
4179
|
+
originalTraceId: replayCtx.sourceBitfabTraceId,
|
|
4103
4180
|
accessed: replayCtx.dbSnapshotAccessed === true
|
|
4104
4181
|
}
|
|
4105
4182
|
}
|
|
@@ -4127,24 +4204,77 @@ var Bitfab = class {
|
|
|
4127
4204
|
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
4128
4205
|
const callIndex = counters.get(counterKey) ?? 0;
|
|
4129
4206
|
counters.set(counterKey, callIndex + 1);
|
|
4130
|
-
const
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
if (
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4207
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
4208
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
4209
|
+
const emitMock = (output) => {
|
|
4210
|
+
void sendSpan({ result: output, mocked: true });
|
|
4211
|
+
if (fnReturnsPromise) {
|
|
4212
|
+
return Promise.resolve(output);
|
|
4213
|
+
}
|
|
4214
|
+
return output;
|
|
4215
|
+
};
|
|
4216
|
+
const emitMockAsync = (pending) => {
|
|
4217
|
+
if (!fnReturnsPromise) {
|
|
4218
|
+
throw new BitfabError(
|
|
4219
|
+
`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.`
|
|
4220
|
+
);
|
|
4221
|
+
}
|
|
4222
|
+
return (async () => {
|
|
4223
|
+
const output = await pending;
|
|
4142
4224
|
void sendSpan({ result: output, mocked: true });
|
|
4143
|
-
if (fnReturnsPromise) {
|
|
4144
|
-
return Promise.resolve(output);
|
|
4145
|
-
}
|
|
4146
4225
|
return output;
|
|
4226
|
+
})();
|
|
4227
|
+
};
|
|
4228
|
+
const resolveRecordedOutput = () => {
|
|
4229
|
+
const hasInlineOutput = mockSpan?.output !== void 0 || mockSpan?.outputMeta !== void 0;
|
|
4230
|
+
if (!hasInlineOutput && replayCtxForMock.fetchSpanOutput && mockSpan?.externalSpanId) {
|
|
4231
|
+
return replayCtxForMock.fetchSpanOutput(mockSpan.externalSpanId);
|
|
4232
|
+
}
|
|
4233
|
+
if (!mockSpan) {
|
|
4234
|
+
return Promise.reject(
|
|
4235
|
+
new BitfabError(
|
|
4236
|
+
`No recorded span to source output for "${traceFunctionKey}".`
|
|
4237
|
+
)
|
|
4238
|
+
);
|
|
4147
4239
|
}
|
|
4240
|
+
let output = mockSpan.output;
|
|
4241
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
4242
|
+
output = deserializeValue({
|
|
4243
|
+
json: mockSpan.output,
|
|
4244
|
+
meta: mockSpan.outputMeta
|
|
4245
|
+
});
|
|
4246
|
+
}
|
|
4247
|
+
return output;
|
|
4248
|
+
};
|
|
4249
|
+
if (replayCtxForMock.mockOverrides?.length) {
|
|
4250
|
+
const nodeMeta = {
|
|
4251
|
+
traceFunctionKey,
|
|
4252
|
+
spanName: baseSpanParams.spanName,
|
|
4253
|
+
type: options.type ?? "custom",
|
|
4254
|
+
originalSpanId: mockSpan?.sourceSpanId
|
|
4255
|
+
};
|
|
4256
|
+
const override = replayCtxForMock.mockOverrides.find(
|
|
4257
|
+
(o) => o.match(nodeMeta)
|
|
4258
|
+
);
|
|
4259
|
+
if (override) {
|
|
4260
|
+
const injected = resolveMockValue(override.value, {
|
|
4261
|
+
node: nodeMeta,
|
|
4262
|
+
inputs: args,
|
|
4263
|
+
getOriginalOutput: () => Promise.resolve(resolveRecordedOutput())
|
|
4264
|
+
});
|
|
4265
|
+
if (injected instanceof Promise) {
|
|
4266
|
+
return emitMockAsync(injected);
|
|
4267
|
+
}
|
|
4268
|
+
return emitMock(injected);
|
|
4269
|
+
}
|
|
4270
|
+
}
|
|
4271
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
4272
|
+
if (shouldMock && mockSpan) {
|
|
4273
|
+
const recorded = resolveRecordedOutput();
|
|
4274
|
+
if (recorded instanceof Promise) {
|
|
4275
|
+
return emitMockAsync(recorded);
|
|
4276
|
+
}
|
|
4277
|
+
return emitMock(recorded);
|
|
4148
4278
|
}
|
|
4149
4279
|
}
|
|
4150
4280
|
const recordSpan = (result) => {
|
|
@@ -4350,8 +4480,11 @@ var Bitfab = class {
|
|
|
4350
4480
|
...params.dbSnapshotUsage.snapshotTimestamp && {
|
|
4351
4481
|
snapshot_timestamp: params.dbSnapshotUsage.snapshotTimestamp
|
|
4352
4482
|
},
|
|
4353
|
-
...params.dbSnapshotUsage.
|
|
4354
|
-
|
|
4483
|
+
...params.dbSnapshotUsage.originalTraceId && {
|
|
4484
|
+
original_trace_id: params.dbSnapshotUsage.originalTraceId,
|
|
4485
|
+
// Deprecated wire alias, kept so this SDK still reports usage
|
|
4486
|
+
// against servers that predate the rename.
|
|
4487
|
+
source_trace_id: params.dbSnapshotUsage.originalTraceId
|
|
4355
4488
|
},
|
|
4356
4489
|
accessed: params.dbSnapshotUsage.accessed
|
|
4357
4490
|
};
|
|
@@ -4424,26 +4557,14 @@ var Bitfab = class {
|
|
|
4424
4557
|
...params.mocked && { mocked: true }
|
|
4425
4558
|
});
|
|
4426
4559
|
}
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
* invocation records a trace tied to the test run. The plain-callable form
|
|
4436
|
-
* is how handler-instrumented workflows (LangGraph/LangChain, Claude Agent
|
|
4437
|
-
* SDK) replay - those record traces under a key with no `withSpan`-wrapped
|
|
4438
|
-
* root in the app.
|
|
4439
|
-
*
|
|
4440
|
-
* @param traceFunctionKey - The trace function key to replay
|
|
4441
|
-
* @param fn - The function to run recorded inputs through
|
|
4442
|
-
* @param options - Optional replay options. When `traceIds` is passed,
|
|
4443
|
-
* `limit` is ignored (with a warning): an explicit ID list already
|
|
4444
|
-
* determines how many traces replay.
|
|
4445
|
-
* @returns ReplayResult with items, testRunId, and testRunUrl
|
|
4446
|
-
*/
|
|
4560
|
+
registerMockOverride(overrideOrMatch, value) {
|
|
4561
|
+
const override = typeof overrideOrMatch === "function" ? { match: overrideOrMatch, value } : overrideOrMatch;
|
|
4562
|
+
this.mockOverrides.push(override);
|
|
4563
|
+
}
|
|
4564
|
+
/** Remove all overrides registered via {@link registerMockOverride}. */
|
|
4565
|
+
clearMockOverrides() {
|
|
4566
|
+
this.mockOverrides.length = 0;
|
|
4567
|
+
}
|
|
4447
4568
|
async replay(traceFunctionKey, fn, options) {
|
|
4448
4569
|
const wrappedKey = fn._bitfabTraceFunctionKey;
|
|
4449
4570
|
let replayFn = fn;
|
|
@@ -4464,7 +4585,8 @@ var Bitfab = class {
|
|
|
4464
4585
|
this.serviceUrl,
|
|
4465
4586
|
traceFunctionKey,
|
|
4466
4587
|
replayFn,
|
|
4467
|
-
options
|
|
4588
|
+
options,
|
|
4589
|
+
this.mockOverrides
|
|
4468
4590
|
);
|
|
4469
4591
|
}
|
|
4470
4592
|
};
|