@bitfab/sdk 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
|
@@ -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);
|
|
@@ -463,6 +529,7 @@ var init_replay = __esm({
|
|
|
463
529
|
"src/replay.ts"() {
|
|
464
530
|
"use strict";
|
|
465
531
|
init_errors();
|
|
532
|
+
init_randomUuid();
|
|
466
533
|
init_replayContext();
|
|
467
534
|
init_serialize();
|
|
468
535
|
}
|
|
@@ -492,13 +559,24 @@ __export(index_exports, {
|
|
|
492
559
|
module.exports = __toCommonJS(index_exports);
|
|
493
560
|
|
|
494
561
|
// src/version.generated.ts
|
|
495
|
-
var __version__ = "0.
|
|
562
|
+
var __version__ = "0.24.0";
|
|
496
563
|
|
|
497
564
|
// src/constants.ts
|
|
498
565
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
499
566
|
|
|
500
567
|
// src/http.ts
|
|
501
568
|
init_errors();
|
|
569
|
+
|
|
570
|
+
// src/unrefTimer.ts
|
|
571
|
+
function unrefTimer(timer) {
|
|
572
|
+
const handle = timer;
|
|
573
|
+
if (typeof handle.unref === "function") {
|
|
574
|
+
handle.unref();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/http.ts
|
|
579
|
+
init_warnOnce();
|
|
502
580
|
function serializePayloadBody(payload) {
|
|
503
581
|
try {
|
|
504
582
|
return { body: JSON.stringify(payload), dropped: [] };
|
|
@@ -543,11 +621,20 @@ function serializePayloadBody(payload) {
|
|
|
543
621
|
result = `<unserializable: ${className}>`;
|
|
544
622
|
}
|
|
545
623
|
} else {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
624
|
+
try {
|
|
625
|
+
const out = {};
|
|
626
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
627
|
+
out[k] = sanitize(v, seen);
|
|
628
|
+
}
|
|
629
|
+
result = out;
|
|
630
|
+
} catch {
|
|
631
|
+
warnOnce(
|
|
632
|
+
"payload:field-getter-threw",
|
|
633
|
+
"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."
|
|
634
|
+
);
|
|
635
|
+
dropped.push(className);
|
|
636
|
+
result = `<unserializable: ${className}>`;
|
|
549
637
|
}
|
|
550
|
-
result = out;
|
|
551
638
|
}
|
|
552
639
|
seen.delete(obj);
|
|
553
640
|
return result;
|
|
@@ -592,10 +679,20 @@ async function flushTraces(timeoutMs = 5e3) {
|
|
|
592
679
|
if (pendingTracePromises.size === 0) {
|
|
593
680
|
return;
|
|
594
681
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
682
|
+
let timer;
|
|
683
|
+
try {
|
|
684
|
+
await Promise.race([
|
|
685
|
+
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
686
|
+
new Promise((resolve) => {
|
|
687
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
688
|
+
unrefTimer(timer);
|
|
689
|
+
})
|
|
690
|
+
]);
|
|
691
|
+
} finally {
|
|
692
|
+
if (timer) {
|
|
693
|
+
clearTimeout(timer);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
599
696
|
}
|
|
600
697
|
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
601
698
|
let isFlushing = false;
|
|
@@ -905,6 +1002,7 @@ var HttpClient = class {
|
|
|
905
1002
|
};
|
|
906
1003
|
|
|
907
1004
|
// src/claudeAgentSdk.ts
|
|
1005
|
+
init_randomUuid();
|
|
908
1006
|
init_serialize();
|
|
909
1007
|
function nowIso() {
|
|
910
1008
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -992,7 +1090,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
992
1090
|
if (this.activeContext) {
|
|
993
1091
|
this.traceId = this.activeContext.traceId;
|
|
994
1092
|
} else {
|
|
995
|
-
this.traceId =
|
|
1093
|
+
this.traceId = randomUuid();
|
|
996
1094
|
}
|
|
997
1095
|
this.traceStartedAt = nowIso();
|
|
998
1096
|
return this.traceId;
|
|
@@ -1017,7 +1115,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1017
1115
|
if (this.activeContext !== null) {
|
|
1018
1116
|
return;
|
|
1019
1117
|
}
|
|
1020
|
-
const spanId =
|
|
1118
|
+
const spanId = randomUuid();
|
|
1021
1119
|
this.startSpan(spanId, this.traceFunctionKey, "agent", this.rootInput, null);
|
|
1022
1120
|
this.rootSpanId = spanId;
|
|
1023
1121
|
}
|
|
@@ -1131,7 +1229,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1131
1229
|
// ── hook callbacks ───────────────────────────────────────────
|
|
1132
1230
|
async preToolUseHook(inputData, toolUseId, _context) {
|
|
1133
1231
|
try {
|
|
1134
|
-
const sid = inputData.tool_use_id ?? toolUseId ??
|
|
1232
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? randomUuid();
|
|
1135
1233
|
const toolName = inputData.tool_name ?? "tool";
|
|
1136
1234
|
const toolInput = inputData.tool_input ?? {};
|
|
1137
1235
|
const agentId = inputData.agent_id;
|
|
@@ -1161,10 +1259,10 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1161
1259
|
}
|
|
1162
1260
|
async subagentStartHook(inputData, _toolUseId, _context) {
|
|
1163
1261
|
try {
|
|
1164
|
-
const agentId = inputData.agent_id ??
|
|
1262
|
+
const agentId = inputData.agent_id ?? randomUuid();
|
|
1165
1263
|
const agentType = inputData.agent_type ?? "subagent";
|
|
1166
1264
|
const parentId = this.getParentId();
|
|
1167
|
-
const spanId =
|
|
1265
|
+
const spanId = randomUuid();
|
|
1168
1266
|
this.activeSubagentSpans.set(agentId, spanId);
|
|
1169
1267
|
this.startSpan(
|
|
1170
1268
|
spanId,
|
|
@@ -1305,7 +1403,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1305
1403
|
this.flushLlmTurn();
|
|
1306
1404
|
this.conversationHistory.push(...this.pendingMessages);
|
|
1307
1405
|
this.pendingMessages = [];
|
|
1308
|
-
this.currentLlmSpanId =
|
|
1406
|
+
this.currentLlmSpanId = randomUuid();
|
|
1309
1407
|
this.currentLlmMessageId = messageId ?? null;
|
|
1310
1408
|
this.currentLlmContent = [];
|
|
1311
1409
|
this.currentLlmModel = inner.model ?? null;
|
|
@@ -1433,6 +1531,16 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1433
1531
|
// src/client.ts
|
|
1434
1532
|
init_asyncStorage();
|
|
1435
1533
|
|
|
1534
|
+
// src/optionalPeer.ts
|
|
1535
|
+
function importOptionalPeer(specifierParts) {
|
|
1536
|
+
const specifier = specifierParts.join("/");
|
|
1537
|
+
return import(
|
|
1538
|
+
/* webpackIgnore: true */
|
|
1539
|
+
/* @vite-ignore */
|
|
1540
|
+
specifier
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1436
1544
|
// src/baml.ts
|
|
1437
1545
|
var cachedBaml = null;
|
|
1438
1546
|
async function loadBaml() {
|
|
@@ -1440,7 +1548,7 @@ async function loadBaml() {
|
|
|
1440
1548
|
return cachedBaml;
|
|
1441
1549
|
}
|
|
1442
1550
|
try {
|
|
1443
|
-
cachedBaml = await
|
|
1551
|
+
cachedBaml = await importOptionalPeer(["@boundaryml", "baml"]);
|
|
1444
1552
|
return cachedBaml;
|
|
1445
1553
|
} catch {
|
|
1446
1554
|
throw new Error(
|
|
@@ -1739,6 +1847,7 @@ function buildSnapshotRef(config, sdkWallClockBeforeFn) {
|
|
|
1739
1847
|
}
|
|
1740
1848
|
|
|
1741
1849
|
// src/langgraph.ts
|
|
1850
|
+
init_randomUuid();
|
|
1742
1851
|
init_serialize();
|
|
1743
1852
|
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
1744
1853
|
var LANGGRAPH_METADATA_KEYS = [
|
|
@@ -1987,7 +2096,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
1987
2096
|
} else {
|
|
1988
2097
|
const activeContext = this.getActiveSpanContext?.() ?? null;
|
|
1989
2098
|
invocation = {
|
|
1990
|
-
traceId: activeContext ? activeContext.traceId :
|
|
2099
|
+
traceId: activeContext ? activeContext.traceId : randomUuid(),
|
|
1991
2100
|
activeContext,
|
|
1992
2101
|
rootRunId: runId
|
|
1993
2102
|
};
|
|
@@ -2282,8 +2391,25 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2282
2391
|
this.withSpanFn = config.withSpan;
|
|
2283
2392
|
this.getActiveSpanContext = config.getActiveSpanContext;
|
|
2284
2393
|
}
|
|
2394
|
+
/**
|
|
2395
|
+
* Drop-in replacement for the OpenAI Agents SDK's `run()` that records a
|
|
2396
|
+
* replayable root `agent` span.
|
|
2397
|
+
*
|
|
2398
|
+
* The `input` is captured as the root span's input (as a single positional
|
|
2399
|
+
* argument, so `replay(key, fn)` re-feeds it), and the run's `finalOutput`
|
|
2400
|
+
* is recorded as the root output. For streaming runs (`{ stream: true }`),
|
|
2401
|
+
* the result is handed back immediately and the final output is recorded
|
|
2402
|
+
* once the stream completes — first-byte latency is untouched.
|
|
2403
|
+
*
|
|
2404
|
+
* The process-wide tracing processor (`getOpenAiTracingProcessor`) must still
|
|
2405
|
+
* be registered: it captures the LLM/tool/handoff spans that nest beneath
|
|
2406
|
+
* this root.
|
|
2407
|
+
*/
|
|
2285
2408
|
async wrapRun(agent, input, options) {
|
|
2286
|
-
const { run } = await
|
|
2409
|
+
const { run } = await importOptionalPeer([
|
|
2410
|
+
"@openai",
|
|
2411
|
+
"agents"
|
|
2412
|
+
]);
|
|
2287
2413
|
if (this.getActiveSpanContext?.() != null) {
|
|
2288
2414
|
return run(
|
|
2289
2415
|
agent,
|
|
@@ -2317,6 +2443,7 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2317
2443
|
};
|
|
2318
2444
|
|
|
2319
2445
|
// src/client.ts
|
|
2446
|
+
init_randomUuid();
|
|
2320
2447
|
init_replayContext();
|
|
2321
2448
|
|
|
2322
2449
|
// src/replayEnvironment.ts
|
|
@@ -2736,6 +2863,7 @@ var BitfabVercelAiHandler = class {
|
|
|
2736
2863
|
};
|
|
2737
2864
|
|
|
2738
2865
|
// src/client.ts
|
|
2866
|
+
init_warnOnce();
|
|
2739
2867
|
var activeTraceStates = /* @__PURE__ */ new Map();
|
|
2740
2868
|
var pendingSpanPromises = /* @__PURE__ */ new Map();
|
|
2741
2869
|
var asyncLocalStorage = null;
|
|
@@ -2834,7 +2962,10 @@ async function loadCollectorClass() {
|
|
|
2834
2962
|
return cachedCollectorClass;
|
|
2835
2963
|
}
|
|
2836
2964
|
try {
|
|
2837
|
-
const baml = await
|
|
2965
|
+
const baml = await importOptionalPeer([
|
|
2966
|
+
"@boundaryml",
|
|
2967
|
+
"baml"
|
|
2968
|
+
]);
|
|
2838
2969
|
cachedCollectorClass = baml.Collector;
|
|
2839
2970
|
return cachedCollectorClass;
|
|
2840
2971
|
} catch {
|
|
@@ -3091,7 +3222,20 @@ var Bitfab = class {
|
|
|
3091
3222
|
functionVersion.providers,
|
|
3092
3223
|
this.envVars
|
|
3093
3224
|
);
|
|
3094
|
-
|
|
3225
|
+
let resultStr;
|
|
3226
|
+
if (typeof executionResult.result === "string") {
|
|
3227
|
+
resultStr = executionResult.result;
|
|
3228
|
+
} else {
|
|
3229
|
+
try {
|
|
3230
|
+
resultStr = JSON.stringify(executionResult.result) ?? String(executionResult.result);
|
|
3231
|
+
} catch {
|
|
3232
|
+
warnOnce(
|
|
3233
|
+
"call-result-serialize",
|
|
3234
|
+
"a local execution result could not be JSON-serialized; storing its String() form instead. The call still returns its real value."
|
|
3235
|
+
);
|
|
3236
|
+
resultStr = String(executionResult.result);
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3095
3239
|
this.httpClient.sendInternalTrace(functionVersion.id, {
|
|
3096
3240
|
result: resultStr,
|
|
3097
3241
|
source: "typescript-sdk",
|
|
@@ -3340,11 +3484,26 @@ var Bitfab = class {
|
|
|
3340
3484
|
return await bamlClient[methodName](...args);
|
|
3341
3485
|
}
|
|
3342
3486
|
const collector = new CollectorClass("bitfab-baml-tracing");
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
trackedClient
|
|
3347
|
-
|
|
3487
|
+
let trackedClient;
|
|
3488
|
+
let trackedMethod;
|
|
3489
|
+
try {
|
|
3490
|
+
trackedClient = bamlClient.withOptions({ collector });
|
|
3491
|
+
const method2 = trackedClient[methodName];
|
|
3492
|
+
if (typeof method2 !== "function") {
|
|
3493
|
+
throw new BitfabError(
|
|
3494
|
+
"bamlClient.withOptions did not return the wrapped method"
|
|
3495
|
+
);
|
|
3496
|
+
}
|
|
3497
|
+
trackedMethod = method2;
|
|
3498
|
+
} catch {
|
|
3499
|
+
warnOnce(
|
|
3500
|
+
`wrapBAML-setup:${methodName}`,
|
|
3501
|
+
`BAML tracing setup failed for "${methodName}" (incompatible bamlClient or BAML version); calling it untraced. The call still runs; no span is recorded.`
|
|
3502
|
+
);
|
|
3503
|
+
wrappedFn.collector = null;
|
|
3504
|
+
return await bamlClient[methodName](...args);
|
|
3505
|
+
}
|
|
3506
|
+
const result = await trackedMethod.bind(trackedClient)(...args);
|
|
3348
3507
|
wrappedFn.collector = collector;
|
|
3349
3508
|
try {
|
|
3350
3509
|
const prompt = extractPromptFromCollector(collector);
|
|
@@ -3420,200 +3579,229 @@ var Bitfab = class {
|
|
|
3420
3579
|
() => wrappedFn.apply(this, args)
|
|
3421
3580
|
);
|
|
3422
3581
|
}
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
const
|
|
3437
|
-
|
|
3582
|
+
let newStack;
|
|
3583
|
+
let executeWithContext;
|
|
3584
|
+
let registeredTraceId;
|
|
3585
|
+
try {
|
|
3586
|
+
const currentStack = getSpanStack();
|
|
3587
|
+
const parentContext = currentStack[currentStack.length - 1];
|
|
3588
|
+
const replayCtxForTraceId = parentContext ? null : getReplayContext();
|
|
3589
|
+
const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? randomUuid();
|
|
3590
|
+
const spanId = randomUuid();
|
|
3591
|
+
const parentSpanId = parentContext?.spanId ?? null;
|
|
3592
|
+
const isRootSpan = parentSpanId === null;
|
|
3593
|
+
const newContext = { traceId, spanId, contexts: [] };
|
|
3594
|
+
newStack = [...currentStack, newContext];
|
|
3595
|
+
const inputs = args;
|
|
3596
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3597
|
+
if (isRootSpan && !activeTraceStates.has(traceId)) {
|
|
3598
|
+
const replayCtxAtRoot = getReplayContext();
|
|
3599
|
+
const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
|
|
3600
|
+
activeTraceStates.set(traceId, {
|
|
3601
|
+
traceId,
|
|
3602
|
+
startedAt,
|
|
3603
|
+
contexts: [],
|
|
3604
|
+
...replayCtxAtRoot?.testRunId && {
|
|
3605
|
+
testRunId: replayCtxAtRoot.testRunId
|
|
3606
|
+
},
|
|
3607
|
+
...replayCtxAtRoot?.inputSourceTraceId && {
|
|
3608
|
+
inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
|
|
3609
|
+
},
|
|
3610
|
+
dbSnapshotRef
|
|
3611
|
+
});
|
|
3612
|
+
pendingSpanPromises.set(traceId, []);
|
|
3613
|
+
registeredTraceId = traceId;
|
|
3614
|
+
}
|
|
3615
|
+
const functionName = fn.name !== "" ? fn.name : void 0;
|
|
3616
|
+
const baseSpanParams = {
|
|
3617
|
+
traceFunctionKey,
|
|
3618
|
+
functionName,
|
|
3619
|
+
spanName: options.name ?? functionName ?? traceFunctionKey,
|
|
3438
3620
|
traceId,
|
|
3621
|
+
spanId,
|
|
3622
|
+
parentSpanId,
|
|
3623
|
+
inputs,
|
|
3439
3624
|
startedAt,
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
startedAt,
|
|
3461
|
-
spanType: options.type ?? "custom"
|
|
3462
|
-
};
|
|
3463
|
-
const sendSpan = async (params, spanOpts) => {
|
|
3464
|
-
const replayCtx = getReplayContext();
|
|
3465
|
-
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3466
|
-
let resolvePersistence;
|
|
3467
|
-
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3468
|
-
persistenceCollector.push(
|
|
3469
|
-
new Promise((resolve) => {
|
|
3470
|
-
resolvePersistence = resolve;
|
|
3471
|
-
})
|
|
3472
|
-
);
|
|
3473
|
-
}
|
|
3474
|
-
try {
|
|
3475
|
-
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3476
|
-
const spanPromise = self.sendWrapperSpan({
|
|
3477
|
-
...baseSpanParams,
|
|
3478
|
-
...params,
|
|
3479
|
-
contexts: newContext.contexts,
|
|
3480
|
-
prompt: newContext.prompt,
|
|
3481
|
-
endedAt,
|
|
3482
|
-
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3483
|
-
...replayCtx?.inputSourceSpanId && {
|
|
3484
|
-
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3485
|
-
}
|
|
3486
|
-
});
|
|
3487
|
-
if (isRootSpan) {
|
|
3488
|
-
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3489
|
-
pending.push(spanPromise);
|
|
3490
|
-
if (persistenceCollector) {
|
|
3491
|
-
await Promise.allSettled(pending);
|
|
3492
|
-
} else {
|
|
3493
|
-
await Promise.race([
|
|
3494
|
-
Promise.allSettled(pending),
|
|
3495
|
-
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
3496
|
-
]);
|
|
3497
|
-
}
|
|
3498
|
-
pendingSpanPromises.delete(traceId);
|
|
3499
|
-
const traceState = activeTraceStates.get(traceId);
|
|
3500
|
-
const completionPromise = self.sendTraceCompletion({
|
|
3501
|
-
traceFunctionKey,
|
|
3502
|
-
traceId,
|
|
3503
|
-
startedAt: traceState?.startedAt ?? startedAt,
|
|
3625
|
+
spanType: options.type ?? "custom"
|
|
3626
|
+
};
|
|
3627
|
+
const sendSpan = async (params, spanOpts) => {
|
|
3628
|
+
const replayCtx = getReplayContext();
|
|
3629
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3630
|
+
let resolvePersistence;
|
|
3631
|
+
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3632
|
+
persistenceCollector.push(
|
|
3633
|
+
new Promise((resolve) => {
|
|
3634
|
+
resolvePersistence = resolve;
|
|
3635
|
+
})
|
|
3636
|
+
);
|
|
3637
|
+
}
|
|
3638
|
+
try {
|
|
3639
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3640
|
+
const spanPromise = self.sendWrapperSpan({
|
|
3641
|
+
...baseSpanParams,
|
|
3642
|
+
...params,
|
|
3643
|
+
contexts: newContext.contexts,
|
|
3644
|
+
prompt: newContext.prompt,
|
|
3504
3645
|
endedAt,
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
testRunId: traceState?.testRunId,
|
|
3509
|
-
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3510
|
-
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3511
|
-
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3512
|
-
// whether customer code obtained the branch URL during this
|
|
3513
|
-
// item. Omitted entirely when no lease was attached, so the
|
|
3514
|
-
// server can distinguish "no branch" from "branch ignored".
|
|
3515
|
-
...replayCtx?.dbBranchLease && {
|
|
3516
|
-
dbSnapshotUsage: {
|
|
3517
|
-
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3518
|
-
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3519
|
-
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3520
|
-
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3521
|
-
}
|
|
3646
|
+
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3647
|
+
...replayCtx?.inputSourceSpanId && {
|
|
3648
|
+
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3522
3649
|
}
|
|
3523
3650
|
});
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
await completionPromise;
|
|
3527
|
-
}
|
|
3528
|
-
} else {
|
|
3529
|
-
const pending = pendingSpanPromises.get(traceId);
|
|
3530
|
-
if (pending) {
|
|
3651
|
+
if (isRootSpan) {
|
|
3652
|
+
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3531
3653
|
pending.push(spanPromise);
|
|
3654
|
+
if (persistenceCollector) {
|
|
3655
|
+
await Promise.allSettled(pending);
|
|
3656
|
+
} else {
|
|
3657
|
+
let raceTimer;
|
|
3658
|
+
try {
|
|
3659
|
+
await Promise.race([
|
|
3660
|
+
Promise.allSettled(pending),
|
|
3661
|
+
new Promise((resolve) => {
|
|
3662
|
+
raceTimer = setTimeout(resolve, 5e3);
|
|
3663
|
+
unrefTimer(raceTimer);
|
|
3664
|
+
})
|
|
3665
|
+
]);
|
|
3666
|
+
} finally {
|
|
3667
|
+
if (raceTimer) {
|
|
3668
|
+
clearTimeout(raceTimer);
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
pendingSpanPromises.delete(traceId);
|
|
3673
|
+
const traceState = activeTraceStates.get(traceId);
|
|
3674
|
+
const completionPromise = self.sendTraceCompletion({
|
|
3675
|
+
traceFunctionKey,
|
|
3676
|
+
traceId,
|
|
3677
|
+
startedAt: traceState?.startedAt ?? startedAt,
|
|
3678
|
+
endedAt,
|
|
3679
|
+
sessionId: traceState?.sessionId,
|
|
3680
|
+
metadata: traceState?.metadata,
|
|
3681
|
+
contexts: traceState?.contexts ?? [],
|
|
3682
|
+
testRunId: traceState?.testRunId,
|
|
3683
|
+
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3684
|
+
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3685
|
+
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3686
|
+
// whether customer code obtained the branch URL during this
|
|
3687
|
+
// item. Omitted entirely when no lease was attached, so the
|
|
3688
|
+
// server can distinguish "no branch" from "branch ignored".
|
|
3689
|
+
...replayCtx?.dbBranchLease && {
|
|
3690
|
+
dbSnapshotUsage: {
|
|
3691
|
+
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3692
|
+
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3693
|
+
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3694
|
+
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
});
|
|
3698
|
+
activeTraceStates.delete(traceId);
|
|
3699
|
+
if (persistenceCollector) {
|
|
3700
|
+
await completionPromise;
|
|
3701
|
+
}
|
|
3532
3702
|
} else {
|
|
3533
|
-
pendingSpanPromises.
|
|
3703
|
+
const pending = pendingSpanPromises.get(traceId);
|
|
3704
|
+
if (pending) {
|
|
3705
|
+
pending.push(spanPromise);
|
|
3706
|
+
} else {
|
|
3707
|
+
pendingSpanPromises.set(traceId, [spanPromise]);
|
|
3708
|
+
}
|
|
3534
3709
|
}
|
|
3710
|
+
} catch {
|
|
3711
|
+
} finally {
|
|
3712
|
+
resolvePersistence?.();
|
|
3535
3713
|
}
|
|
3536
|
-
}
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
if (fnReturnsPromise) {
|
|
3561
|
-
return Promise.resolve(output);
|
|
3714
|
+
};
|
|
3715
|
+
const replayCtxForMock = getReplayContext();
|
|
3716
|
+
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
3717
|
+
const counters = replayCtxForMock.callCounters;
|
|
3718
|
+
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
3719
|
+
const callIndex = counters.get(counterKey) ?? 0;
|
|
3720
|
+
counters.set(counterKey, callIndex + 1);
|
|
3721
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
3722
|
+
if (shouldMock) {
|
|
3723
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
3724
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
3725
|
+
if (mockSpan) {
|
|
3726
|
+
let output = mockSpan.output;
|
|
3727
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
3728
|
+
output = deserializeValue({
|
|
3729
|
+
json: mockSpan.output,
|
|
3730
|
+
meta: mockSpan.outputMeta
|
|
3731
|
+
});
|
|
3732
|
+
}
|
|
3733
|
+
void sendSpan({ result: output });
|
|
3734
|
+
if (fnReturnsPromise) {
|
|
3735
|
+
return Promise.resolve(output);
|
|
3736
|
+
}
|
|
3737
|
+
return output;
|
|
3562
3738
|
}
|
|
3563
|
-
return output;
|
|
3564
3739
|
}
|
|
3565
3740
|
}
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3741
|
+
const recordSpan = (result) => {
|
|
3742
|
+
if (options.finalize) {
|
|
3743
|
+
const replayCtx = getReplayContext();
|
|
3744
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3745
|
+
let resolvePersistence;
|
|
3746
|
+
if (persistenceCollector) {
|
|
3747
|
+
persistenceCollector.push(
|
|
3748
|
+
new Promise((resolve) => {
|
|
3749
|
+
resolvePersistence = resolve;
|
|
3750
|
+
})
|
|
3751
|
+
);
|
|
3752
|
+
}
|
|
3753
|
+
void Promise.resolve().then(() => options.finalize(result)).then(
|
|
3754
|
+
(output) => sendSpan(
|
|
3755
|
+
{ result: output },
|
|
3756
|
+
{ skipPersistenceRegistration: true }
|
|
3757
|
+
)
|
|
3758
|
+
).catch(
|
|
3759
|
+
(error) => sendSpan(
|
|
3760
|
+
{
|
|
3761
|
+
result: void 0,
|
|
3762
|
+
error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
|
|
3763
|
+
},
|
|
3764
|
+
{ skipPersistenceRegistration: true }
|
|
3765
|
+
)
|
|
3766
|
+
).finally(() => resolvePersistence?.());
|
|
3767
|
+
} else {
|
|
3768
|
+
void sendSpan({ result });
|
|
3578
3769
|
}
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
)
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3770
|
+
};
|
|
3771
|
+
executeWithContext = () => {
|
|
3772
|
+
const result = fn(...args);
|
|
3773
|
+
if (result instanceof Promise) {
|
|
3774
|
+
return result.then((resolvedResult) => {
|
|
3775
|
+
recordSpan(resolvedResult);
|
|
3776
|
+
return resolvedResult;
|
|
3777
|
+
}).catch((error) => {
|
|
3778
|
+
void sendSpan({
|
|
3587
3779
|
result: void 0,
|
|
3588
|
-
error: error instanceof Error ?
|
|
3589
|
-
}
|
|
3590
|
-
|
|
3591
|
-
)
|
|
3592
|
-
).finally(() => resolvePersistence?.());
|
|
3593
|
-
} else {
|
|
3594
|
-
void sendSpan({ result });
|
|
3595
|
-
}
|
|
3596
|
-
};
|
|
3597
|
-
const executeWithContext = () => {
|
|
3598
|
-
const result = fn(...args);
|
|
3599
|
-
if (result instanceof Promise) {
|
|
3600
|
-
return result.then((resolvedResult) => {
|
|
3601
|
-
recordSpan(resolvedResult);
|
|
3602
|
-
return resolvedResult;
|
|
3603
|
-
}).catch((error) => {
|
|
3604
|
-
void sendSpan({
|
|
3605
|
-
result: void 0,
|
|
3606
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3780
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3781
|
+
});
|
|
3782
|
+
throw error;
|
|
3607
3783
|
});
|
|
3608
|
-
|
|
3609
|
-
|
|
3784
|
+
}
|
|
3785
|
+
if (isAsyncGenerator(result)) {
|
|
3786
|
+
return wrapAsyncGenerator(result, newStack, sendSpan);
|
|
3787
|
+
}
|
|
3788
|
+
recordSpan(result);
|
|
3789
|
+
return result;
|
|
3790
|
+
};
|
|
3791
|
+
} catch (setupError) {
|
|
3792
|
+
if (registeredTraceId) {
|
|
3793
|
+
activeTraceStates.delete(registeredTraceId);
|
|
3794
|
+
pendingSpanPromises.delete(registeredTraceId);
|
|
3610
3795
|
}
|
|
3611
|
-
if (
|
|
3612
|
-
|
|
3796
|
+
if (getReplayContext()) {
|
|
3797
|
+
throw setupError;
|
|
3613
3798
|
}
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3799
|
+
warnOnce(
|
|
3800
|
+
`withSpan-setup:${traceFunctionKey}`,
|
|
3801
|
+
`tracing setup failed for "${traceFunctionKey}"; running it untraced. The function still runs and returns normally; no span is recorded.`
|
|
3802
|
+
);
|
|
3803
|
+
return fn(...args);
|
|
3804
|
+
}
|
|
3617
3805
|
return runWithSpanStack(newStack, executeWithContext);
|
|
3618
3806
|
};
|
|
3619
3807
|
Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
|