@bitfab/sdk 0.28.11 → 0.29.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/dist/{chunk-ZBWTCBVQ.js → chunk-2M5AWVVQ.js} +96 -42
- package/dist/chunk-2M5AWVVQ.js.map +1 -0
- package/dist/{chunk-5E4BUIYA.js → chunk-V3XORTWI.js} +45 -8
- package/dist/chunk-V3XORTWI.js.map +1 -0
- package/dist/index.cjs +144 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +111 -2
- package/dist/index.d.ts +111 -2
- package/dist/index.js +2 -2
- package/dist/node.cjs +144 -46
- 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-HEGU3YU2.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-HEGU3YU2.js.map} +0 -0
|
@@ -6,14 +6,15 @@ import {
|
|
|
6
6
|
getReplayContext,
|
|
7
7
|
isAsyncStorageInitDone,
|
|
8
8
|
randomUuid,
|
|
9
|
+
resolveMockValue,
|
|
9
10
|
serializeValue,
|
|
10
11
|
toJsonSafe,
|
|
11
12
|
toJsonSafeReport,
|
|
12
13
|
warnOnce
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-V3XORTWI.js";
|
|
14
15
|
|
|
15
16
|
// src/version.generated.ts
|
|
16
|
-
var __version__ = "0.
|
|
17
|
+
var __version__ = "0.29.0";
|
|
17
18
|
|
|
18
19
|
// src/constants.ts
|
|
19
20
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
@@ -438,9 +439,14 @@ var HttpClient = class {
|
|
|
438
439
|
/**
|
|
439
440
|
* Fetch the span tree for a root span.
|
|
440
441
|
* Blocking GET request.
|
|
442
|
+
*
|
|
443
|
+
* Pass `includeOutputs: false` for a payload-free tree (structure +
|
|
444
|
+
* `externalSpanId` only), so recorded outputs are fetched lazily per mocked
|
|
445
|
+
* span instead of all up front. Omit it (default eager) for `mock: "all"`.
|
|
441
446
|
*/
|
|
442
|
-
async getSpanTree(externalSpanId) {
|
|
443
|
-
const
|
|
447
|
+
async getSpanTree(externalSpanId, options) {
|
|
448
|
+
const query = options?.includeOutputs === false ? "?includeOutputs=false" : "";
|
|
449
|
+
const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}${query}`;
|
|
444
450
|
const controller = new AbortController();
|
|
445
451
|
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
446
452
|
try {
|
|
@@ -2821,6 +2827,12 @@ var Bitfab = class {
|
|
|
2821
2827
|
constructor(config) {
|
|
2822
2828
|
/** Gate the empty-key warning to fire at most once. */
|
|
2823
2829
|
this.apiKeyWarned = false;
|
|
2830
|
+
/**
|
|
2831
|
+
* Mock overrides registered via {@link Bitfab.registerMockOverride}, applied
|
|
2832
|
+
* to every `replay` on this client (after any per-call `mockOverride`). In
|
|
2833
|
+
* registration order; first matcher wins within this list.
|
|
2834
|
+
*/
|
|
2835
|
+
this.mockOverrides = [];
|
|
2824
2836
|
this.apiKeyConfig = config.apiKey;
|
|
2825
2837
|
this.serviceUrl = config.serviceUrl ?? DEFAULT_SERVICE_URL;
|
|
2826
2838
|
this.timeout = config.timeout ?? 12e4;
|
|
@@ -3433,25 +3445,78 @@ var Bitfab = class {
|
|
|
3433
3445
|
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
3434
3446
|
const callIndex = counters.get(counterKey) ?? 0;
|
|
3435
3447
|
counters.set(counterKey, callIndex + 1);
|
|
3436
|
-
const
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
if (
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
3449
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
3450
|
+
const emitMock = (output) => {
|
|
3451
|
+
void sendSpan({ result: output, mocked: true });
|
|
3452
|
+
if (fnReturnsPromise) {
|
|
3453
|
+
return Promise.resolve(output);
|
|
3454
|
+
}
|
|
3455
|
+
return output;
|
|
3456
|
+
};
|
|
3457
|
+
const emitMockAsync = (pending) => {
|
|
3458
|
+
if (!fnReturnsPromise) {
|
|
3459
|
+
throw new BitfabError(
|
|
3460
|
+
`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.`
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
3463
|
+
return (async () => {
|
|
3464
|
+
const output = await pending;
|
|
3448
3465
|
void sendSpan({ result: output, mocked: true });
|
|
3449
|
-
if (fnReturnsPromise) {
|
|
3450
|
-
return Promise.resolve(output);
|
|
3451
|
-
}
|
|
3452
3466
|
return output;
|
|
3467
|
+
})();
|
|
3468
|
+
};
|
|
3469
|
+
const resolveRecordedOutput = () => {
|
|
3470
|
+
const hasInlineOutput = mockSpan?.output !== void 0 || mockSpan?.outputMeta !== void 0;
|
|
3471
|
+
if (!hasInlineOutput && replayCtxForMock.fetchSpanOutput && mockSpan?.externalSpanId) {
|
|
3472
|
+
return replayCtxForMock.fetchSpanOutput(mockSpan.externalSpanId);
|
|
3473
|
+
}
|
|
3474
|
+
if (!mockSpan) {
|
|
3475
|
+
return Promise.reject(
|
|
3476
|
+
new BitfabError(
|
|
3477
|
+
`No recorded span to source output for "${traceFunctionKey}".`
|
|
3478
|
+
)
|
|
3479
|
+
);
|
|
3480
|
+
}
|
|
3481
|
+
let output = mockSpan.output;
|
|
3482
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
3483
|
+
output = deserializeValue({
|
|
3484
|
+
json: mockSpan.output,
|
|
3485
|
+
meta: mockSpan.outputMeta
|
|
3486
|
+
});
|
|
3487
|
+
}
|
|
3488
|
+
return output;
|
|
3489
|
+
};
|
|
3490
|
+
if (replayCtxForMock.mockOverrides?.length) {
|
|
3491
|
+
const nodeMeta = {
|
|
3492
|
+
traceFunctionKey,
|
|
3493
|
+
spanName: baseSpanParams.spanName,
|
|
3494
|
+
type: options.type ?? "custom",
|
|
3495
|
+
originalSpanId: mockSpan?.sourceSpanId
|
|
3496
|
+
};
|
|
3497
|
+
const override = replayCtxForMock.mockOverrides.find(
|
|
3498
|
+
(o) => o.match(nodeMeta)
|
|
3499
|
+
);
|
|
3500
|
+
if (override) {
|
|
3501
|
+
const injected = resolveMockValue(override.value, {
|
|
3502
|
+
node: nodeMeta,
|
|
3503
|
+
inputs: args,
|
|
3504
|
+
getOriginalOutput: () => Promise.resolve(resolveRecordedOutput())
|
|
3505
|
+
});
|
|
3506
|
+
if (injected instanceof Promise) {
|
|
3507
|
+
return emitMockAsync(injected);
|
|
3508
|
+
}
|
|
3509
|
+
return emitMock(injected);
|
|
3453
3510
|
}
|
|
3454
3511
|
}
|
|
3512
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
3513
|
+
if (shouldMock && mockSpan) {
|
|
3514
|
+
const recorded = resolveRecordedOutput();
|
|
3515
|
+
if (recorded instanceof Promise) {
|
|
3516
|
+
return emitMockAsync(recorded);
|
|
3517
|
+
}
|
|
3518
|
+
return emitMock(recorded);
|
|
3519
|
+
}
|
|
3455
3520
|
}
|
|
3456
3521
|
const recordSpan = (result) => {
|
|
3457
3522
|
if (options.finalize) {
|
|
@@ -3730,26 +3795,14 @@ var Bitfab = class {
|
|
|
3730
3795
|
...params.mocked && { mocked: true }
|
|
3731
3796
|
});
|
|
3732
3797
|
}
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
* invocation records a trace tied to the test run. The plain-callable form
|
|
3742
|
-
* is how handler-instrumented workflows (LangGraph/LangChain, Claude Agent
|
|
3743
|
-
* SDK) replay - those record traces under a key with no `withSpan`-wrapped
|
|
3744
|
-
* root in the app.
|
|
3745
|
-
*
|
|
3746
|
-
* @param traceFunctionKey - The trace function key to replay
|
|
3747
|
-
* @param fn - The function to run recorded inputs through
|
|
3748
|
-
* @param options - Optional replay options. When `traceIds` is passed,
|
|
3749
|
-
* `limit` is ignored (with a warning): an explicit ID list already
|
|
3750
|
-
* determines how many traces replay.
|
|
3751
|
-
* @returns ReplayResult with items, testRunId, and testRunUrl
|
|
3752
|
-
*/
|
|
3798
|
+
registerMockOverride(overrideOrMatch, value) {
|
|
3799
|
+
const override = typeof overrideOrMatch === "function" ? { match: overrideOrMatch, value } : overrideOrMatch;
|
|
3800
|
+
this.mockOverrides.push(override);
|
|
3801
|
+
}
|
|
3802
|
+
/** Remove all overrides registered via {@link registerMockOverride}. */
|
|
3803
|
+
clearMockOverrides() {
|
|
3804
|
+
this.mockOverrides.length = 0;
|
|
3805
|
+
}
|
|
3753
3806
|
async replay(traceFunctionKey, fn, options) {
|
|
3754
3807
|
const wrappedKey = fn._bitfabTraceFunctionKey;
|
|
3755
3808
|
let replayFn = fn;
|
|
@@ -3764,13 +3817,14 @@ var Bitfab = class {
|
|
|
3764
3817
|
`Function is wrapped with trace function key '${wrappedKey}' but replay was called with '${traceFunctionKey}'. Pass matching keys, or pass the unwrapped function to replay it under the explicit key.`
|
|
3765
3818
|
);
|
|
3766
3819
|
}
|
|
3767
|
-
const { replay: doReplay } = await import("./replay-
|
|
3820
|
+
const { replay: doReplay } = await import("./replay-HEGU3YU2.js");
|
|
3768
3821
|
return doReplay(
|
|
3769
3822
|
this.httpClient,
|
|
3770
3823
|
this.serviceUrl,
|
|
3771
3824
|
traceFunctionKey,
|
|
3772
3825
|
replayFn,
|
|
3773
|
-
options
|
|
3826
|
+
options,
|
|
3827
|
+
this.mockOverrides
|
|
3774
3828
|
);
|
|
3775
3829
|
}
|
|
3776
3830
|
};
|
|
@@ -3989,4 +4043,4 @@ export {
|
|
|
3989
4043
|
BitfabFunction,
|
|
3990
4044
|
finalizers
|
|
3991
4045
|
};
|
|
3992
|
-
//# sourceMappingURL=chunk-
|
|
4046
|
+
//# sourceMappingURL=chunk-2M5AWVVQ.js.map
|