@bitfab/sdk 0.28.10 → 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-SK4TNXRC.js → chunk-2M5AWVVQ.js} +230 -72
- 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 +279 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +153 -9
- package/dist/index.d.ts +153 -9
- package/dist/index.js +2 -2
- package/dist/node.cjs +279 -76
- 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-SK4TNXRC.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";
|
|
@@ -249,6 +250,50 @@ var HttpClient = class {
|
|
|
249
250
|
async lookupFunction(name) {
|
|
250
251
|
return this.request("/api/sdk/functions/lookup", { name });
|
|
251
252
|
}
|
|
253
|
+
async getTraceSpan(traceId, lookup) {
|
|
254
|
+
const searchParams = new URLSearchParams();
|
|
255
|
+
if (lookup.id !== void 0) {
|
|
256
|
+
searchParams.set("id", lookup.id);
|
|
257
|
+
} else {
|
|
258
|
+
searchParams.set("name", lookup.name);
|
|
259
|
+
searchParams.set("occurrence", String(lookup.occurrence ?? "last"));
|
|
260
|
+
}
|
|
261
|
+
const endpoint = `/api/sdk/traces/${encodeURIComponent(traceId)}/span?${searchParams.toString()}`;
|
|
262
|
+
const response = await this.get(endpoint);
|
|
263
|
+
return response.span;
|
|
264
|
+
}
|
|
265
|
+
async get(endpoint) {
|
|
266
|
+
const url = `${this.serviceUrl}${endpoint}`;
|
|
267
|
+
const controller = new AbortController();
|
|
268
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
269
|
+
try {
|
|
270
|
+
const response = await fetch(url, {
|
|
271
|
+
method: "GET",
|
|
272
|
+
headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
|
|
273
|
+
signal: controller.signal
|
|
274
|
+
});
|
|
275
|
+
if (!response.ok) {
|
|
276
|
+
const errorText = await response.text();
|
|
277
|
+
throw new BitfabError(
|
|
278
|
+
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
return await response.json();
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (error instanceof BitfabError) {
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
if (error instanceof Error) {
|
|
287
|
+
if (error.name === "AbortError") {
|
|
288
|
+
throw new BitfabError(`Request timed out after ${this.timeout}ms`);
|
|
289
|
+
}
|
|
290
|
+
throw new BitfabError(error.message);
|
|
291
|
+
}
|
|
292
|
+
throw new BitfabError("Unknown error occurred");
|
|
293
|
+
} finally {
|
|
294
|
+
clearTimeout(timeoutId);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
252
297
|
/**
|
|
253
298
|
* Send an internal trace (from BAML execution).
|
|
254
299
|
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
@@ -305,12 +350,12 @@ var HttpClient = class {
|
|
|
305
350
|
});
|
|
306
351
|
}
|
|
307
352
|
/**
|
|
308
|
-
* Partial update of an existing
|
|
353
|
+
* Partial update of an existing trace identified by its Bitfab trace ID.
|
|
309
354
|
* Used by the detached `client.getTrace(id)` handle. Fire-and-forget;
|
|
310
355
|
* returns a tracked promise that callers may optionally await.
|
|
311
356
|
*/
|
|
312
|
-
patchTrace(
|
|
313
|
-
const endpoint = `/api/sdk/
|
|
357
|
+
patchTrace(traceId, payload) {
|
|
358
|
+
const endpoint = `/api/sdk/traces/${encodeURIComponent(traceId)}`;
|
|
314
359
|
return awaitOnExit(
|
|
315
360
|
this.request(endpoint, payload, { method: "PATCH" })
|
|
316
361
|
).catch((error) => {
|
|
@@ -394,9 +439,14 @@ var HttpClient = class {
|
|
|
394
439
|
/**
|
|
395
440
|
* Fetch the span tree for a root span.
|
|
396
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"`.
|
|
397
446
|
*/
|
|
398
|
-
async getSpanTree(externalSpanId) {
|
|
399
|
-
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}`;
|
|
400
450
|
const controller = new AbortController();
|
|
401
451
|
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
402
452
|
try {
|
|
@@ -644,6 +694,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
644
694
|
const traceId = this.ensureTrace();
|
|
645
695
|
const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
|
|
646
696
|
const spanInfo = {
|
|
697
|
+
id: randomUuid(),
|
|
647
698
|
spanId,
|
|
648
699
|
traceId,
|
|
649
700
|
parentId: parentId ?? null,
|
|
@@ -707,6 +758,8 @@ var BitfabClaudeAgentHandler = class {
|
|
|
707
758
|
rawSpan.parent_id = spanInfo.parentId;
|
|
708
759
|
}
|
|
709
760
|
const payload = {
|
|
761
|
+
id: spanInfo.id,
|
|
762
|
+
traceId: spanInfo.traceId,
|
|
710
763
|
type: "sdk-function",
|
|
711
764
|
source: "typescript-sdk-claude-agent-sdk",
|
|
712
765
|
traceFunctionKey: this.traceFunctionKey,
|
|
@@ -736,6 +789,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
736
789
|
externalTrace.metadata = metadata;
|
|
737
790
|
}
|
|
738
791
|
const traceData = {
|
|
792
|
+
id: traceId,
|
|
739
793
|
type: "sdk-function",
|
|
740
794
|
source: "typescript-sdk-claude-agent-sdk",
|
|
741
795
|
traceFunctionKey: this.traceFunctionKey,
|
|
@@ -1004,6 +1058,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1004
1058
|
}
|
|
1005
1059
|
Object.assign(llmContext, this.currentLlmUsage);
|
|
1006
1060
|
const spanInfo = {
|
|
1061
|
+
id: randomUuid(),
|
|
1007
1062
|
spanId,
|
|
1008
1063
|
traceId,
|
|
1009
1064
|
parentId,
|
|
@@ -1644,6 +1699,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
1644
1699
|
const contexts = Object.keys(lgMetadata).length > 0 ? [lgMetadata] : [];
|
|
1645
1700
|
const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
|
|
1646
1701
|
const spanInfo = {
|
|
1702
|
+
id: randomUuid(),
|
|
1647
1703
|
spanId: runId,
|
|
1648
1704
|
traceId: invocation.traceId,
|
|
1649
1705
|
rootRunId: invocation.rootRunId,
|
|
@@ -1722,6 +1778,8 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
1722
1778
|
rawSpan.parent_id = spanInfo.parentId;
|
|
1723
1779
|
}
|
|
1724
1780
|
const payload = {
|
|
1781
|
+
id: spanInfo.id,
|
|
1782
|
+
traceId: spanInfo.traceId,
|
|
1725
1783
|
type: "sdk-function",
|
|
1726
1784
|
source: "typescript-sdk-langgraph",
|
|
1727
1785
|
traceFunctionKey: this.traceFunctionKey,
|
|
@@ -1737,6 +1795,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
1737
1795
|
sendTraceCompletion(rootSpan, activeContext) {
|
|
1738
1796
|
const completed = activeContext === null;
|
|
1739
1797
|
const traceData = {
|
|
1798
|
+
id: rootSpan.traceId,
|
|
1740
1799
|
type: "sdk-function",
|
|
1741
1800
|
source: "typescript-sdk-langgraph",
|
|
1742
1801
|
traceFunctionKey: this.traceFunctionKey,
|
|
@@ -1756,6 +1815,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
1756
1815
|
}
|
|
1757
1816
|
sendTraceStart(rootSpan) {
|
|
1758
1817
|
const traceData = {
|
|
1818
|
+
id: rootSpan.traceId,
|
|
1759
1819
|
type: "sdk-function",
|
|
1760
1820
|
source: "typescript-sdk-langgraph",
|
|
1761
1821
|
traceFunctionKey: this.traceFunctionKey,
|
|
@@ -2115,6 +2175,7 @@ var BitfabOpenAITracingProcessor = class {
|
|
|
2115
2175
|
constructor(config) {
|
|
2116
2176
|
this.activeTraces = {};
|
|
2117
2177
|
this.activeSpanMappings = {};
|
|
2178
|
+
this.canonicalTraceIds = {};
|
|
2118
2179
|
this.httpClient = new HttpClient({
|
|
2119
2180
|
apiKey: config.apiKey,
|
|
2120
2181
|
serviceUrl: config.serviceUrl ?? DEFAULT_SERVICE_URL,
|
|
@@ -2122,6 +2183,15 @@ var BitfabOpenAITracingProcessor = class {
|
|
|
2122
2183
|
});
|
|
2123
2184
|
this.getActiveSpanContext = config.getActiveSpanContext ?? null;
|
|
2124
2185
|
}
|
|
2186
|
+
getCanonicalTraceId(sourceTraceId) {
|
|
2187
|
+
const existing = this.canonicalTraceIds[sourceTraceId];
|
|
2188
|
+
if (existing) {
|
|
2189
|
+
return existing;
|
|
2190
|
+
}
|
|
2191
|
+
const created = randomUuid();
|
|
2192
|
+
this.canonicalTraceIds[sourceTraceId] = created;
|
|
2193
|
+
return created;
|
|
2194
|
+
}
|
|
2125
2195
|
/**
|
|
2126
2196
|
* Called when a trace is started.
|
|
2127
2197
|
* If there's an active withSpan context, the trace ID is remapped to the
|
|
@@ -2133,7 +2203,12 @@ var BitfabOpenAITracingProcessor = class {
|
|
|
2133
2203
|
if (activeContext) {
|
|
2134
2204
|
this.activeSpanMappings[trace.traceId] = activeContext;
|
|
2135
2205
|
}
|
|
2136
|
-
|
|
2206
|
+
const canonicalTraceId = activeContext?.traceId ?? this.getCanonicalTraceId(trace.traceId);
|
|
2207
|
+
this.canonicalTraceIds[trace.traceId] = canonicalTraceId;
|
|
2208
|
+
this.sendTrace(trace, {
|
|
2209
|
+
id: canonicalTraceId,
|
|
2210
|
+
sourceTraceId: activeContext?.traceId
|
|
2211
|
+
});
|
|
2137
2212
|
}
|
|
2138
2213
|
/**
|
|
2139
2214
|
* Called when a trace is ended.
|
|
@@ -2142,11 +2217,13 @@ var BitfabOpenAITracingProcessor = class {
|
|
|
2142
2217
|
*/
|
|
2143
2218
|
async onTraceEnd(trace) {
|
|
2144
2219
|
const mapping = this.activeSpanMappings[trace.traceId];
|
|
2145
|
-
this.sendTrace(
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2220
|
+
this.sendTrace(trace, {
|
|
2221
|
+
completed: mapping === void 0,
|
|
2222
|
+
id: mapping?.traceId ?? this.getCanonicalTraceId(trace.traceId),
|
|
2223
|
+
sourceTraceId: mapping?.traceId
|
|
2224
|
+
});
|
|
2149
2225
|
delete this.activeSpanMappings[trace.traceId];
|
|
2226
|
+
delete this.canonicalTraceIds[trace.traceId];
|
|
2150
2227
|
delete this.activeTraces[trace.traceId];
|
|
2151
2228
|
}
|
|
2152
2229
|
/**
|
|
@@ -2176,22 +2253,25 @@ var BitfabOpenAITracingProcessor = class {
|
|
|
2176
2253
|
async shutdown(_timeout) {
|
|
2177
2254
|
this.activeTraces = {};
|
|
2178
2255
|
this.activeSpanMappings = {};
|
|
2256
|
+
this.canonicalTraceIds = {};
|
|
2179
2257
|
}
|
|
2180
2258
|
/**
|
|
2181
2259
|
* Send trace to Bitfab API (fire-and-forget).
|
|
2182
2260
|
* When traceIdOverride is provided, the trace ID is remapped to link
|
|
2183
2261
|
* the OpenAI trace into an outer withSpan trace.
|
|
2184
2262
|
*/
|
|
2185
|
-
sendTrace(trace,
|
|
2263
|
+
sendTrace(trace, options = {}) {
|
|
2186
2264
|
try {
|
|
2187
|
-
const { completed, ...traceOverrides } = overrides;
|
|
2188
2265
|
const traceData = trace.toJSON();
|
|
2189
|
-
|
|
2266
|
+
if (options.sourceTraceId) {
|
|
2267
|
+
traceData.id = options.sourceTraceId;
|
|
2268
|
+
}
|
|
2190
2269
|
this.httpClient.sendExternalTrace({
|
|
2270
|
+
...options.id && { id: options.id },
|
|
2191
2271
|
type: "openai",
|
|
2192
2272
|
source: "typescript-sdk-openai-tracing",
|
|
2193
2273
|
externalTrace: traceData,
|
|
2194
|
-
completed: completed ?? false
|
|
2274
|
+
completed: options.completed ?? false
|
|
2195
2275
|
});
|
|
2196
2276
|
} catch {
|
|
2197
2277
|
}
|
|
@@ -2277,6 +2357,7 @@ var BitfabOpenAITracingProcessor = class {
|
|
|
2277
2357
|
*/
|
|
2278
2358
|
buildSpanPayload(serializedSpan, errors) {
|
|
2279
2359
|
const payload = {
|
|
2360
|
+
id: randomUuid(),
|
|
2280
2361
|
type: "openai",
|
|
2281
2362
|
source: "typescript-sdk-openai-tracing",
|
|
2282
2363
|
sourceTraceId: serializedSpan.trace_id ?? "unknown",
|
|
@@ -2299,6 +2380,10 @@ var BitfabOpenAITracingProcessor = class {
|
|
|
2299
2380
|
this.extractSpanInputResponse(span, serializedSpan, errors);
|
|
2300
2381
|
this.applySpanOverrides(serializedSpan, span.traceId ?? "");
|
|
2301
2382
|
const payload = this.buildSpanPayload(serializedSpan, errors);
|
|
2383
|
+
const canonicalTraceId = span.traceId ? this.getCanonicalTraceId(span.traceId) : void 0;
|
|
2384
|
+
if (canonicalTraceId) {
|
|
2385
|
+
payload.traceId = canonicalTraceId;
|
|
2386
|
+
}
|
|
2302
2387
|
this.httpClient.sendExternalSpan(payload);
|
|
2303
2388
|
}
|
|
2304
2389
|
};
|
|
@@ -2614,24 +2699,19 @@ function extractContextFromCollector(collector) {
|
|
|
2614
2699
|
return null;
|
|
2615
2700
|
}
|
|
2616
2701
|
}
|
|
2617
|
-
var
|
|
2618
|
-
var TRACE_ID_MAX_LENGTH = 256;
|
|
2702
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
2619
2703
|
function validateTraceId(traceId) {
|
|
2620
|
-
if (typeof traceId !== "string" || traceId
|
|
2621
|
-
throw new BitfabError("traceId
|
|
2704
|
+
if (typeof traceId !== "string" || !UUID_PATTERN.test(traceId)) {
|
|
2705
|
+
throw new BitfabError("traceId must be a valid Bitfab trace ID");
|
|
2622
2706
|
}
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
);
|
|
2627
|
-
}
|
|
2628
|
-
if (!TRACE_ID_PATTERN.test(traceId)) {
|
|
2629
|
-
throw new BitfabError(
|
|
2630
|
-
`traceId may only contain letters, digits, "_", "-", ".", ":"`
|
|
2631
|
-
);
|
|
2707
|
+
}
|
|
2708
|
+
function validateSpanId(id) {
|
|
2709
|
+
if (typeof id !== "string" || !UUID_PATTERN.test(id)) {
|
|
2710
|
+
throw new BitfabError("id must be a valid Bitfab span ID");
|
|
2632
2711
|
}
|
|
2633
2712
|
}
|
|
2634
2713
|
var noOpSpan = {
|
|
2714
|
+
id: "",
|
|
2635
2715
|
traceId: "",
|
|
2636
2716
|
addContext() {
|
|
2637
2717
|
},
|
|
@@ -2655,6 +2735,7 @@ function getCurrentSpan() {
|
|
|
2655
2735
|
return noOpSpan;
|
|
2656
2736
|
}
|
|
2657
2737
|
return {
|
|
2738
|
+
id: current.spanId,
|
|
2658
2739
|
traceId: current.traceId,
|
|
2659
2740
|
addContext(context) {
|
|
2660
2741
|
try {
|
|
@@ -2746,6 +2827,12 @@ var Bitfab = class {
|
|
|
2746
2827
|
constructor(config) {
|
|
2747
2828
|
/** Gate the empty-key warning to fire at most once. */
|
|
2748
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 = [];
|
|
2749
2836
|
this.apiKeyConfig = config.apiKey;
|
|
2750
2837
|
this.serviceUrl = config.serviceUrl ?? DEFAULT_SERVICE_URL;
|
|
2751
2838
|
this.timeout = config.timeout ?? 12e4;
|
|
@@ -3358,25 +3445,78 @@ var Bitfab = class {
|
|
|
3358
3445
|
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
3359
3446
|
const callIndex = counters.get(counterKey) ?? 0;
|
|
3360
3447
|
counters.set(counterKey, callIndex + 1);
|
|
3361
|
-
const
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
if (
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
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;
|
|
3373
3465
|
void sendSpan({ result: output, mocked: true });
|
|
3374
|
-
if (fnReturnsPromise) {
|
|
3375
|
-
return Promise.resolve(output);
|
|
3376
|
-
}
|
|
3377
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);
|
|
3378
3510
|
}
|
|
3379
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
|
+
}
|
|
3380
3520
|
}
|
|
3381
3521
|
const recordSpan = (result) => {
|
|
3382
3522
|
if (options.finalize) {
|
|
@@ -3451,20 +3591,19 @@ var Bitfab = class {
|
|
|
3451
3591
|
}
|
|
3452
3592
|
/**
|
|
3453
3593
|
* Get a detached handle to a previously-created trace, looked up by the
|
|
3454
|
-
*
|
|
3594
|
+
* canonical Bitfab trace ID.
|
|
3455
3595
|
*
|
|
3456
3596
|
* The returned handle is not tied to AsyncLocalStorage - each method sends
|
|
3457
3597
|
* to the server immediately. Useful for adding context to a trace from a
|
|
3458
3598
|
* different process or thread than the one that created it.
|
|
3459
3599
|
*
|
|
3460
|
-
* Throws synchronously if `traceId` is
|
|
3461
|
-
*
|
|
3462
|
-
* no trace exists with that id in the org; the failure surfaces as a
|
|
3600
|
+
* Throws synchronously if `traceId` is not a valid Bitfab trace ID. The
|
|
3601
|
+
* server returns 404 if no trace exists with that ID in the org; the failure surfaces as a
|
|
3463
3602
|
* logged warning (fire-and-forget) or via the awaited promise.
|
|
3464
3603
|
*
|
|
3465
3604
|
* Example:
|
|
3466
3605
|
* ```typescript
|
|
3467
|
-
* const trace = client.getTrace(
|
|
3606
|
+
* const trace = client.getTrace(traceId);
|
|
3468
3607
|
* await trace.addContext({ refund_status: "approved" });
|
|
3469
3608
|
* await trace.setMetadata({ region: "us-west" });
|
|
3470
3609
|
* ```
|
|
@@ -3504,6 +3643,33 @@ var Bitfab = class {
|
|
|
3504
3643
|
}
|
|
3505
3644
|
};
|
|
3506
3645
|
}
|
|
3646
|
+
/**
|
|
3647
|
+
* Fetch one persisted span from a trace without loading the full trace.
|
|
3648
|
+
* Name lookups return the last matching span by default. Pass `occurrence`
|
|
3649
|
+
* as `"first"` or a zero-based index to select a different match.
|
|
3650
|
+
*/
|
|
3651
|
+
async getTraceSpan(traceId, lookup) {
|
|
3652
|
+
validateTraceId(traceId);
|
|
3653
|
+
const hasId = lookup.id !== void 0;
|
|
3654
|
+
const hasName = lookup.name !== void 0;
|
|
3655
|
+
if (hasId === hasName) {
|
|
3656
|
+
throw new BitfabError("Provide exactly one of id or name");
|
|
3657
|
+
}
|
|
3658
|
+
if (hasId) {
|
|
3659
|
+
validateSpanId(lookup.id);
|
|
3660
|
+
} else {
|
|
3661
|
+
if (lookup.name.length === 0) {
|
|
3662
|
+
throw new BitfabError("name must be a non-empty string");
|
|
3663
|
+
}
|
|
3664
|
+
const occurrence = lookup.occurrence ?? "last";
|
|
3665
|
+
if (occurrence !== "first" && occurrence !== "last" && (!Number.isInteger(occurrence) || occurrence < 0)) {
|
|
3666
|
+
throw new BitfabError(
|
|
3667
|
+
'occurrence must be "first", "last", or a non-negative integer'
|
|
3668
|
+
);
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
return this.httpClient.getTraceSpan(traceId, lookup);
|
|
3672
|
+
}
|
|
3507
3673
|
/**
|
|
3508
3674
|
* Get a function wrapper for a specific trace function key.
|
|
3509
3675
|
*
|
|
@@ -3562,6 +3728,7 @@ var Bitfab = class {
|
|
|
3562
3728
|
};
|
|
3563
3729
|
}
|
|
3564
3730
|
return this.httpClient.sendExternalTrace({
|
|
3731
|
+
id: params.traceId,
|
|
3565
3732
|
type: "sdk-function",
|
|
3566
3733
|
source: "typescript-sdk-function",
|
|
3567
3734
|
traceFunctionKey: params.traceFunctionKey,
|
|
@@ -3617,6 +3784,8 @@ var Bitfab = class {
|
|
|
3617
3784
|
externalSpan.input_source_span_id = params.inputSourceSpanId;
|
|
3618
3785
|
}
|
|
3619
3786
|
return this.httpClient.sendExternalSpan({
|
|
3787
|
+
id: params.spanId,
|
|
3788
|
+
traceId: params.traceId,
|
|
3620
3789
|
type: "sdk-function",
|
|
3621
3790
|
source: "typescript-sdk-function",
|
|
3622
3791
|
sourceTraceId: params.traceId,
|
|
@@ -3626,26 +3795,14 @@ var Bitfab = class {
|
|
|
3626
3795
|
...params.mocked && { mocked: true }
|
|
3627
3796
|
});
|
|
3628
3797
|
}
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
* invocation records a trace tied to the test run. The plain-callable form
|
|
3638
|
-
* is how handler-instrumented workflows (LangGraph/LangChain, Claude Agent
|
|
3639
|
-
* SDK) replay - those record traces under a key with no `withSpan`-wrapped
|
|
3640
|
-
* root in the app.
|
|
3641
|
-
*
|
|
3642
|
-
* @param traceFunctionKey - The trace function key to replay
|
|
3643
|
-
* @param fn - The function to run recorded inputs through
|
|
3644
|
-
* @param options - Optional replay options. When `traceIds` is passed,
|
|
3645
|
-
* `limit` is ignored (with a warning): an explicit ID list already
|
|
3646
|
-
* determines how many traces replay.
|
|
3647
|
-
* @returns ReplayResult with items, testRunId, and testRunUrl
|
|
3648
|
-
*/
|
|
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
|
+
}
|
|
3649
3806
|
async replay(traceFunctionKey, fn, options) {
|
|
3650
3807
|
const wrappedKey = fn._bitfabTraceFunctionKey;
|
|
3651
3808
|
let replayFn = fn;
|
|
@@ -3660,13 +3817,14 @@ var Bitfab = class {
|
|
|
3660
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.`
|
|
3661
3818
|
);
|
|
3662
3819
|
}
|
|
3663
|
-
const { replay: doReplay } = await import("./replay-
|
|
3820
|
+
const { replay: doReplay } = await import("./replay-HEGU3YU2.js");
|
|
3664
3821
|
return doReplay(
|
|
3665
3822
|
this.httpClient,
|
|
3666
3823
|
this.serviceUrl,
|
|
3667
3824
|
traceFunctionKey,
|
|
3668
3825
|
replayFn,
|
|
3669
|
-
options
|
|
3826
|
+
options,
|
|
3827
|
+
this.mockOverrides
|
|
3670
3828
|
);
|
|
3671
3829
|
}
|
|
3672
3830
|
};
|
|
@@ -3885,4 +4043,4 @@ export {
|
|
|
3885
4043
|
BitfabFunction,
|
|
3886
4044
|
finalizers
|
|
3887
4045
|
};
|
|
3888
|
-
//# sourceMappingURL=chunk-
|
|
4046
|
+
//# sourceMappingURL=chunk-2M5AWVVQ.js.map
|