bitfab 0.23.2 → 0.24.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-EQI6ZJC3.js → chunk-26MUT4IP.js} +57 -2
- package/dist/chunk-26MUT4IP.js.map +1 -0
- package/dist/{chunk-PP5K6RIU.js → chunk-S3PN26RH.js} +322 -206
- package/dist/chunk-S3PN26RH.js.map +1 -0
- package/dist/index.cjs +393 -205
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -8
- package/dist/index.d.ts +24 -8
- package/dist/index.js +2 -2
- package/dist/node.cjs +393 -205
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +0 -1
- package/dist/node.d.ts +0 -1
- package/dist/node.js +2 -2
- package/dist/{replay-V6RPJYXZ.js → replay-CQIU2ITL.js} +4 -3
- package/dist/replay-CQIU2ITL.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-EQI6ZJC3.js.map +0 -1
- package/dist/chunk-PP5K6RIU.js.map +0 -1
- package/dist/replay-V6RPJYXZ.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -54,6 +54,54 @@ var init_errors = __esm({
|
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
+
// src/warnOnce.ts
|
|
58
|
+
function warnOnce(key, message) {
|
|
59
|
+
if (warned.has(key)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
warned.add(key);
|
|
63
|
+
try {
|
|
64
|
+
console.warn(`[bitfab] ${message}`);
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
var warned;
|
|
69
|
+
var init_warnOnce = __esm({
|
|
70
|
+
"src/warnOnce.ts"() {
|
|
71
|
+
"use strict";
|
|
72
|
+
warned = /* @__PURE__ */ new Set();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// src/randomUuid.ts
|
|
77
|
+
function randomUuid() {
|
|
78
|
+
const globalCrypto = globalThis.crypto;
|
|
79
|
+
if (typeof globalCrypto?.randomUUID === "function") {
|
|
80
|
+
try {
|
|
81
|
+
return globalCrypto.randomUUID();
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
warnOnce(
|
|
86
|
+
"crypto-unavailable",
|
|
87
|
+
"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
|
|
88
|
+
);
|
|
89
|
+
return fallbackUuidV4();
|
|
90
|
+
}
|
|
91
|
+
function fallbackUuidV4() {
|
|
92
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
93
|
+
const rand = Math.random() * 16 | 0;
|
|
94
|
+
const value = char === "x" ? rand : rand & 3 | 8;
|
|
95
|
+
return value.toString(16);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
var init_randomUuid = __esm({
|
|
99
|
+
"src/randomUuid.ts"() {
|
|
100
|
+
"use strict";
|
|
101
|
+
init_warnOnce();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
57
105
|
// src/serialize.ts
|
|
58
106
|
function describeValue(value) {
|
|
59
107
|
try {
|
|
@@ -66,6 +114,10 @@ function describeValue(value) {
|
|
|
66
114
|
return typeof value;
|
|
67
115
|
}
|
|
68
116
|
function unserializableStub(value, reason) {
|
|
117
|
+
warnOnce(
|
|
118
|
+
`serialize:${reason.replace(/\d+/g, "N")}`,
|
|
119
|
+
`a value could not be fully serialized for a span (${reason}); it was replaced with a placeholder. The span still ships, but its captured input/output is incomplete.`
|
|
120
|
+
);
|
|
69
121
|
let summary;
|
|
70
122
|
try {
|
|
71
123
|
summary = `<unserializable: ${describeValue(value)} (${reason})>`;
|
|
@@ -105,7 +157,19 @@ function deserializeValue(serialized) {
|
|
|
105
157
|
});
|
|
106
158
|
}
|
|
107
159
|
function toJsonSafe(value) {
|
|
108
|
-
|
|
160
|
+
const safe = toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet());
|
|
161
|
+
try {
|
|
162
|
+
const size = JSON.stringify(safe)?.length ?? 0;
|
|
163
|
+
if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {
|
|
164
|
+
warnOnce(
|
|
165
|
+
"toJsonSafe:too_large",
|
|
166
|
+
`a framework payload exceeded ${MAX_FRAMEWORK_SERIALIZED_BYTES} bytes and was replaced with a placeholder so the span still ships. The captured state for this span is incomplete.`
|
|
167
|
+
);
|
|
168
|
+
return `<unserializable: too_large_${size}_bytes>`;
|
|
169
|
+
}
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
return safe;
|
|
109
173
|
}
|
|
110
174
|
function toJsonSafeInner(value, depth, seen) {
|
|
111
175
|
if (value === null || value === void 0) {
|
|
@@ -158,12 +222,14 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
158
222
|
seen.delete(value);
|
|
159
223
|
return result;
|
|
160
224
|
}
|
|
161
|
-
var import_superjson, MAX_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
225
|
+
var import_superjson, MAX_SERIALIZED_BYTES, MAX_FRAMEWORK_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
162
226
|
var init_serialize = __esm({
|
|
163
227
|
"src/serialize.ts"() {
|
|
164
228
|
"use strict";
|
|
165
229
|
import_superjson = __toESM(require("superjson"), 1);
|
|
230
|
+
init_warnOnce();
|
|
166
231
|
MAX_SERIALIZED_BYTES = 512e3;
|
|
232
|
+
MAX_FRAMEWORK_SERIALIZED_BYTES = 2e6;
|
|
167
233
|
MAX_SAFE_DEPTH = 6;
|
|
168
234
|
}
|
|
169
235
|
});
|
|
@@ -288,7 +354,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
288
354
|
let originalOutput;
|
|
289
355
|
let result;
|
|
290
356
|
let error = null;
|
|
291
|
-
const replayedTraceId =
|
|
357
|
+
const replayedTraceId = randomUuid();
|
|
292
358
|
const pendingPersistence = [];
|
|
293
359
|
try {
|
|
294
360
|
const span = await httpClient.getExternalSpan(serverItem.externalSpanId);
|
|
@@ -472,6 +538,7 @@ var init_replay = __esm({
|
|
|
472
538
|
"src/replay.ts"() {
|
|
473
539
|
"use strict";
|
|
474
540
|
init_errors();
|
|
541
|
+
init_randomUuid();
|
|
475
542
|
init_replayContext();
|
|
476
543
|
init_serialize();
|
|
477
544
|
}
|
|
@@ -501,13 +568,24 @@ __export(index_exports, {
|
|
|
501
568
|
module.exports = __toCommonJS(index_exports);
|
|
502
569
|
|
|
503
570
|
// src/version.generated.ts
|
|
504
|
-
var __version__ = "0.
|
|
571
|
+
var __version__ = "0.24.0";
|
|
505
572
|
|
|
506
573
|
// src/constants.ts
|
|
507
574
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
508
575
|
|
|
509
576
|
// src/http.ts
|
|
510
577
|
init_errors();
|
|
578
|
+
|
|
579
|
+
// src/unrefTimer.ts
|
|
580
|
+
function unrefTimer(timer) {
|
|
581
|
+
const handle = timer;
|
|
582
|
+
if (typeof handle.unref === "function") {
|
|
583
|
+
handle.unref();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// src/http.ts
|
|
588
|
+
init_warnOnce();
|
|
511
589
|
function serializePayloadBody(payload) {
|
|
512
590
|
try {
|
|
513
591
|
return { body: JSON.stringify(payload), dropped: [] };
|
|
@@ -552,11 +630,20 @@ function serializePayloadBody(payload) {
|
|
|
552
630
|
result = `<unserializable: ${className}>`;
|
|
553
631
|
}
|
|
554
632
|
} else {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
633
|
+
try {
|
|
634
|
+
const out = {};
|
|
635
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
636
|
+
out[k] = sanitize(v, seen);
|
|
637
|
+
}
|
|
638
|
+
result = out;
|
|
639
|
+
} catch {
|
|
640
|
+
warnOnce(
|
|
641
|
+
"payload:field-getter-threw",
|
|
642
|
+
"a value with a throwing getter/proxy could not be serialized into a span payload; it was replaced with a placeholder. The span still ships with its other fields intact."
|
|
643
|
+
);
|
|
644
|
+
dropped.push(className);
|
|
645
|
+
result = `<unserializable: ${className}>`;
|
|
558
646
|
}
|
|
559
|
-
result = out;
|
|
560
647
|
}
|
|
561
648
|
seen.delete(obj);
|
|
562
649
|
return result;
|
|
@@ -601,10 +688,20 @@ async function flushTraces(timeoutMs = 5e3) {
|
|
|
601
688
|
if (pendingTracePromises.size === 0) {
|
|
602
689
|
return;
|
|
603
690
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
691
|
+
let timer;
|
|
692
|
+
try {
|
|
693
|
+
await Promise.race([
|
|
694
|
+
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
695
|
+
new Promise((resolve) => {
|
|
696
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
697
|
+
unrefTimer(timer);
|
|
698
|
+
})
|
|
699
|
+
]);
|
|
700
|
+
} finally {
|
|
701
|
+
if (timer) {
|
|
702
|
+
clearTimeout(timer);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
608
705
|
}
|
|
609
706
|
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
610
707
|
let isFlushing = false;
|
|
@@ -914,6 +1011,7 @@ var HttpClient = class {
|
|
|
914
1011
|
};
|
|
915
1012
|
|
|
916
1013
|
// src/claudeAgentSdk.ts
|
|
1014
|
+
init_randomUuid();
|
|
917
1015
|
init_serialize();
|
|
918
1016
|
function nowIso() {
|
|
919
1017
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1001,7 +1099,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1001
1099
|
if (this.activeContext) {
|
|
1002
1100
|
this.traceId = this.activeContext.traceId;
|
|
1003
1101
|
} else {
|
|
1004
|
-
this.traceId =
|
|
1102
|
+
this.traceId = randomUuid();
|
|
1005
1103
|
}
|
|
1006
1104
|
this.traceStartedAt = nowIso();
|
|
1007
1105
|
return this.traceId;
|
|
@@ -1026,7 +1124,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1026
1124
|
if (this.activeContext !== null) {
|
|
1027
1125
|
return;
|
|
1028
1126
|
}
|
|
1029
|
-
const spanId =
|
|
1127
|
+
const spanId = randomUuid();
|
|
1030
1128
|
this.startSpan(spanId, this.traceFunctionKey, "agent", this.rootInput, null);
|
|
1031
1129
|
this.rootSpanId = spanId;
|
|
1032
1130
|
}
|
|
@@ -1140,7 +1238,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1140
1238
|
// ── hook callbacks ───────────────────────────────────────────
|
|
1141
1239
|
async preToolUseHook(inputData, toolUseId, _context) {
|
|
1142
1240
|
try {
|
|
1143
|
-
const sid = inputData.tool_use_id ?? toolUseId ??
|
|
1241
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? randomUuid();
|
|
1144
1242
|
const toolName = inputData.tool_name ?? "tool";
|
|
1145
1243
|
const toolInput = inputData.tool_input ?? {};
|
|
1146
1244
|
const agentId = inputData.agent_id;
|
|
@@ -1170,10 +1268,10 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1170
1268
|
}
|
|
1171
1269
|
async subagentStartHook(inputData, _toolUseId, _context) {
|
|
1172
1270
|
try {
|
|
1173
|
-
const agentId = inputData.agent_id ??
|
|
1271
|
+
const agentId = inputData.agent_id ?? randomUuid();
|
|
1174
1272
|
const agentType = inputData.agent_type ?? "subagent";
|
|
1175
1273
|
const parentId = this.getParentId();
|
|
1176
|
-
const spanId =
|
|
1274
|
+
const spanId = randomUuid();
|
|
1177
1275
|
this.activeSubagentSpans.set(agentId, spanId);
|
|
1178
1276
|
this.startSpan(
|
|
1179
1277
|
spanId,
|
|
@@ -1314,7 +1412,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1314
1412
|
this.flushLlmTurn();
|
|
1315
1413
|
this.conversationHistory.push(...this.pendingMessages);
|
|
1316
1414
|
this.pendingMessages = [];
|
|
1317
|
-
this.currentLlmSpanId =
|
|
1415
|
+
this.currentLlmSpanId = randomUuid();
|
|
1318
1416
|
this.currentLlmMessageId = messageId ?? null;
|
|
1319
1417
|
this.currentLlmContent = [];
|
|
1320
1418
|
this.currentLlmModel = inner.model ?? null;
|
|
@@ -1442,6 +1540,16 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1442
1540
|
// src/client.ts
|
|
1443
1541
|
init_asyncStorage();
|
|
1444
1542
|
|
|
1543
|
+
// src/optionalPeer.ts
|
|
1544
|
+
function importOptionalPeer(specifierParts) {
|
|
1545
|
+
const specifier = specifierParts.join("/");
|
|
1546
|
+
return import(
|
|
1547
|
+
/* webpackIgnore: true */
|
|
1548
|
+
/* @vite-ignore */
|
|
1549
|
+
specifier
|
|
1550
|
+
);
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1445
1553
|
// src/baml.ts
|
|
1446
1554
|
var cachedBaml = null;
|
|
1447
1555
|
async function loadBaml() {
|
|
@@ -1449,7 +1557,7 @@ async function loadBaml() {
|
|
|
1449
1557
|
return cachedBaml;
|
|
1450
1558
|
}
|
|
1451
1559
|
try {
|
|
1452
|
-
cachedBaml = await
|
|
1560
|
+
cachedBaml = await importOptionalPeer(["@boundaryml", "baml"]);
|
|
1453
1561
|
return cachedBaml;
|
|
1454
1562
|
} catch {
|
|
1455
1563
|
throw new Error(
|
|
@@ -1748,6 +1856,7 @@ function buildSnapshotRef(config, sdkWallClockBeforeFn) {
|
|
|
1748
1856
|
}
|
|
1749
1857
|
|
|
1750
1858
|
// src/langgraph.ts
|
|
1859
|
+
init_randomUuid();
|
|
1751
1860
|
init_serialize();
|
|
1752
1861
|
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
1753
1862
|
var LANGGRAPH_METADATA_KEYS = [
|
|
@@ -1996,7 +2105,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
1996
2105
|
} else {
|
|
1997
2106
|
const activeContext = this.getActiveSpanContext?.() ?? null;
|
|
1998
2107
|
invocation = {
|
|
1999
|
-
traceId: activeContext ? activeContext.traceId :
|
|
2108
|
+
traceId: activeContext ? activeContext.traceId : randomUuid(),
|
|
2000
2109
|
activeContext,
|
|
2001
2110
|
rootRunId: runId
|
|
2002
2111
|
};
|
|
@@ -2291,8 +2400,25 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2291
2400
|
this.withSpanFn = config.withSpan;
|
|
2292
2401
|
this.getActiveSpanContext = config.getActiveSpanContext;
|
|
2293
2402
|
}
|
|
2403
|
+
/**
|
|
2404
|
+
* Drop-in replacement for the OpenAI Agents SDK's `run()` that records a
|
|
2405
|
+
* replayable root `agent` span.
|
|
2406
|
+
*
|
|
2407
|
+
* The `input` is captured as the root span's input (as a single positional
|
|
2408
|
+
* argument, so `replay(key, fn)` re-feeds it), and the run's `finalOutput`
|
|
2409
|
+
* is recorded as the root output. For streaming runs (`{ stream: true }`),
|
|
2410
|
+
* the result is handed back immediately and the final output is recorded
|
|
2411
|
+
* once the stream completes — first-byte latency is untouched.
|
|
2412
|
+
*
|
|
2413
|
+
* The process-wide tracing processor (`getOpenAiTracingProcessor`) must still
|
|
2414
|
+
* be registered: it captures the LLM/tool/handoff spans that nest beneath
|
|
2415
|
+
* this root.
|
|
2416
|
+
*/
|
|
2294
2417
|
async wrapRun(agent, input, options) {
|
|
2295
|
-
const { run } = await
|
|
2418
|
+
const { run } = await importOptionalPeer([
|
|
2419
|
+
"@openai",
|
|
2420
|
+
"agents"
|
|
2421
|
+
]);
|
|
2296
2422
|
if (this.getActiveSpanContext?.() != null) {
|
|
2297
2423
|
return run(
|
|
2298
2424
|
agent,
|
|
@@ -2326,6 +2452,7 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2326
2452
|
};
|
|
2327
2453
|
|
|
2328
2454
|
// src/client.ts
|
|
2455
|
+
init_randomUuid();
|
|
2329
2456
|
init_replayContext();
|
|
2330
2457
|
|
|
2331
2458
|
// src/replayEnvironment.ts
|
|
@@ -2745,6 +2872,7 @@ var BitfabVercelAiHandler = class {
|
|
|
2745
2872
|
};
|
|
2746
2873
|
|
|
2747
2874
|
// src/client.ts
|
|
2875
|
+
init_warnOnce();
|
|
2748
2876
|
var activeTraceStates = /* @__PURE__ */ new Map();
|
|
2749
2877
|
var pendingSpanPromises = /* @__PURE__ */ new Map();
|
|
2750
2878
|
var asyncLocalStorage = null;
|
|
@@ -2843,7 +2971,10 @@ async function loadCollectorClass() {
|
|
|
2843
2971
|
return cachedCollectorClass;
|
|
2844
2972
|
}
|
|
2845
2973
|
try {
|
|
2846
|
-
const baml = await
|
|
2974
|
+
const baml = await importOptionalPeer([
|
|
2975
|
+
"@boundaryml",
|
|
2976
|
+
"baml"
|
|
2977
|
+
]);
|
|
2847
2978
|
cachedCollectorClass = baml.Collector;
|
|
2848
2979
|
return cachedCollectorClass;
|
|
2849
2980
|
} catch {
|
|
@@ -3100,7 +3231,20 @@ var Bitfab = class {
|
|
|
3100
3231
|
functionVersion.providers,
|
|
3101
3232
|
this.envVars
|
|
3102
3233
|
);
|
|
3103
|
-
|
|
3234
|
+
let resultStr;
|
|
3235
|
+
if (typeof executionResult.result === "string") {
|
|
3236
|
+
resultStr = executionResult.result;
|
|
3237
|
+
} else {
|
|
3238
|
+
try {
|
|
3239
|
+
resultStr = JSON.stringify(executionResult.result) ?? String(executionResult.result);
|
|
3240
|
+
} catch {
|
|
3241
|
+
warnOnce(
|
|
3242
|
+
"call-result-serialize",
|
|
3243
|
+
"a local execution result could not be JSON-serialized; storing its String() form instead. The call still returns its real value."
|
|
3244
|
+
);
|
|
3245
|
+
resultStr = String(executionResult.result);
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3104
3248
|
this.httpClient.sendInternalTrace(functionVersion.id, {
|
|
3105
3249
|
result: resultStr,
|
|
3106
3250
|
source: "typescript-sdk",
|
|
@@ -3349,11 +3493,26 @@ var Bitfab = class {
|
|
|
3349
3493
|
return await bamlClient[methodName](...args);
|
|
3350
3494
|
}
|
|
3351
3495
|
const collector = new CollectorClass("bitfab-baml-tracing");
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
trackedClient
|
|
3356
|
-
|
|
3496
|
+
let trackedClient;
|
|
3497
|
+
let trackedMethod;
|
|
3498
|
+
try {
|
|
3499
|
+
trackedClient = bamlClient.withOptions({ collector });
|
|
3500
|
+
const method2 = trackedClient[methodName];
|
|
3501
|
+
if (typeof method2 !== "function") {
|
|
3502
|
+
throw new BitfabError(
|
|
3503
|
+
"bamlClient.withOptions did not return the wrapped method"
|
|
3504
|
+
);
|
|
3505
|
+
}
|
|
3506
|
+
trackedMethod = method2;
|
|
3507
|
+
} catch {
|
|
3508
|
+
warnOnce(
|
|
3509
|
+
`wrapBAML-setup:${methodName}`,
|
|
3510
|
+
`BAML tracing setup failed for "${methodName}" (incompatible bamlClient or BAML version); calling it untraced. The call still runs; no span is recorded.`
|
|
3511
|
+
);
|
|
3512
|
+
wrappedFn.collector = null;
|
|
3513
|
+
return await bamlClient[methodName](...args);
|
|
3514
|
+
}
|
|
3515
|
+
const result = await trackedMethod.bind(trackedClient)(...args);
|
|
3357
3516
|
wrappedFn.collector = collector;
|
|
3358
3517
|
try {
|
|
3359
3518
|
const prompt = extractPromptFromCollector(collector);
|
|
@@ -3429,200 +3588,229 @@ var Bitfab = class {
|
|
|
3429
3588
|
() => wrappedFn.apply(this, args)
|
|
3430
3589
|
);
|
|
3431
3590
|
}
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
const
|
|
3446
|
-
|
|
3591
|
+
let newStack;
|
|
3592
|
+
let executeWithContext;
|
|
3593
|
+
let registeredTraceId;
|
|
3594
|
+
try {
|
|
3595
|
+
const currentStack = getSpanStack();
|
|
3596
|
+
const parentContext = currentStack[currentStack.length - 1];
|
|
3597
|
+
const replayCtxForTraceId = parentContext ? null : getReplayContext();
|
|
3598
|
+
const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? randomUuid();
|
|
3599
|
+
const spanId = randomUuid();
|
|
3600
|
+
const parentSpanId = parentContext?.spanId ?? null;
|
|
3601
|
+
const isRootSpan = parentSpanId === null;
|
|
3602
|
+
const newContext = { traceId, spanId, contexts: [] };
|
|
3603
|
+
newStack = [...currentStack, newContext];
|
|
3604
|
+
const inputs = args;
|
|
3605
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3606
|
+
if (isRootSpan && !activeTraceStates.has(traceId)) {
|
|
3607
|
+
const replayCtxAtRoot = getReplayContext();
|
|
3608
|
+
const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
|
|
3609
|
+
activeTraceStates.set(traceId, {
|
|
3610
|
+
traceId,
|
|
3611
|
+
startedAt,
|
|
3612
|
+
contexts: [],
|
|
3613
|
+
...replayCtxAtRoot?.testRunId && {
|
|
3614
|
+
testRunId: replayCtxAtRoot.testRunId
|
|
3615
|
+
},
|
|
3616
|
+
...replayCtxAtRoot?.inputSourceTraceId && {
|
|
3617
|
+
inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
|
|
3618
|
+
},
|
|
3619
|
+
dbSnapshotRef
|
|
3620
|
+
});
|
|
3621
|
+
pendingSpanPromises.set(traceId, []);
|
|
3622
|
+
registeredTraceId = traceId;
|
|
3623
|
+
}
|
|
3624
|
+
const functionName = fn.name !== "" ? fn.name : void 0;
|
|
3625
|
+
const baseSpanParams = {
|
|
3626
|
+
traceFunctionKey,
|
|
3627
|
+
functionName,
|
|
3628
|
+
spanName: options.name ?? functionName ?? traceFunctionKey,
|
|
3447
3629
|
traceId,
|
|
3630
|
+
spanId,
|
|
3631
|
+
parentSpanId,
|
|
3632
|
+
inputs,
|
|
3448
3633
|
startedAt,
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
startedAt,
|
|
3470
|
-
spanType: options.type ?? "custom"
|
|
3471
|
-
};
|
|
3472
|
-
const sendSpan = async (params, spanOpts) => {
|
|
3473
|
-
const replayCtx = getReplayContext();
|
|
3474
|
-
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3475
|
-
let resolvePersistence;
|
|
3476
|
-
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3477
|
-
persistenceCollector.push(
|
|
3478
|
-
new Promise((resolve) => {
|
|
3479
|
-
resolvePersistence = resolve;
|
|
3480
|
-
})
|
|
3481
|
-
);
|
|
3482
|
-
}
|
|
3483
|
-
try {
|
|
3484
|
-
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3485
|
-
const spanPromise = self.sendWrapperSpan({
|
|
3486
|
-
...baseSpanParams,
|
|
3487
|
-
...params,
|
|
3488
|
-
contexts: newContext.contexts,
|
|
3489
|
-
prompt: newContext.prompt,
|
|
3490
|
-
endedAt,
|
|
3491
|
-
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3492
|
-
...replayCtx?.inputSourceSpanId && {
|
|
3493
|
-
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3494
|
-
}
|
|
3495
|
-
});
|
|
3496
|
-
if (isRootSpan) {
|
|
3497
|
-
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3498
|
-
pending.push(spanPromise);
|
|
3499
|
-
if (persistenceCollector) {
|
|
3500
|
-
await Promise.allSettled(pending);
|
|
3501
|
-
} else {
|
|
3502
|
-
await Promise.race([
|
|
3503
|
-
Promise.allSettled(pending),
|
|
3504
|
-
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
3505
|
-
]);
|
|
3506
|
-
}
|
|
3507
|
-
pendingSpanPromises.delete(traceId);
|
|
3508
|
-
const traceState = activeTraceStates.get(traceId);
|
|
3509
|
-
const completionPromise = self.sendTraceCompletion({
|
|
3510
|
-
traceFunctionKey,
|
|
3511
|
-
traceId,
|
|
3512
|
-
startedAt: traceState?.startedAt ?? startedAt,
|
|
3634
|
+
spanType: options.type ?? "custom"
|
|
3635
|
+
};
|
|
3636
|
+
const sendSpan = async (params, spanOpts) => {
|
|
3637
|
+
const replayCtx = getReplayContext();
|
|
3638
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3639
|
+
let resolvePersistence;
|
|
3640
|
+
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3641
|
+
persistenceCollector.push(
|
|
3642
|
+
new Promise((resolve) => {
|
|
3643
|
+
resolvePersistence = resolve;
|
|
3644
|
+
})
|
|
3645
|
+
);
|
|
3646
|
+
}
|
|
3647
|
+
try {
|
|
3648
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3649
|
+
const spanPromise = self.sendWrapperSpan({
|
|
3650
|
+
...baseSpanParams,
|
|
3651
|
+
...params,
|
|
3652
|
+
contexts: newContext.contexts,
|
|
3653
|
+
prompt: newContext.prompt,
|
|
3513
3654
|
endedAt,
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
testRunId: traceState?.testRunId,
|
|
3518
|
-
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3519
|
-
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3520
|
-
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3521
|
-
// whether customer code obtained the branch URL during this
|
|
3522
|
-
// item. Omitted entirely when no lease was attached, so the
|
|
3523
|
-
// server can distinguish "no branch" from "branch ignored".
|
|
3524
|
-
...replayCtx?.dbBranchLease && {
|
|
3525
|
-
dbSnapshotUsage: {
|
|
3526
|
-
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3527
|
-
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3528
|
-
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3529
|
-
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3530
|
-
}
|
|
3655
|
+
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3656
|
+
...replayCtx?.inputSourceSpanId && {
|
|
3657
|
+
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3531
3658
|
}
|
|
3532
3659
|
});
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
await completionPromise;
|
|
3536
|
-
}
|
|
3537
|
-
} else {
|
|
3538
|
-
const pending = pendingSpanPromises.get(traceId);
|
|
3539
|
-
if (pending) {
|
|
3660
|
+
if (isRootSpan) {
|
|
3661
|
+
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3540
3662
|
pending.push(spanPromise);
|
|
3663
|
+
if (persistenceCollector) {
|
|
3664
|
+
await Promise.allSettled(pending);
|
|
3665
|
+
} else {
|
|
3666
|
+
let raceTimer;
|
|
3667
|
+
try {
|
|
3668
|
+
await Promise.race([
|
|
3669
|
+
Promise.allSettled(pending),
|
|
3670
|
+
new Promise((resolve) => {
|
|
3671
|
+
raceTimer = setTimeout(resolve, 5e3);
|
|
3672
|
+
unrefTimer(raceTimer);
|
|
3673
|
+
})
|
|
3674
|
+
]);
|
|
3675
|
+
} finally {
|
|
3676
|
+
if (raceTimer) {
|
|
3677
|
+
clearTimeout(raceTimer);
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
pendingSpanPromises.delete(traceId);
|
|
3682
|
+
const traceState = activeTraceStates.get(traceId);
|
|
3683
|
+
const completionPromise = self.sendTraceCompletion({
|
|
3684
|
+
traceFunctionKey,
|
|
3685
|
+
traceId,
|
|
3686
|
+
startedAt: traceState?.startedAt ?? startedAt,
|
|
3687
|
+
endedAt,
|
|
3688
|
+
sessionId: traceState?.sessionId,
|
|
3689
|
+
metadata: traceState?.metadata,
|
|
3690
|
+
contexts: traceState?.contexts ?? [],
|
|
3691
|
+
testRunId: traceState?.testRunId,
|
|
3692
|
+
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3693
|
+
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3694
|
+
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3695
|
+
// whether customer code obtained the branch URL during this
|
|
3696
|
+
// item. Omitted entirely when no lease was attached, so the
|
|
3697
|
+
// server can distinguish "no branch" from "branch ignored".
|
|
3698
|
+
...replayCtx?.dbBranchLease && {
|
|
3699
|
+
dbSnapshotUsage: {
|
|
3700
|
+
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3701
|
+
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3702
|
+
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3703
|
+
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
});
|
|
3707
|
+
activeTraceStates.delete(traceId);
|
|
3708
|
+
if (persistenceCollector) {
|
|
3709
|
+
await completionPromise;
|
|
3710
|
+
}
|
|
3541
3711
|
} else {
|
|
3542
|
-
pendingSpanPromises.
|
|
3712
|
+
const pending = pendingSpanPromises.get(traceId);
|
|
3713
|
+
if (pending) {
|
|
3714
|
+
pending.push(spanPromise);
|
|
3715
|
+
} else {
|
|
3716
|
+
pendingSpanPromises.set(traceId, [spanPromise]);
|
|
3717
|
+
}
|
|
3543
3718
|
}
|
|
3719
|
+
} catch {
|
|
3720
|
+
} finally {
|
|
3721
|
+
resolvePersistence?.();
|
|
3544
3722
|
}
|
|
3545
|
-
}
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
if (fnReturnsPromise) {
|
|
3570
|
-
return Promise.resolve(output);
|
|
3723
|
+
};
|
|
3724
|
+
const replayCtxForMock = getReplayContext();
|
|
3725
|
+
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
3726
|
+
const counters = replayCtxForMock.callCounters;
|
|
3727
|
+
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
3728
|
+
const callIndex = counters.get(counterKey) ?? 0;
|
|
3729
|
+
counters.set(counterKey, callIndex + 1);
|
|
3730
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
3731
|
+
if (shouldMock) {
|
|
3732
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
3733
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
3734
|
+
if (mockSpan) {
|
|
3735
|
+
let output = mockSpan.output;
|
|
3736
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
3737
|
+
output = deserializeValue({
|
|
3738
|
+
json: mockSpan.output,
|
|
3739
|
+
meta: mockSpan.outputMeta
|
|
3740
|
+
});
|
|
3741
|
+
}
|
|
3742
|
+
void sendSpan({ result: output });
|
|
3743
|
+
if (fnReturnsPromise) {
|
|
3744
|
+
return Promise.resolve(output);
|
|
3745
|
+
}
|
|
3746
|
+
return output;
|
|
3571
3747
|
}
|
|
3572
|
-
return output;
|
|
3573
3748
|
}
|
|
3574
3749
|
}
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3750
|
+
const recordSpan = (result) => {
|
|
3751
|
+
if (options.finalize) {
|
|
3752
|
+
const replayCtx = getReplayContext();
|
|
3753
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3754
|
+
let resolvePersistence;
|
|
3755
|
+
if (persistenceCollector) {
|
|
3756
|
+
persistenceCollector.push(
|
|
3757
|
+
new Promise((resolve) => {
|
|
3758
|
+
resolvePersistence = resolve;
|
|
3759
|
+
})
|
|
3760
|
+
);
|
|
3761
|
+
}
|
|
3762
|
+
void Promise.resolve().then(() => options.finalize(result)).then(
|
|
3763
|
+
(output) => sendSpan(
|
|
3764
|
+
{ result: output },
|
|
3765
|
+
{ skipPersistenceRegistration: true }
|
|
3766
|
+
)
|
|
3767
|
+
).catch(
|
|
3768
|
+
(error) => sendSpan(
|
|
3769
|
+
{
|
|
3770
|
+
result: void 0,
|
|
3771
|
+
error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
|
|
3772
|
+
},
|
|
3773
|
+
{ skipPersistenceRegistration: true }
|
|
3774
|
+
)
|
|
3775
|
+
).finally(() => resolvePersistence?.());
|
|
3776
|
+
} else {
|
|
3777
|
+
void sendSpan({ result });
|
|
3587
3778
|
}
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
)
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3779
|
+
};
|
|
3780
|
+
executeWithContext = () => {
|
|
3781
|
+
const result = fn(...args);
|
|
3782
|
+
if (result instanceof Promise) {
|
|
3783
|
+
return result.then((resolvedResult) => {
|
|
3784
|
+
recordSpan(resolvedResult);
|
|
3785
|
+
return resolvedResult;
|
|
3786
|
+
}).catch((error) => {
|
|
3787
|
+
void sendSpan({
|
|
3596
3788
|
result: void 0,
|
|
3597
|
-
error: error instanceof Error ?
|
|
3598
|
-
}
|
|
3599
|
-
|
|
3600
|
-
)
|
|
3601
|
-
).finally(() => resolvePersistence?.());
|
|
3602
|
-
} else {
|
|
3603
|
-
void sendSpan({ result });
|
|
3604
|
-
}
|
|
3605
|
-
};
|
|
3606
|
-
const executeWithContext = () => {
|
|
3607
|
-
const result = fn(...args);
|
|
3608
|
-
if (result instanceof Promise) {
|
|
3609
|
-
return result.then((resolvedResult) => {
|
|
3610
|
-
recordSpan(resolvedResult);
|
|
3611
|
-
return resolvedResult;
|
|
3612
|
-
}).catch((error) => {
|
|
3613
|
-
void sendSpan({
|
|
3614
|
-
result: void 0,
|
|
3615
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3789
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3790
|
+
});
|
|
3791
|
+
throw error;
|
|
3616
3792
|
});
|
|
3617
|
-
|
|
3618
|
-
|
|
3793
|
+
}
|
|
3794
|
+
if (isAsyncGenerator(result)) {
|
|
3795
|
+
return wrapAsyncGenerator(result, newStack, sendSpan);
|
|
3796
|
+
}
|
|
3797
|
+
recordSpan(result);
|
|
3798
|
+
return result;
|
|
3799
|
+
};
|
|
3800
|
+
} catch (setupError) {
|
|
3801
|
+
if (registeredTraceId) {
|
|
3802
|
+
activeTraceStates.delete(registeredTraceId);
|
|
3803
|
+
pendingSpanPromises.delete(registeredTraceId);
|
|
3619
3804
|
}
|
|
3620
|
-
if (
|
|
3621
|
-
|
|
3805
|
+
if (getReplayContext()) {
|
|
3806
|
+
throw setupError;
|
|
3622
3807
|
}
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3808
|
+
warnOnce(
|
|
3809
|
+
`withSpan-setup:${traceFunctionKey}`,
|
|
3810
|
+
`tracing setup failed for "${traceFunctionKey}"; running it untraced. The function still runs and returns normally; no span is recorded.`
|
|
3811
|
+
);
|
|
3812
|
+
return fn(...args);
|
|
3813
|
+
}
|
|
3626
3814
|
return runWithSpanStack(newStack, executeWithContext);
|
|
3627
3815
|
};
|
|
3628
3816
|
Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
|