@bitfab/sdk 0.23.3 → 0.24.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-EQI6ZJC3.js → chunk-26MUT4IP.js} +57 -2
- package/dist/chunk-26MUT4IP.js.map +1 -0
- package/dist/{chunk-CG6LVLBK.js → chunk-J4D6PRM4.js} +303 -203
- package/dist/chunk-J4D6PRM4.js.map +1 -0
- package/dist/index.cjs +382 -203
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -8
- package/dist/index.d.ts +18 -8
- package/dist/index.js +2 -2
- package/dist/node.cjs +382 -203
- 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-NMQA7XY6.js} +12 -4
- package/dist/replay-NMQA7XY6.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-CG6LVLBK.js.map +0 -1
- package/dist/chunk-EQI6ZJC3.js.map +0 -1
- package/dist/replay-V6RPJYXZ.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -45,6 +45,54 @@ var init_errors = __esm({
|
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
// src/warnOnce.ts
|
|
49
|
+
function warnOnce(key, message) {
|
|
50
|
+
if (warned.has(key)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
warned.add(key);
|
|
54
|
+
try {
|
|
55
|
+
console.warn(`[bitfab] ${message}`);
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
var warned;
|
|
60
|
+
var init_warnOnce = __esm({
|
|
61
|
+
"src/warnOnce.ts"() {
|
|
62
|
+
"use strict";
|
|
63
|
+
warned = /* @__PURE__ */ new Set();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// src/randomUuid.ts
|
|
68
|
+
function randomUuid() {
|
|
69
|
+
const globalCrypto = globalThis.crypto;
|
|
70
|
+
if (typeof globalCrypto?.randomUUID === "function") {
|
|
71
|
+
try {
|
|
72
|
+
return globalCrypto.randomUUID();
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
warnOnce(
|
|
77
|
+
"crypto-unavailable",
|
|
78
|
+
"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
|
|
79
|
+
);
|
|
80
|
+
return fallbackUuidV4();
|
|
81
|
+
}
|
|
82
|
+
function fallbackUuidV4() {
|
|
83
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
84
|
+
const rand = Math.random() * 16 | 0;
|
|
85
|
+
const value = char === "x" ? rand : rand & 3 | 8;
|
|
86
|
+
return value.toString(16);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
var init_randomUuid = __esm({
|
|
90
|
+
"src/randomUuid.ts"() {
|
|
91
|
+
"use strict";
|
|
92
|
+
init_warnOnce();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
48
96
|
// src/serialize.ts
|
|
49
97
|
function describeValue(value) {
|
|
50
98
|
try {
|
|
@@ -57,6 +105,10 @@ function describeValue(value) {
|
|
|
57
105
|
return typeof value;
|
|
58
106
|
}
|
|
59
107
|
function unserializableStub(value, reason) {
|
|
108
|
+
warnOnce(
|
|
109
|
+
`serialize:${reason.replace(/\d+/g, "N")}`,
|
|
110
|
+
`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.`
|
|
111
|
+
);
|
|
60
112
|
let summary;
|
|
61
113
|
try {
|
|
62
114
|
summary = `<unserializable: ${describeValue(value)} (${reason})>`;
|
|
@@ -96,7 +148,19 @@ function deserializeValue(serialized) {
|
|
|
96
148
|
});
|
|
97
149
|
}
|
|
98
150
|
function toJsonSafe(value) {
|
|
99
|
-
|
|
151
|
+
const safe = toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet());
|
|
152
|
+
try {
|
|
153
|
+
const size = JSON.stringify(safe)?.length ?? 0;
|
|
154
|
+
if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {
|
|
155
|
+
warnOnce(
|
|
156
|
+
"toJsonSafe:too_large",
|
|
157
|
+
`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.`
|
|
158
|
+
);
|
|
159
|
+
return `<unserializable: too_large_${size}_bytes>`;
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
return safe;
|
|
100
164
|
}
|
|
101
165
|
function toJsonSafeInner(value, depth, seen) {
|
|
102
166
|
if (value === null || value === void 0) {
|
|
@@ -149,12 +213,14 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
149
213
|
seen.delete(value);
|
|
150
214
|
return result;
|
|
151
215
|
}
|
|
152
|
-
var import_superjson, MAX_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
216
|
+
var import_superjson, MAX_SERIALIZED_BYTES, MAX_FRAMEWORK_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
153
217
|
var init_serialize = __esm({
|
|
154
218
|
"src/serialize.ts"() {
|
|
155
219
|
"use strict";
|
|
156
220
|
import_superjson = __toESM(require("superjson"), 1);
|
|
221
|
+
init_warnOnce();
|
|
157
222
|
MAX_SERIALIZED_BYTES = 512e3;
|
|
223
|
+
MAX_FRAMEWORK_SERIALIZED_BYTES = 2e6;
|
|
158
224
|
MAX_SAFE_DEPTH = 6;
|
|
159
225
|
}
|
|
160
226
|
});
|
|
@@ -279,7 +345,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
279
345
|
let originalOutput;
|
|
280
346
|
let result;
|
|
281
347
|
let error = null;
|
|
282
|
-
const replayedTraceId =
|
|
348
|
+
const replayedTraceId = randomUuid();
|
|
283
349
|
const pendingPersistence = [];
|
|
284
350
|
try {
|
|
285
351
|
const span = await httpClient.getExternalSpan(serverItem.externalSpanId);
|
|
@@ -339,7 +405,10 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
339
405
|
originalOutput,
|
|
340
406
|
error,
|
|
341
407
|
durationMs: serverItem.durationMs ?? null,
|
|
342
|
-
|
|
408
|
+
// Filled in by replay() from the complete-replay response once the
|
|
409
|
+
// replay traces are persisted and their spans aggregated server-side.
|
|
410
|
+
// Null here (and on older servers) means "replay tokens not known".
|
|
411
|
+
tokens: null,
|
|
343
412
|
model: serverItem.model ?? null,
|
|
344
413
|
dbSnapshotRef: serverItem.dbSnapshotRef ?? null
|
|
345
414
|
};
|
|
@@ -413,6 +482,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
413
482
|
const resultItems = await mapWithConcurrency(tasks, maxConcurrency);
|
|
414
483
|
const completeResult = await httpClient.completeReplay(testRunId);
|
|
415
484
|
const serverTraceIds = completeResult.traceIds;
|
|
485
|
+
const replayTokens = completeResult.tokens;
|
|
416
486
|
if (serverTraceIds === void 0) {
|
|
417
487
|
try {
|
|
418
488
|
console.warn(
|
|
@@ -435,6 +505,9 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
435
505
|
missing.push(item.traceId);
|
|
436
506
|
}
|
|
437
507
|
}
|
|
508
|
+
if (mapped !== void 0) {
|
|
509
|
+
item.tokens = replayTokens?.[mapped] ?? null;
|
|
510
|
+
}
|
|
438
511
|
item.traceId = mapped ?? null;
|
|
439
512
|
}
|
|
440
513
|
}
|
|
@@ -463,6 +536,7 @@ var init_replay = __esm({
|
|
|
463
536
|
"src/replay.ts"() {
|
|
464
537
|
"use strict";
|
|
465
538
|
init_errors();
|
|
539
|
+
init_randomUuid();
|
|
466
540
|
init_replayContext();
|
|
467
541
|
init_serialize();
|
|
468
542
|
}
|
|
@@ -492,13 +566,24 @@ __export(index_exports, {
|
|
|
492
566
|
module.exports = __toCommonJS(index_exports);
|
|
493
567
|
|
|
494
568
|
// src/version.generated.ts
|
|
495
|
-
var __version__ = "0.
|
|
569
|
+
var __version__ = "0.24.1";
|
|
496
570
|
|
|
497
571
|
// src/constants.ts
|
|
498
572
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
499
573
|
|
|
500
574
|
// src/http.ts
|
|
501
575
|
init_errors();
|
|
576
|
+
|
|
577
|
+
// src/unrefTimer.ts
|
|
578
|
+
function unrefTimer(timer) {
|
|
579
|
+
const handle = timer;
|
|
580
|
+
if (typeof handle.unref === "function") {
|
|
581
|
+
handle.unref();
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/http.ts
|
|
586
|
+
init_warnOnce();
|
|
502
587
|
function serializePayloadBody(payload) {
|
|
503
588
|
try {
|
|
504
589
|
return { body: JSON.stringify(payload), dropped: [] };
|
|
@@ -543,11 +628,20 @@ function serializePayloadBody(payload) {
|
|
|
543
628
|
result = `<unserializable: ${className}>`;
|
|
544
629
|
}
|
|
545
630
|
} else {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
631
|
+
try {
|
|
632
|
+
const out = {};
|
|
633
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
634
|
+
out[k] = sanitize(v, seen);
|
|
635
|
+
}
|
|
636
|
+
result = out;
|
|
637
|
+
} catch {
|
|
638
|
+
warnOnce(
|
|
639
|
+
"payload:field-getter-threw",
|
|
640
|
+
"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."
|
|
641
|
+
);
|
|
642
|
+
dropped.push(className);
|
|
643
|
+
result = `<unserializable: ${className}>`;
|
|
549
644
|
}
|
|
550
|
-
result = out;
|
|
551
645
|
}
|
|
552
646
|
seen.delete(obj);
|
|
553
647
|
return result;
|
|
@@ -592,10 +686,20 @@ async function flushTraces(timeoutMs = 5e3) {
|
|
|
592
686
|
if (pendingTracePromises.size === 0) {
|
|
593
687
|
return;
|
|
594
688
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
689
|
+
let timer;
|
|
690
|
+
try {
|
|
691
|
+
await Promise.race([
|
|
692
|
+
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
693
|
+
new Promise((resolve) => {
|
|
694
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
695
|
+
unrefTimer(timer);
|
|
696
|
+
})
|
|
697
|
+
]);
|
|
698
|
+
} finally {
|
|
699
|
+
if (timer) {
|
|
700
|
+
clearTimeout(timer);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
599
703
|
}
|
|
600
704
|
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
601
705
|
let isFlushing = false;
|
|
@@ -905,6 +1009,7 @@ var HttpClient = class {
|
|
|
905
1009
|
};
|
|
906
1010
|
|
|
907
1011
|
// src/claudeAgentSdk.ts
|
|
1012
|
+
init_randomUuid();
|
|
908
1013
|
init_serialize();
|
|
909
1014
|
function nowIso() {
|
|
910
1015
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -992,7 +1097,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
992
1097
|
if (this.activeContext) {
|
|
993
1098
|
this.traceId = this.activeContext.traceId;
|
|
994
1099
|
} else {
|
|
995
|
-
this.traceId =
|
|
1100
|
+
this.traceId = randomUuid();
|
|
996
1101
|
}
|
|
997
1102
|
this.traceStartedAt = nowIso();
|
|
998
1103
|
return this.traceId;
|
|
@@ -1017,7 +1122,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1017
1122
|
if (this.activeContext !== null) {
|
|
1018
1123
|
return;
|
|
1019
1124
|
}
|
|
1020
|
-
const spanId =
|
|
1125
|
+
const spanId = randomUuid();
|
|
1021
1126
|
this.startSpan(spanId, this.traceFunctionKey, "agent", this.rootInput, null);
|
|
1022
1127
|
this.rootSpanId = spanId;
|
|
1023
1128
|
}
|
|
@@ -1131,7 +1236,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1131
1236
|
// ── hook callbacks ───────────────────────────────────────────
|
|
1132
1237
|
async preToolUseHook(inputData, toolUseId, _context) {
|
|
1133
1238
|
try {
|
|
1134
|
-
const sid = inputData.tool_use_id ?? toolUseId ??
|
|
1239
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? randomUuid();
|
|
1135
1240
|
const toolName = inputData.tool_name ?? "tool";
|
|
1136
1241
|
const toolInput = inputData.tool_input ?? {};
|
|
1137
1242
|
const agentId = inputData.agent_id;
|
|
@@ -1161,10 +1266,10 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1161
1266
|
}
|
|
1162
1267
|
async subagentStartHook(inputData, _toolUseId, _context) {
|
|
1163
1268
|
try {
|
|
1164
|
-
const agentId = inputData.agent_id ??
|
|
1269
|
+
const agentId = inputData.agent_id ?? randomUuid();
|
|
1165
1270
|
const agentType = inputData.agent_type ?? "subagent";
|
|
1166
1271
|
const parentId = this.getParentId();
|
|
1167
|
-
const spanId =
|
|
1272
|
+
const spanId = randomUuid();
|
|
1168
1273
|
this.activeSubagentSpans.set(agentId, spanId);
|
|
1169
1274
|
this.startSpan(
|
|
1170
1275
|
spanId,
|
|
@@ -1305,7 +1410,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1305
1410
|
this.flushLlmTurn();
|
|
1306
1411
|
this.conversationHistory.push(...this.pendingMessages);
|
|
1307
1412
|
this.pendingMessages = [];
|
|
1308
|
-
this.currentLlmSpanId =
|
|
1413
|
+
this.currentLlmSpanId = randomUuid();
|
|
1309
1414
|
this.currentLlmMessageId = messageId ?? null;
|
|
1310
1415
|
this.currentLlmContent = [];
|
|
1311
1416
|
this.currentLlmModel = inner.model ?? null;
|
|
@@ -1749,6 +1854,7 @@ function buildSnapshotRef(config, sdkWallClockBeforeFn) {
|
|
|
1749
1854
|
}
|
|
1750
1855
|
|
|
1751
1856
|
// src/langgraph.ts
|
|
1857
|
+
init_randomUuid();
|
|
1752
1858
|
init_serialize();
|
|
1753
1859
|
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
1754
1860
|
var LANGGRAPH_METADATA_KEYS = [
|
|
@@ -1997,7 +2103,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
1997
2103
|
} else {
|
|
1998
2104
|
const activeContext = this.getActiveSpanContext?.() ?? null;
|
|
1999
2105
|
invocation = {
|
|
2000
|
-
traceId: activeContext ? activeContext.traceId :
|
|
2106
|
+
traceId: activeContext ? activeContext.traceId : randomUuid(),
|
|
2001
2107
|
activeContext,
|
|
2002
2108
|
rootRunId: runId
|
|
2003
2109
|
};
|
|
@@ -2292,6 +2398,20 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2292
2398
|
this.withSpanFn = config.withSpan;
|
|
2293
2399
|
this.getActiveSpanContext = config.getActiveSpanContext;
|
|
2294
2400
|
}
|
|
2401
|
+
/**
|
|
2402
|
+
* Drop-in replacement for the OpenAI Agents SDK's `run()` that records a
|
|
2403
|
+
* replayable root `agent` span.
|
|
2404
|
+
*
|
|
2405
|
+
* The `input` is captured as the root span's input (as a single positional
|
|
2406
|
+
* argument, so `replay(key, fn)` re-feeds it), and the run's `finalOutput`
|
|
2407
|
+
* is recorded as the root output. For streaming runs (`{ stream: true }`),
|
|
2408
|
+
* the result is handed back immediately and the final output is recorded
|
|
2409
|
+
* once the stream completes — first-byte latency is untouched.
|
|
2410
|
+
*
|
|
2411
|
+
* The process-wide tracing processor (`getOpenAiTracingProcessor`) must still
|
|
2412
|
+
* be registered: it captures the LLM/tool/handoff spans that nest beneath
|
|
2413
|
+
* this root.
|
|
2414
|
+
*/
|
|
2295
2415
|
async wrapRun(agent, input, options) {
|
|
2296
2416
|
const { run } = await importOptionalPeer([
|
|
2297
2417
|
"@openai",
|
|
@@ -2330,6 +2450,7 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2330
2450
|
};
|
|
2331
2451
|
|
|
2332
2452
|
// src/client.ts
|
|
2453
|
+
init_randomUuid();
|
|
2333
2454
|
init_replayContext();
|
|
2334
2455
|
|
|
2335
2456
|
// src/replayEnvironment.ts
|
|
@@ -2749,6 +2870,7 @@ var BitfabVercelAiHandler = class {
|
|
|
2749
2870
|
};
|
|
2750
2871
|
|
|
2751
2872
|
// src/client.ts
|
|
2873
|
+
init_warnOnce();
|
|
2752
2874
|
var activeTraceStates = /* @__PURE__ */ new Map();
|
|
2753
2875
|
var pendingSpanPromises = /* @__PURE__ */ new Map();
|
|
2754
2876
|
var asyncLocalStorage = null;
|
|
@@ -3107,7 +3229,20 @@ var Bitfab = class {
|
|
|
3107
3229
|
functionVersion.providers,
|
|
3108
3230
|
this.envVars
|
|
3109
3231
|
);
|
|
3110
|
-
|
|
3232
|
+
let resultStr;
|
|
3233
|
+
if (typeof executionResult.result === "string") {
|
|
3234
|
+
resultStr = executionResult.result;
|
|
3235
|
+
} else {
|
|
3236
|
+
try {
|
|
3237
|
+
resultStr = JSON.stringify(executionResult.result) ?? String(executionResult.result);
|
|
3238
|
+
} catch {
|
|
3239
|
+
warnOnce(
|
|
3240
|
+
"call-result-serialize",
|
|
3241
|
+
"a local execution result could not be JSON-serialized; storing its String() form instead. The call still returns its real value."
|
|
3242
|
+
);
|
|
3243
|
+
resultStr = String(executionResult.result);
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3111
3246
|
this.httpClient.sendInternalTrace(functionVersion.id, {
|
|
3112
3247
|
result: resultStr,
|
|
3113
3248
|
source: "typescript-sdk",
|
|
@@ -3356,11 +3491,26 @@ var Bitfab = class {
|
|
|
3356
3491
|
return await bamlClient[methodName](...args);
|
|
3357
3492
|
}
|
|
3358
3493
|
const collector = new CollectorClass("bitfab-baml-tracing");
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
trackedClient
|
|
3363
|
-
|
|
3494
|
+
let trackedClient;
|
|
3495
|
+
let trackedMethod;
|
|
3496
|
+
try {
|
|
3497
|
+
trackedClient = bamlClient.withOptions({ collector });
|
|
3498
|
+
const method2 = trackedClient[methodName];
|
|
3499
|
+
if (typeof method2 !== "function") {
|
|
3500
|
+
throw new BitfabError(
|
|
3501
|
+
"bamlClient.withOptions did not return the wrapped method"
|
|
3502
|
+
);
|
|
3503
|
+
}
|
|
3504
|
+
trackedMethod = method2;
|
|
3505
|
+
} catch {
|
|
3506
|
+
warnOnce(
|
|
3507
|
+
`wrapBAML-setup:${methodName}`,
|
|
3508
|
+
`BAML tracing setup failed for "${methodName}" (incompatible bamlClient or BAML version); calling it untraced. The call still runs; no span is recorded.`
|
|
3509
|
+
);
|
|
3510
|
+
wrappedFn.collector = null;
|
|
3511
|
+
return await bamlClient[methodName](...args);
|
|
3512
|
+
}
|
|
3513
|
+
const result = await trackedMethod.bind(trackedClient)(...args);
|
|
3364
3514
|
wrappedFn.collector = collector;
|
|
3365
3515
|
try {
|
|
3366
3516
|
const prompt = extractPromptFromCollector(collector);
|
|
@@ -3436,200 +3586,229 @@ var Bitfab = class {
|
|
|
3436
3586
|
() => wrappedFn.apply(this, args)
|
|
3437
3587
|
);
|
|
3438
3588
|
}
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
const
|
|
3453
|
-
|
|
3589
|
+
let newStack;
|
|
3590
|
+
let executeWithContext;
|
|
3591
|
+
let registeredTraceId;
|
|
3592
|
+
try {
|
|
3593
|
+
const currentStack = getSpanStack();
|
|
3594
|
+
const parentContext = currentStack[currentStack.length - 1];
|
|
3595
|
+
const replayCtxForTraceId = parentContext ? null : getReplayContext();
|
|
3596
|
+
const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? randomUuid();
|
|
3597
|
+
const spanId = randomUuid();
|
|
3598
|
+
const parentSpanId = parentContext?.spanId ?? null;
|
|
3599
|
+
const isRootSpan = parentSpanId === null;
|
|
3600
|
+
const newContext = { traceId, spanId, contexts: [] };
|
|
3601
|
+
newStack = [...currentStack, newContext];
|
|
3602
|
+
const inputs = args;
|
|
3603
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3604
|
+
if (isRootSpan && !activeTraceStates.has(traceId)) {
|
|
3605
|
+
const replayCtxAtRoot = getReplayContext();
|
|
3606
|
+
const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
|
|
3607
|
+
activeTraceStates.set(traceId, {
|
|
3608
|
+
traceId,
|
|
3609
|
+
startedAt,
|
|
3610
|
+
contexts: [],
|
|
3611
|
+
...replayCtxAtRoot?.testRunId && {
|
|
3612
|
+
testRunId: replayCtxAtRoot.testRunId
|
|
3613
|
+
},
|
|
3614
|
+
...replayCtxAtRoot?.inputSourceTraceId && {
|
|
3615
|
+
inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
|
|
3616
|
+
},
|
|
3617
|
+
dbSnapshotRef
|
|
3618
|
+
});
|
|
3619
|
+
pendingSpanPromises.set(traceId, []);
|
|
3620
|
+
registeredTraceId = traceId;
|
|
3621
|
+
}
|
|
3622
|
+
const functionName = fn.name !== "" ? fn.name : void 0;
|
|
3623
|
+
const baseSpanParams = {
|
|
3624
|
+
traceFunctionKey,
|
|
3625
|
+
functionName,
|
|
3626
|
+
spanName: options.name ?? functionName ?? traceFunctionKey,
|
|
3454
3627
|
traceId,
|
|
3628
|
+
spanId,
|
|
3629
|
+
parentSpanId,
|
|
3630
|
+
inputs,
|
|
3455
3631
|
startedAt,
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
startedAt,
|
|
3477
|
-
spanType: options.type ?? "custom"
|
|
3478
|
-
};
|
|
3479
|
-
const sendSpan = async (params, spanOpts) => {
|
|
3480
|
-
const replayCtx = getReplayContext();
|
|
3481
|
-
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3482
|
-
let resolvePersistence;
|
|
3483
|
-
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3484
|
-
persistenceCollector.push(
|
|
3485
|
-
new Promise((resolve) => {
|
|
3486
|
-
resolvePersistence = resolve;
|
|
3487
|
-
})
|
|
3488
|
-
);
|
|
3489
|
-
}
|
|
3490
|
-
try {
|
|
3491
|
-
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3492
|
-
const spanPromise = self.sendWrapperSpan({
|
|
3493
|
-
...baseSpanParams,
|
|
3494
|
-
...params,
|
|
3495
|
-
contexts: newContext.contexts,
|
|
3496
|
-
prompt: newContext.prompt,
|
|
3497
|
-
endedAt,
|
|
3498
|
-
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3499
|
-
...replayCtx?.inputSourceSpanId && {
|
|
3500
|
-
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3501
|
-
}
|
|
3502
|
-
});
|
|
3503
|
-
if (isRootSpan) {
|
|
3504
|
-
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3505
|
-
pending.push(spanPromise);
|
|
3506
|
-
if (persistenceCollector) {
|
|
3507
|
-
await Promise.allSettled(pending);
|
|
3508
|
-
} else {
|
|
3509
|
-
await Promise.race([
|
|
3510
|
-
Promise.allSettled(pending),
|
|
3511
|
-
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
3512
|
-
]);
|
|
3513
|
-
}
|
|
3514
|
-
pendingSpanPromises.delete(traceId);
|
|
3515
|
-
const traceState = activeTraceStates.get(traceId);
|
|
3516
|
-
const completionPromise = self.sendTraceCompletion({
|
|
3517
|
-
traceFunctionKey,
|
|
3518
|
-
traceId,
|
|
3519
|
-
startedAt: traceState?.startedAt ?? startedAt,
|
|
3632
|
+
spanType: options.type ?? "custom"
|
|
3633
|
+
};
|
|
3634
|
+
const sendSpan = async (params, spanOpts) => {
|
|
3635
|
+
const replayCtx = getReplayContext();
|
|
3636
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3637
|
+
let resolvePersistence;
|
|
3638
|
+
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3639
|
+
persistenceCollector.push(
|
|
3640
|
+
new Promise((resolve) => {
|
|
3641
|
+
resolvePersistence = resolve;
|
|
3642
|
+
})
|
|
3643
|
+
);
|
|
3644
|
+
}
|
|
3645
|
+
try {
|
|
3646
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3647
|
+
const spanPromise = self.sendWrapperSpan({
|
|
3648
|
+
...baseSpanParams,
|
|
3649
|
+
...params,
|
|
3650
|
+
contexts: newContext.contexts,
|
|
3651
|
+
prompt: newContext.prompt,
|
|
3520
3652
|
endedAt,
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
testRunId: traceState?.testRunId,
|
|
3525
|
-
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3526
|
-
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3527
|
-
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3528
|
-
// whether customer code obtained the branch URL during this
|
|
3529
|
-
// item. Omitted entirely when no lease was attached, so the
|
|
3530
|
-
// server can distinguish "no branch" from "branch ignored".
|
|
3531
|
-
...replayCtx?.dbBranchLease && {
|
|
3532
|
-
dbSnapshotUsage: {
|
|
3533
|
-
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3534
|
-
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3535
|
-
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3536
|
-
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3537
|
-
}
|
|
3653
|
+
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3654
|
+
...replayCtx?.inputSourceSpanId && {
|
|
3655
|
+
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3538
3656
|
}
|
|
3539
3657
|
});
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
await completionPromise;
|
|
3543
|
-
}
|
|
3544
|
-
} else {
|
|
3545
|
-
const pending = pendingSpanPromises.get(traceId);
|
|
3546
|
-
if (pending) {
|
|
3658
|
+
if (isRootSpan) {
|
|
3659
|
+
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3547
3660
|
pending.push(spanPromise);
|
|
3661
|
+
if (persistenceCollector) {
|
|
3662
|
+
await Promise.allSettled(pending);
|
|
3663
|
+
} else {
|
|
3664
|
+
let raceTimer;
|
|
3665
|
+
try {
|
|
3666
|
+
await Promise.race([
|
|
3667
|
+
Promise.allSettled(pending),
|
|
3668
|
+
new Promise((resolve) => {
|
|
3669
|
+
raceTimer = setTimeout(resolve, 5e3);
|
|
3670
|
+
unrefTimer(raceTimer);
|
|
3671
|
+
})
|
|
3672
|
+
]);
|
|
3673
|
+
} finally {
|
|
3674
|
+
if (raceTimer) {
|
|
3675
|
+
clearTimeout(raceTimer);
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
pendingSpanPromises.delete(traceId);
|
|
3680
|
+
const traceState = activeTraceStates.get(traceId);
|
|
3681
|
+
const completionPromise = self.sendTraceCompletion({
|
|
3682
|
+
traceFunctionKey,
|
|
3683
|
+
traceId,
|
|
3684
|
+
startedAt: traceState?.startedAt ?? startedAt,
|
|
3685
|
+
endedAt,
|
|
3686
|
+
sessionId: traceState?.sessionId,
|
|
3687
|
+
metadata: traceState?.metadata,
|
|
3688
|
+
contexts: traceState?.contexts ?? [],
|
|
3689
|
+
testRunId: traceState?.testRunId,
|
|
3690
|
+
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3691
|
+
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3692
|
+
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3693
|
+
// whether customer code obtained the branch URL during this
|
|
3694
|
+
// item. Omitted entirely when no lease was attached, so the
|
|
3695
|
+
// server can distinguish "no branch" from "branch ignored".
|
|
3696
|
+
...replayCtx?.dbBranchLease && {
|
|
3697
|
+
dbSnapshotUsage: {
|
|
3698
|
+
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3699
|
+
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3700
|
+
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3701
|
+
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
});
|
|
3705
|
+
activeTraceStates.delete(traceId);
|
|
3706
|
+
if (persistenceCollector) {
|
|
3707
|
+
await completionPromise;
|
|
3708
|
+
}
|
|
3548
3709
|
} else {
|
|
3549
|
-
pendingSpanPromises.
|
|
3710
|
+
const pending = pendingSpanPromises.get(traceId);
|
|
3711
|
+
if (pending) {
|
|
3712
|
+
pending.push(spanPromise);
|
|
3713
|
+
} else {
|
|
3714
|
+
pendingSpanPromises.set(traceId, [spanPromise]);
|
|
3715
|
+
}
|
|
3550
3716
|
}
|
|
3717
|
+
} catch {
|
|
3718
|
+
} finally {
|
|
3719
|
+
resolvePersistence?.();
|
|
3551
3720
|
}
|
|
3552
|
-
}
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
if (fnReturnsPromise) {
|
|
3577
|
-
return Promise.resolve(output);
|
|
3721
|
+
};
|
|
3722
|
+
const replayCtxForMock = getReplayContext();
|
|
3723
|
+
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
3724
|
+
const counters = replayCtxForMock.callCounters;
|
|
3725
|
+
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
3726
|
+
const callIndex = counters.get(counterKey) ?? 0;
|
|
3727
|
+
counters.set(counterKey, callIndex + 1);
|
|
3728
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
3729
|
+
if (shouldMock) {
|
|
3730
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
3731
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
3732
|
+
if (mockSpan) {
|
|
3733
|
+
let output = mockSpan.output;
|
|
3734
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
3735
|
+
output = deserializeValue({
|
|
3736
|
+
json: mockSpan.output,
|
|
3737
|
+
meta: mockSpan.outputMeta
|
|
3738
|
+
});
|
|
3739
|
+
}
|
|
3740
|
+
void sendSpan({ result: output });
|
|
3741
|
+
if (fnReturnsPromise) {
|
|
3742
|
+
return Promise.resolve(output);
|
|
3743
|
+
}
|
|
3744
|
+
return output;
|
|
3578
3745
|
}
|
|
3579
|
-
return output;
|
|
3580
3746
|
}
|
|
3581
3747
|
}
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3748
|
+
const recordSpan = (result) => {
|
|
3749
|
+
if (options.finalize) {
|
|
3750
|
+
const replayCtx = getReplayContext();
|
|
3751
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3752
|
+
let resolvePersistence;
|
|
3753
|
+
if (persistenceCollector) {
|
|
3754
|
+
persistenceCollector.push(
|
|
3755
|
+
new Promise((resolve) => {
|
|
3756
|
+
resolvePersistence = resolve;
|
|
3757
|
+
})
|
|
3758
|
+
);
|
|
3759
|
+
}
|
|
3760
|
+
void Promise.resolve().then(() => options.finalize(result)).then(
|
|
3761
|
+
(output) => sendSpan(
|
|
3762
|
+
{ result: output },
|
|
3763
|
+
{ skipPersistenceRegistration: true }
|
|
3764
|
+
)
|
|
3765
|
+
).catch(
|
|
3766
|
+
(error) => sendSpan(
|
|
3767
|
+
{
|
|
3768
|
+
result: void 0,
|
|
3769
|
+
error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
|
|
3770
|
+
},
|
|
3771
|
+
{ skipPersistenceRegistration: true }
|
|
3772
|
+
)
|
|
3773
|
+
).finally(() => resolvePersistence?.());
|
|
3774
|
+
} else {
|
|
3775
|
+
void sendSpan({ result });
|
|
3594
3776
|
}
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
)
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3777
|
+
};
|
|
3778
|
+
executeWithContext = () => {
|
|
3779
|
+
const result = fn(...args);
|
|
3780
|
+
if (result instanceof Promise) {
|
|
3781
|
+
return result.then((resolvedResult) => {
|
|
3782
|
+
recordSpan(resolvedResult);
|
|
3783
|
+
return resolvedResult;
|
|
3784
|
+
}).catch((error) => {
|
|
3785
|
+
void sendSpan({
|
|
3603
3786
|
result: void 0,
|
|
3604
|
-
error: error instanceof Error ?
|
|
3605
|
-
}
|
|
3606
|
-
|
|
3607
|
-
)
|
|
3608
|
-
).finally(() => resolvePersistence?.());
|
|
3609
|
-
} else {
|
|
3610
|
-
void sendSpan({ result });
|
|
3611
|
-
}
|
|
3612
|
-
};
|
|
3613
|
-
const executeWithContext = () => {
|
|
3614
|
-
const result = fn(...args);
|
|
3615
|
-
if (result instanceof Promise) {
|
|
3616
|
-
return result.then((resolvedResult) => {
|
|
3617
|
-
recordSpan(resolvedResult);
|
|
3618
|
-
return resolvedResult;
|
|
3619
|
-
}).catch((error) => {
|
|
3620
|
-
void sendSpan({
|
|
3621
|
-
result: void 0,
|
|
3622
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3787
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3788
|
+
});
|
|
3789
|
+
throw error;
|
|
3623
3790
|
});
|
|
3624
|
-
|
|
3625
|
-
|
|
3791
|
+
}
|
|
3792
|
+
if (isAsyncGenerator(result)) {
|
|
3793
|
+
return wrapAsyncGenerator(result, newStack, sendSpan);
|
|
3794
|
+
}
|
|
3795
|
+
recordSpan(result);
|
|
3796
|
+
return result;
|
|
3797
|
+
};
|
|
3798
|
+
} catch (setupError) {
|
|
3799
|
+
if (registeredTraceId) {
|
|
3800
|
+
activeTraceStates.delete(registeredTraceId);
|
|
3801
|
+
pendingSpanPromises.delete(registeredTraceId);
|
|
3626
3802
|
}
|
|
3627
|
-
if (
|
|
3628
|
-
|
|
3803
|
+
if (getReplayContext()) {
|
|
3804
|
+
throw setupError;
|
|
3629
3805
|
}
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3806
|
+
warnOnce(
|
|
3807
|
+
`withSpan-setup:${traceFunctionKey}`,
|
|
3808
|
+
`tracing setup failed for "${traceFunctionKey}"; running it untraced. The function still runs and returns normally; no span is recorded.`
|
|
3809
|
+
);
|
|
3810
|
+
return fn(...args);
|
|
3811
|
+
}
|
|
3633
3812
|
return runWithSpanStack(newStack, executeWithContext);
|
|
3634
3813
|
};
|
|
3635
3814
|
Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
|