bitfab 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
|
@@ -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);
|
|
@@ -348,7 +414,10 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
348
414
|
originalOutput,
|
|
349
415
|
error,
|
|
350
416
|
durationMs: serverItem.durationMs ?? null,
|
|
351
|
-
|
|
417
|
+
// Filled in by replay() from the complete-replay response once the
|
|
418
|
+
// replay traces are persisted and their spans aggregated server-side.
|
|
419
|
+
// Null here (and on older servers) means "replay tokens not known".
|
|
420
|
+
tokens: null,
|
|
352
421
|
model: serverItem.model ?? null,
|
|
353
422
|
dbSnapshotRef: serverItem.dbSnapshotRef ?? null
|
|
354
423
|
};
|
|
@@ -422,6 +491,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
422
491
|
const resultItems = await mapWithConcurrency(tasks, maxConcurrency);
|
|
423
492
|
const completeResult = await httpClient.completeReplay(testRunId);
|
|
424
493
|
const serverTraceIds = completeResult.traceIds;
|
|
494
|
+
const replayTokens = completeResult.tokens;
|
|
425
495
|
if (serverTraceIds === void 0) {
|
|
426
496
|
try {
|
|
427
497
|
console.warn(
|
|
@@ -444,6 +514,9 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
444
514
|
missing.push(item.traceId);
|
|
445
515
|
}
|
|
446
516
|
}
|
|
517
|
+
if (mapped !== void 0) {
|
|
518
|
+
item.tokens = replayTokens?.[mapped] ?? null;
|
|
519
|
+
}
|
|
447
520
|
item.traceId = mapped ?? null;
|
|
448
521
|
}
|
|
449
522
|
}
|
|
@@ -472,6 +545,7 @@ var init_replay = __esm({
|
|
|
472
545
|
"src/replay.ts"() {
|
|
473
546
|
"use strict";
|
|
474
547
|
init_errors();
|
|
548
|
+
init_randomUuid();
|
|
475
549
|
init_replayContext();
|
|
476
550
|
init_serialize();
|
|
477
551
|
}
|
|
@@ -501,13 +575,24 @@ __export(index_exports, {
|
|
|
501
575
|
module.exports = __toCommonJS(index_exports);
|
|
502
576
|
|
|
503
577
|
// src/version.generated.ts
|
|
504
|
-
var __version__ = "0.
|
|
578
|
+
var __version__ = "0.24.1";
|
|
505
579
|
|
|
506
580
|
// src/constants.ts
|
|
507
581
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
508
582
|
|
|
509
583
|
// src/http.ts
|
|
510
584
|
init_errors();
|
|
585
|
+
|
|
586
|
+
// src/unrefTimer.ts
|
|
587
|
+
function unrefTimer(timer) {
|
|
588
|
+
const handle = timer;
|
|
589
|
+
if (typeof handle.unref === "function") {
|
|
590
|
+
handle.unref();
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// src/http.ts
|
|
595
|
+
init_warnOnce();
|
|
511
596
|
function serializePayloadBody(payload) {
|
|
512
597
|
try {
|
|
513
598
|
return { body: JSON.stringify(payload), dropped: [] };
|
|
@@ -552,11 +637,20 @@ function serializePayloadBody(payload) {
|
|
|
552
637
|
result = `<unserializable: ${className}>`;
|
|
553
638
|
}
|
|
554
639
|
} else {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
640
|
+
try {
|
|
641
|
+
const out = {};
|
|
642
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
643
|
+
out[k] = sanitize(v, seen);
|
|
644
|
+
}
|
|
645
|
+
result = out;
|
|
646
|
+
} catch {
|
|
647
|
+
warnOnce(
|
|
648
|
+
"payload:field-getter-threw",
|
|
649
|
+
"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."
|
|
650
|
+
);
|
|
651
|
+
dropped.push(className);
|
|
652
|
+
result = `<unserializable: ${className}>`;
|
|
558
653
|
}
|
|
559
|
-
result = out;
|
|
560
654
|
}
|
|
561
655
|
seen.delete(obj);
|
|
562
656
|
return result;
|
|
@@ -601,10 +695,20 @@ async function flushTraces(timeoutMs = 5e3) {
|
|
|
601
695
|
if (pendingTracePromises.size === 0) {
|
|
602
696
|
return;
|
|
603
697
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
698
|
+
let timer;
|
|
699
|
+
try {
|
|
700
|
+
await Promise.race([
|
|
701
|
+
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
702
|
+
new Promise((resolve) => {
|
|
703
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
704
|
+
unrefTimer(timer);
|
|
705
|
+
})
|
|
706
|
+
]);
|
|
707
|
+
} finally {
|
|
708
|
+
if (timer) {
|
|
709
|
+
clearTimeout(timer);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
608
712
|
}
|
|
609
713
|
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
610
714
|
let isFlushing = false;
|
|
@@ -914,6 +1018,7 @@ var HttpClient = class {
|
|
|
914
1018
|
};
|
|
915
1019
|
|
|
916
1020
|
// src/claudeAgentSdk.ts
|
|
1021
|
+
init_randomUuid();
|
|
917
1022
|
init_serialize();
|
|
918
1023
|
function nowIso() {
|
|
919
1024
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1001,7 +1106,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1001
1106
|
if (this.activeContext) {
|
|
1002
1107
|
this.traceId = this.activeContext.traceId;
|
|
1003
1108
|
} else {
|
|
1004
|
-
this.traceId =
|
|
1109
|
+
this.traceId = randomUuid();
|
|
1005
1110
|
}
|
|
1006
1111
|
this.traceStartedAt = nowIso();
|
|
1007
1112
|
return this.traceId;
|
|
@@ -1026,7 +1131,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1026
1131
|
if (this.activeContext !== null) {
|
|
1027
1132
|
return;
|
|
1028
1133
|
}
|
|
1029
|
-
const spanId =
|
|
1134
|
+
const spanId = randomUuid();
|
|
1030
1135
|
this.startSpan(spanId, this.traceFunctionKey, "agent", this.rootInput, null);
|
|
1031
1136
|
this.rootSpanId = spanId;
|
|
1032
1137
|
}
|
|
@@ -1140,7 +1245,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1140
1245
|
// ── hook callbacks ───────────────────────────────────────────
|
|
1141
1246
|
async preToolUseHook(inputData, toolUseId, _context) {
|
|
1142
1247
|
try {
|
|
1143
|
-
const sid = inputData.tool_use_id ?? toolUseId ??
|
|
1248
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? randomUuid();
|
|
1144
1249
|
const toolName = inputData.tool_name ?? "tool";
|
|
1145
1250
|
const toolInput = inputData.tool_input ?? {};
|
|
1146
1251
|
const agentId = inputData.agent_id;
|
|
@@ -1170,10 +1275,10 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1170
1275
|
}
|
|
1171
1276
|
async subagentStartHook(inputData, _toolUseId, _context) {
|
|
1172
1277
|
try {
|
|
1173
|
-
const agentId = inputData.agent_id ??
|
|
1278
|
+
const agentId = inputData.agent_id ?? randomUuid();
|
|
1174
1279
|
const agentType = inputData.agent_type ?? "subagent";
|
|
1175
1280
|
const parentId = this.getParentId();
|
|
1176
|
-
const spanId =
|
|
1281
|
+
const spanId = randomUuid();
|
|
1177
1282
|
this.activeSubagentSpans.set(agentId, spanId);
|
|
1178
1283
|
this.startSpan(
|
|
1179
1284
|
spanId,
|
|
@@ -1314,7 +1419,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1314
1419
|
this.flushLlmTurn();
|
|
1315
1420
|
this.conversationHistory.push(...this.pendingMessages);
|
|
1316
1421
|
this.pendingMessages = [];
|
|
1317
|
-
this.currentLlmSpanId =
|
|
1422
|
+
this.currentLlmSpanId = randomUuid();
|
|
1318
1423
|
this.currentLlmMessageId = messageId ?? null;
|
|
1319
1424
|
this.currentLlmContent = [];
|
|
1320
1425
|
this.currentLlmModel = inner.model ?? null;
|
|
@@ -1758,6 +1863,7 @@ function buildSnapshotRef(config, sdkWallClockBeforeFn) {
|
|
|
1758
1863
|
}
|
|
1759
1864
|
|
|
1760
1865
|
// src/langgraph.ts
|
|
1866
|
+
init_randomUuid();
|
|
1761
1867
|
init_serialize();
|
|
1762
1868
|
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
1763
1869
|
var LANGGRAPH_METADATA_KEYS = [
|
|
@@ -2006,7 +2112,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2006
2112
|
} else {
|
|
2007
2113
|
const activeContext = this.getActiveSpanContext?.() ?? null;
|
|
2008
2114
|
invocation = {
|
|
2009
|
-
traceId: activeContext ? activeContext.traceId :
|
|
2115
|
+
traceId: activeContext ? activeContext.traceId : randomUuid(),
|
|
2010
2116
|
activeContext,
|
|
2011
2117
|
rootRunId: runId
|
|
2012
2118
|
};
|
|
@@ -2301,6 +2407,20 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2301
2407
|
this.withSpanFn = config.withSpan;
|
|
2302
2408
|
this.getActiveSpanContext = config.getActiveSpanContext;
|
|
2303
2409
|
}
|
|
2410
|
+
/**
|
|
2411
|
+
* Drop-in replacement for the OpenAI Agents SDK's `run()` that records a
|
|
2412
|
+
* replayable root `agent` span.
|
|
2413
|
+
*
|
|
2414
|
+
* The `input` is captured as the root span's input (as a single positional
|
|
2415
|
+
* argument, so `replay(key, fn)` re-feeds it), and the run's `finalOutput`
|
|
2416
|
+
* is recorded as the root output. For streaming runs (`{ stream: true }`),
|
|
2417
|
+
* the result is handed back immediately and the final output is recorded
|
|
2418
|
+
* once the stream completes — first-byte latency is untouched.
|
|
2419
|
+
*
|
|
2420
|
+
* The process-wide tracing processor (`getOpenAiTracingProcessor`) must still
|
|
2421
|
+
* be registered: it captures the LLM/tool/handoff spans that nest beneath
|
|
2422
|
+
* this root.
|
|
2423
|
+
*/
|
|
2304
2424
|
async wrapRun(agent, input, options) {
|
|
2305
2425
|
const { run } = await importOptionalPeer([
|
|
2306
2426
|
"@openai",
|
|
@@ -2339,6 +2459,7 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2339
2459
|
};
|
|
2340
2460
|
|
|
2341
2461
|
// src/client.ts
|
|
2462
|
+
init_randomUuid();
|
|
2342
2463
|
init_replayContext();
|
|
2343
2464
|
|
|
2344
2465
|
// src/replayEnvironment.ts
|
|
@@ -2758,6 +2879,7 @@ var BitfabVercelAiHandler = class {
|
|
|
2758
2879
|
};
|
|
2759
2880
|
|
|
2760
2881
|
// src/client.ts
|
|
2882
|
+
init_warnOnce();
|
|
2761
2883
|
var activeTraceStates = /* @__PURE__ */ new Map();
|
|
2762
2884
|
var pendingSpanPromises = /* @__PURE__ */ new Map();
|
|
2763
2885
|
var asyncLocalStorage = null;
|
|
@@ -3116,7 +3238,20 @@ var Bitfab = class {
|
|
|
3116
3238
|
functionVersion.providers,
|
|
3117
3239
|
this.envVars
|
|
3118
3240
|
);
|
|
3119
|
-
|
|
3241
|
+
let resultStr;
|
|
3242
|
+
if (typeof executionResult.result === "string") {
|
|
3243
|
+
resultStr = executionResult.result;
|
|
3244
|
+
} else {
|
|
3245
|
+
try {
|
|
3246
|
+
resultStr = JSON.stringify(executionResult.result) ?? String(executionResult.result);
|
|
3247
|
+
} catch {
|
|
3248
|
+
warnOnce(
|
|
3249
|
+
"call-result-serialize",
|
|
3250
|
+
"a local execution result could not be JSON-serialized; storing its String() form instead. The call still returns its real value."
|
|
3251
|
+
);
|
|
3252
|
+
resultStr = String(executionResult.result);
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3120
3255
|
this.httpClient.sendInternalTrace(functionVersion.id, {
|
|
3121
3256
|
result: resultStr,
|
|
3122
3257
|
source: "typescript-sdk",
|
|
@@ -3365,11 +3500,26 @@ var Bitfab = class {
|
|
|
3365
3500
|
return await bamlClient[methodName](...args);
|
|
3366
3501
|
}
|
|
3367
3502
|
const collector = new CollectorClass("bitfab-baml-tracing");
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
trackedClient
|
|
3372
|
-
|
|
3503
|
+
let trackedClient;
|
|
3504
|
+
let trackedMethod;
|
|
3505
|
+
try {
|
|
3506
|
+
trackedClient = bamlClient.withOptions({ collector });
|
|
3507
|
+
const method2 = trackedClient[methodName];
|
|
3508
|
+
if (typeof method2 !== "function") {
|
|
3509
|
+
throw new BitfabError(
|
|
3510
|
+
"bamlClient.withOptions did not return the wrapped method"
|
|
3511
|
+
);
|
|
3512
|
+
}
|
|
3513
|
+
trackedMethod = method2;
|
|
3514
|
+
} catch {
|
|
3515
|
+
warnOnce(
|
|
3516
|
+
`wrapBAML-setup:${methodName}`,
|
|
3517
|
+
`BAML tracing setup failed for "${methodName}" (incompatible bamlClient or BAML version); calling it untraced. The call still runs; no span is recorded.`
|
|
3518
|
+
);
|
|
3519
|
+
wrappedFn.collector = null;
|
|
3520
|
+
return await bamlClient[methodName](...args);
|
|
3521
|
+
}
|
|
3522
|
+
const result = await trackedMethod.bind(trackedClient)(...args);
|
|
3373
3523
|
wrappedFn.collector = collector;
|
|
3374
3524
|
try {
|
|
3375
3525
|
const prompt = extractPromptFromCollector(collector);
|
|
@@ -3445,200 +3595,229 @@ var Bitfab = class {
|
|
|
3445
3595
|
() => wrappedFn.apply(this, args)
|
|
3446
3596
|
);
|
|
3447
3597
|
}
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
const
|
|
3462
|
-
|
|
3598
|
+
let newStack;
|
|
3599
|
+
let executeWithContext;
|
|
3600
|
+
let registeredTraceId;
|
|
3601
|
+
try {
|
|
3602
|
+
const currentStack = getSpanStack();
|
|
3603
|
+
const parentContext = currentStack[currentStack.length - 1];
|
|
3604
|
+
const replayCtxForTraceId = parentContext ? null : getReplayContext();
|
|
3605
|
+
const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? randomUuid();
|
|
3606
|
+
const spanId = randomUuid();
|
|
3607
|
+
const parentSpanId = parentContext?.spanId ?? null;
|
|
3608
|
+
const isRootSpan = parentSpanId === null;
|
|
3609
|
+
const newContext = { traceId, spanId, contexts: [] };
|
|
3610
|
+
newStack = [...currentStack, newContext];
|
|
3611
|
+
const inputs = args;
|
|
3612
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3613
|
+
if (isRootSpan && !activeTraceStates.has(traceId)) {
|
|
3614
|
+
const replayCtxAtRoot = getReplayContext();
|
|
3615
|
+
const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
|
|
3616
|
+
activeTraceStates.set(traceId, {
|
|
3617
|
+
traceId,
|
|
3618
|
+
startedAt,
|
|
3619
|
+
contexts: [],
|
|
3620
|
+
...replayCtxAtRoot?.testRunId && {
|
|
3621
|
+
testRunId: replayCtxAtRoot.testRunId
|
|
3622
|
+
},
|
|
3623
|
+
...replayCtxAtRoot?.inputSourceTraceId && {
|
|
3624
|
+
inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
|
|
3625
|
+
},
|
|
3626
|
+
dbSnapshotRef
|
|
3627
|
+
});
|
|
3628
|
+
pendingSpanPromises.set(traceId, []);
|
|
3629
|
+
registeredTraceId = traceId;
|
|
3630
|
+
}
|
|
3631
|
+
const functionName = fn.name !== "" ? fn.name : void 0;
|
|
3632
|
+
const baseSpanParams = {
|
|
3633
|
+
traceFunctionKey,
|
|
3634
|
+
functionName,
|
|
3635
|
+
spanName: options.name ?? functionName ?? traceFunctionKey,
|
|
3463
3636
|
traceId,
|
|
3637
|
+
spanId,
|
|
3638
|
+
parentSpanId,
|
|
3639
|
+
inputs,
|
|
3464
3640
|
startedAt,
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
startedAt,
|
|
3486
|
-
spanType: options.type ?? "custom"
|
|
3487
|
-
};
|
|
3488
|
-
const sendSpan = async (params, spanOpts) => {
|
|
3489
|
-
const replayCtx = getReplayContext();
|
|
3490
|
-
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3491
|
-
let resolvePersistence;
|
|
3492
|
-
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3493
|
-
persistenceCollector.push(
|
|
3494
|
-
new Promise((resolve) => {
|
|
3495
|
-
resolvePersistence = resolve;
|
|
3496
|
-
})
|
|
3497
|
-
);
|
|
3498
|
-
}
|
|
3499
|
-
try {
|
|
3500
|
-
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3501
|
-
const spanPromise = self.sendWrapperSpan({
|
|
3502
|
-
...baseSpanParams,
|
|
3503
|
-
...params,
|
|
3504
|
-
contexts: newContext.contexts,
|
|
3505
|
-
prompt: newContext.prompt,
|
|
3506
|
-
endedAt,
|
|
3507
|
-
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3508
|
-
...replayCtx?.inputSourceSpanId && {
|
|
3509
|
-
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3510
|
-
}
|
|
3511
|
-
});
|
|
3512
|
-
if (isRootSpan) {
|
|
3513
|
-
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3514
|
-
pending.push(spanPromise);
|
|
3515
|
-
if (persistenceCollector) {
|
|
3516
|
-
await Promise.allSettled(pending);
|
|
3517
|
-
} else {
|
|
3518
|
-
await Promise.race([
|
|
3519
|
-
Promise.allSettled(pending),
|
|
3520
|
-
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
3521
|
-
]);
|
|
3522
|
-
}
|
|
3523
|
-
pendingSpanPromises.delete(traceId);
|
|
3524
|
-
const traceState = activeTraceStates.get(traceId);
|
|
3525
|
-
const completionPromise = self.sendTraceCompletion({
|
|
3526
|
-
traceFunctionKey,
|
|
3527
|
-
traceId,
|
|
3528
|
-
startedAt: traceState?.startedAt ?? startedAt,
|
|
3641
|
+
spanType: options.type ?? "custom"
|
|
3642
|
+
};
|
|
3643
|
+
const sendSpan = async (params, spanOpts) => {
|
|
3644
|
+
const replayCtx = getReplayContext();
|
|
3645
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3646
|
+
let resolvePersistence;
|
|
3647
|
+
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3648
|
+
persistenceCollector.push(
|
|
3649
|
+
new Promise((resolve) => {
|
|
3650
|
+
resolvePersistence = resolve;
|
|
3651
|
+
})
|
|
3652
|
+
);
|
|
3653
|
+
}
|
|
3654
|
+
try {
|
|
3655
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3656
|
+
const spanPromise = self.sendWrapperSpan({
|
|
3657
|
+
...baseSpanParams,
|
|
3658
|
+
...params,
|
|
3659
|
+
contexts: newContext.contexts,
|
|
3660
|
+
prompt: newContext.prompt,
|
|
3529
3661
|
endedAt,
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
testRunId: traceState?.testRunId,
|
|
3534
|
-
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3535
|
-
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3536
|
-
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3537
|
-
// whether customer code obtained the branch URL during this
|
|
3538
|
-
// item. Omitted entirely when no lease was attached, so the
|
|
3539
|
-
// server can distinguish "no branch" from "branch ignored".
|
|
3540
|
-
...replayCtx?.dbBranchLease && {
|
|
3541
|
-
dbSnapshotUsage: {
|
|
3542
|
-
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3543
|
-
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3544
|
-
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3545
|
-
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3546
|
-
}
|
|
3662
|
+
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3663
|
+
...replayCtx?.inputSourceSpanId && {
|
|
3664
|
+
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3547
3665
|
}
|
|
3548
3666
|
});
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
await completionPromise;
|
|
3552
|
-
}
|
|
3553
|
-
} else {
|
|
3554
|
-
const pending = pendingSpanPromises.get(traceId);
|
|
3555
|
-
if (pending) {
|
|
3667
|
+
if (isRootSpan) {
|
|
3668
|
+
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3556
3669
|
pending.push(spanPromise);
|
|
3670
|
+
if (persistenceCollector) {
|
|
3671
|
+
await Promise.allSettled(pending);
|
|
3672
|
+
} else {
|
|
3673
|
+
let raceTimer;
|
|
3674
|
+
try {
|
|
3675
|
+
await Promise.race([
|
|
3676
|
+
Promise.allSettled(pending),
|
|
3677
|
+
new Promise((resolve) => {
|
|
3678
|
+
raceTimer = setTimeout(resolve, 5e3);
|
|
3679
|
+
unrefTimer(raceTimer);
|
|
3680
|
+
})
|
|
3681
|
+
]);
|
|
3682
|
+
} finally {
|
|
3683
|
+
if (raceTimer) {
|
|
3684
|
+
clearTimeout(raceTimer);
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
pendingSpanPromises.delete(traceId);
|
|
3689
|
+
const traceState = activeTraceStates.get(traceId);
|
|
3690
|
+
const completionPromise = self.sendTraceCompletion({
|
|
3691
|
+
traceFunctionKey,
|
|
3692
|
+
traceId,
|
|
3693
|
+
startedAt: traceState?.startedAt ?? startedAt,
|
|
3694
|
+
endedAt,
|
|
3695
|
+
sessionId: traceState?.sessionId,
|
|
3696
|
+
metadata: traceState?.metadata,
|
|
3697
|
+
contexts: traceState?.contexts ?? [],
|
|
3698
|
+
testRunId: traceState?.testRunId,
|
|
3699
|
+
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3700
|
+
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3701
|
+
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3702
|
+
// whether customer code obtained the branch URL during this
|
|
3703
|
+
// item. Omitted entirely when no lease was attached, so the
|
|
3704
|
+
// server can distinguish "no branch" from "branch ignored".
|
|
3705
|
+
...replayCtx?.dbBranchLease && {
|
|
3706
|
+
dbSnapshotUsage: {
|
|
3707
|
+
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3708
|
+
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3709
|
+
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3710
|
+
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
});
|
|
3714
|
+
activeTraceStates.delete(traceId);
|
|
3715
|
+
if (persistenceCollector) {
|
|
3716
|
+
await completionPromise;
|
|
3717
|
+
}
|
|
3557
3718
|
} else {
|
|
3558
|
-
pendingSpanPromises.
|
|
3719
|
+
const pending = pendingSpanPromises.get(traceId);
|
|
3720
|
+
if (pending) {
|
|
3721
|
+
pending.push(spanPromise);
|
|
3722
|
+
} else {
|
|
3723
|
+
pendingSpanPromises.set(traceId, [spanPromise]);
|
|
3724
|
+
}
|
|
3559
3725
|
}
|
|
3726
|
+
} catch {
|
|
3727
|
+
} finally {
|
|
3728
|
+
resolvePersistence?.();
|
|
3560
3729
|
}
|
|
3561
|
-
}
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
if (fnReturnsPromise) {
|
|
3586
|
-
return Promise.resolve(output);
|
|
3730
|
+
};
|
|
3731
|
+
const replayCtxForMock = getReplayContext();
|
|
3732
|
+
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
3733
|
+
const counters = replayCtxForMock.callCounters;
|
|
3734
|
+
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
3735
|
+
const callIndex = counters.get(counterKey) ?? 0;
|
|
3736
|
+
counters.set(counterKey, callIndex + 1);
|
|
3737
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
3738
|
+
if (shouldMock) {
|
|
3739
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
3740
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
3741
|
+
if (mockSpan) {
|
|
3742
|
+
let output = mockSpan.output;
|
|
3743
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
3744
|
+
output = deserializeValue({
|
|
3745
|
+
json: mockSpan.output,
|
|
3746
|
+
meta: mockSpan.outputMeta
|
|
3747
|
+
});
|
|
3748
|
+
}
|
|
3749
|
+
void sendSpan({ result: output });
|
|
3750
|
+
if (fnReturnsPromise) {
|
|
3751
|
+
return Promise.resolve(output);
|
|
3752
|
+
}
|
|
3753
|
+
return output;
|
|
3587
3754
|
}
|
|
3588
|
-
return output;
|
|
3589
3755
|
}
|
|
3590
3756
|
}
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3757
|
+
const recordSpan = (result) => {
|
|
3758
|
+
if (options.finalize) {
|
|
3759
|
+
const replayCtx = getReplayContext();
|
|
3760
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3761
|
+
let resolvePersistence;
|
|
3762
|
+
if (persistenceCollector) {
|
|
3763
|
+
persistenceCollector.push(
|
|
3764
|
+
new Promise((resolve) => {
|
|
3765
|
+
resolvePersistence = resolve;
|
|
3766
|
+
})
|
|
3767
|
+
);
|
|
3768
|
+
}
|
|
3769
|
+
void Promise.resolve().then(() => options.finalize(result)).then(
|
|
3770
|
+
(output) => sendSpan(
|
|
3771
|
+
{ result: output },
|
|
3772
|
+
{ skipPersistenceRegistration: true }
|
|
3773
|
+
)
|
|
3774
|
+
).catch(
|
|
3775
|
+
(error) => sendSpan(
|
|
3776
|
+
{
|
|
3777
|
+
result: void 0,
|
|
3778
|
+
error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
|
|
3779
|
+
},
|
|
3780
|
+
{ skipPersistenceRegistration: true }
|
|
3781
|
+
)
|
|
3782
|
+
).finally(() => resolvePersistence?.());
|
|
3783
|
+
} else {
|
|
3784
|
+
void sendSpan({ result });
|
|
3603
3785
|
}
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
)
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3786
|
+
};
|
|
3787
|
+
executeWithContext = () => {
|
|
3788
|
+
const result = fn(...args);
|
|
3789
|
+
if (result instanceof Promise) {
|
|
3790
|
+
return result.then((resolvedResult) => {
|
|
3791
|
+
recordSpan(resolvedResult);
|
|
3792
|
+
return resolvedResult;
|
|
3793
|
+
}).catch((error) => {
|
|
3794
|
+
void sendSpan({
|
|
3612
3795
|
result: void 0,
|
|
3613
|
-
error: error instanceof Error ?
|
|
3614
|
-
}
|
|
3615
|
-
|
|
3616
|
-
)
|
|
3617
|
-
).finally(() => resolvePersistence?.());
|
|
3618
|
-
} else {
|
|
3619
|
-
void sendSpan({ result });
|
|
3620
|
-
}
|
|
3621
|
-
};
|
|
3622
|
-
const executeWithContext = () => {
|
|
3623
|
-
const result = fn(...args);
|
|
3624
|
-
if (result instanceof Promise) {
|
|
3625
|
-
return result.then((resolvedResult) => {
|
|
3626
|
-
recordSpan(resolvedResult);
|
|
3627
|
-
return resolvedResult;
|
|
3628
|
-
}).catch((error) => {
|
|
3629
|
-
void sendSpan({
|
|
3630
|
-
result: void 0,
|
|
3631
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3796
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3797
|
+
});
|
|
3798
|
+
throw error;
|
|
3632
3799
|
});
|
|
3633
|
-
|
|
3634
|
-
|
|
3800
|
+
}
|
|
3801
|
+
if (isAsyncGenerator(result)) {
|
|
3802
|
+
return wrapAsyncGenerator(result, newStack, sendSpan);
|
|
3803
|
+
}
|
|
3804
|
+
recordSpan(result);
|
|
3805
|
+
return result;
|
|
3806
|
+
};
|
|
3807
|
+
} catch (setupError) {
|
|
3808
|
+
if (registeredTraceId) {
|
|
3809
|
+
activeTraceStates.delete(registeredTraceId);
|
|
3810
|
+
pendingSpanPromises.delete(registeredTraceId);
|
|
3635
3811
|
}
|
|
3636
|
-
if (
|
|
3637
|
-
|
|
3812
|
+
if (getReplayContext()) {
|
|
3813
|
+
throw setupError;
|
|
3638
3814
|
}
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3815
|
+
warnOnce(
|
|
3816
|
+
`withSpan-setup:${traceFunctionKey}`,
|
|
3817
|
+
`tracing setup failed for "${traceFunctionKey}"; running it untraced. The function still runs and returns normally; no span is recorded.`
|
|
3818
|
+
);
|
|
3819
|
+
return fn(...args);
|
|
3820
|
+
}
|
|
3642
3821
|
return runWithSpanStack(newStack, executeWithContext);
|
|
3643
3822
|
};
|
|
3644
3823
|
Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
|