@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/node.cjs
CHANGED
|
@@ -91,6 +91,54 @@ var init_errors = __esm({
|
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
+
// src/warnOnce.ts
|
|
95
|
+
function warnOnce(key, message) {
|
|
96
|
+
if (warned.has(key)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
warned.add(key);
|
|
100
|
+
try {
|
|
101
|
+
console.warn(`[bitfab] ${message}`);
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
var warned;
|
|
106
|
+
var init_warnOnce = __esm({
|
|
107
|
+
"src/warnOnce.ts"() {
|
|
108
|
+
"use strict";
|
|
109
|
+
warned = /* @__PURE__ */ new Set();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// src/randomUuid.ts
|
|
114
|
+
function randomUuid() {
|
|
115
|
+
const globalCrypto = globalThis.crypto;
|
|
116
|
+
if (typeof globalCrypto?.randomUUID === "function") {
|
|
117
|
+
try {
|
|
118
|
+
return globalCrypto.randomUUID();
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
warnOnce(
|
|
123
|
+
"crypto-unavailable",
|
|
124
|
+
"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
|
|
125
|
+
);
|
|
126
|
+
return fallbackUuidV4();
|
|
127
|
+
}
|
|
128
|
+
function fallbackUuidV4() {
|
|
129
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
130
|
+
const rand = Math.random() * 16 | 0;
|
|
131
|
+
const value = char === "x" ? rand : rand & 3 | 8;
|
|
132
|
+
return value.toString(16);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
var init_randomUuid = __esm({
|
|
136
|
+
"src/randomUuid.ts"() {
|
|
137
|
+
"use strict";
|
|
138
|
+
init_warnOnce();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
94
142
|
// src/serialize.ts
|
|
95
143
|
function describeValue(value) {
|
|
96
144
|
try {
|
|
@@ -103,6 +151,10 @@ function describeValue(value) {
|
|
|
103
151
|
return typeof value;
|
|
104
152
|
}
|
|
105
153
|
function unserializableStub(value, reason) {
|
|
154
|
+
warnOnce(
|
|
155
|
+
`serialize:${reason.replace(/\d+/g, "N")}`,
|
|
156
|
+
`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.`
|
|
157
|
+
);
|
|
106
158
|
let summary;
|
|
107
159
|
try {
|
|
108
160
|
summary = `<unserializable: ${describeValue(value)} (${reason})>`;
|
|
@@ -142,7 +194,19 @@ function deserializeValue(serialized) {
|
|
|
142
194
|
});
|
|
143
195
|
}
|
|
144
196
|
function toJsonSafe(value) {
|
|
145
|
-
|
|
197
|
+
const safe = toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet());
|
|
198
|
+
try {
|
|
199
|
+
const size = JSON.stringify(safe)?.length ?? 0;
|
|
200
|
+
if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {
|
|
201
|
+
warnOnce(
|
|
202
|
+
"toJsonSafe:too_large",
|
|
203
|
+
`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.`
|
|
204
|
+
);
|
|
205
|
+
return `<unserializable: too_large_${size}_bytes>`;
|
|
206
|
+
}
|
|
207
|
+
} catch {
|
|
208
|
+
}
|
|
209
|
+
return safe;
|
|
146
210
|
}
|
|
147
211
|
function toJsonSafeInner(value, depth, seen) {
|
|
148
212
|
if (value === null || value === void 0) {
|
|
@@ -195,12 +259,14 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
195
259
|
seen.delete(value);
|
|
196
260
|
return result;
|
|
197
261
|
}
|
|
198
|
-
var import_superjson, MAX_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
262
|
+
var import_superjson, MAX_SERIALIZED_BYTES, MAX_FRAMEWORK_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
199
263
|
var init_serialize = __esm({
|
|
200
264
|
"src/serialize.ts"() {
|
|
201
265
|
"use strict";
|
|
202
266
|
import_superjson = __toESM(require("superjson"), 1);
|
|
267
|
+
init_warnOnce();
|
|
203
268
|
MAX_SERIALIZED_BYTES = 512e3;
|
|
269
|
+
MAX_FRAMEWORK_SERIALIZED_BYTES = 2e6;
|
|
204
270
|
MAX_SAFE_DEPTH = 6;
|
|
205
271
|
}
|
|
206
272
|
});
|
|
@@ -286,7 +352,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
286
352
|
let originalOutput;
|
|
287
353
|
let result;
|
|
288
354
|
let error = null;
|
|
289
|
-
const replayedTraceId =
|
|
355
|
+
const replayedTraceId = randomUuid();
|
|
290
356
|
const pendingPersistence = [];
|
|
291
357
|
try {
|
|
292
358
|
const span = await httpClient.getExternalSpan(serverItem.externalSpanId);
|
|
@@ -346,7 +412,10 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
346
412
|
originalOutput,
|
|
347
413
|
error,
|
|
348
414
|
durationMs: serverItem.durationMs ?? null,
|
|
349
|
-
|
|
415
|
+
// Filled in by replay() from the complete-replay response once the
|
|
416
|
+
// replay traces are persisted and their spans aggregated server-side.
|
|
417
|
+
// Null here (and on older servers) means "replay tokens not known".
|
|
418
|
+
tokens: null,
|
|
350
419
|
model: serverItem.model ?? null,
|
|
351
420
|
dbSnapshotRef: serverItem.dbSnapshotRef ?? null
|
|
352
421
|
};
|
|
@@ -420,6 +489,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
420
489
|
const resultItems = await mapWithConcurrency(tasks, maxConcurrency);
|
|
421
490
|
const completeResult = await httpClient.completeReplay(testRunId);
|
|
422
491
|
const serverTraceIds = completeResult.traceIds;
|
|
492
|
+
const replayTokens = completeResult.tokens;
|
|
423
493
|
if (serverTraceIds === void 0) {
|
|
424
494
|
try {
|
|
425
495
|
console.warn(
|
|
@@ -442,6 +512,9 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
442
512
|
missing.push(item.traceId);
|
|
443
513
|
}
|
|
444
514
|
}
|
|
515
|
+
if (mapped !== void 0) {
|
|
516
|
+
item.tokens = replayTokens?.[mapped] ?? null;
|
|
517
|
+
}
|
|
445
518
|
item.traceId = mapped ?? null;
|
|
446
519
|
}
|
|
447
520
|
}
|
|
@@ -470,6 +543,7 @@ var init_replay = __esm({
|
|
|
470
543
|
"src/replay.ts"() {
|
|
471
544
|
"use strict";
|
|
472
545
|
init_errors();
|
|
546
|
+
init_randomUuid();
|
|
473
547
|
init_replayContext();
|
|
474
548
|
init_serialize();
|
|
475
549
|
}
|
|
@@ -506,13 +580,24 @@ registerAsyncLocalStorageClass(
|
|
|
506
580
|
);
|
|
507
581
|
|
|
508
582
|
// src/version.generated.ts
|
|
509
|
-
var __version__ = "0.
|
|
583
|
+
var __version__ = "0.24.1";
|
|
510
584
|
|
|
511
585
|
// src/constants.ts
|
|
512
586
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
513
587
|
|
|
514
588
|
// src/http.ts
|
|
515
589
|
init_errors();
|
|
590
|
+
|
|
591
|
+
// src/unrefTimer.ts
|
|
592
|
+
function unrefTimer(timer) {
|
|
593
|
+
const handle = timer;
|
|
594
|
+
if (typeof handle.unref === "function") {
|
|
595
|
+
handle.unref();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/http.ts
|
|
600
|
+
init_warnOnce();
|
|
516
601
|
function serializePayloadBody(payload) {
|
|
517
602
|
try {
|
|
518
603
|
return { body: JSON.stringify(payload), dropped: [] };
|
|
@@ -557,11 +642,20 @@ function serializePayloadBody(payload) {
|
|
|
557
642
|
result = `<unserializable: ${className}>`;
|
|
558
643
|
}
|
|
559
644
|
} else {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
645
|
+
try {
|
|
646
|
+
const out = {};
|
|
647
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
648
|
+
out[k] = sanitize(v, seen);
|
|
649
|
+
}
|
|
650
|
+
result = out;
|
|
651
|
+
} catch {
|
|
652
|
+
warnOnce(
|
|
653
|
+
"payload:field-getter-threw",
|
|
654
|
+
"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."
|
|
655
|
+
);
|
|
656
|
+
dropped.push(className);
|
|
657
|
+
result = `<unserializable: ${className}>`;
|
|
563
658
|
}
|
|
564
|
-
result = out;
|
|
565
659
|
}
|
|
566
660
|
seen.delete(obj);
|
|
567
661
|
return result;
|
|
@@ -606,10 +700,20 @@ async function flushTraces(timeoutMs = 5e3) {
|
|
|
606
700
|
if (pendingTracePromises.size === 0) {
|
|
607
701
|
return;
|
|
608
702
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
703
|
+
let timer;
|
|
704
|
+
try {
|
|
705
|
+
await Promise.race([
|
|
706
|
+
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
707
|
+
new Promise((resolve) => {
|
|
708
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
709
|
+
unrefTimer(timer);
|
|
710
|
+
})
|
|
711
|
+
]);
|
|
712
|
+
} finally {
|
|
713
|
+
if (timer) {
|
|
714
|
+
clearTimeout(timer);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
613
717
|
}
|
|
614
718
|
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
615
719
|
let isFlushing = false;
|
|
@@ -919,6 +1023,7 @@ var HttpClient = class {
|
|
|
919
1023
|
};
|
|
920
1024
|
|
|
921
1025
|
// src/claudeAgentSdk.ts
|
|
1026
|
+
init_randomUuid();
|
|
922
1027
|
init_serialize();
|
|
923
1028
|
function nowIso() {
|
|
924
1029
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1006,7 +1111,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1006
1111
|
if (this.activeContext) {
|
|
1007
1112
|
this.traceId = this.activeContext.traceId;
|
|
1008
1113
|
} else {
|
|
1009
|
-
this.traceId =
|
|
1114
|
+
this.traceId = randomUuid();
|
|
1010
1115
|
}
|
|
1011
1116
|
this.traceStartedAt = nowIso();
|
|
1012
1117
|
return this.traceId;
|
|
@@ -1031,7 +1136,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1031
1136
|
if (this.activeContext !== null) {
|
|
1032
1137
|
return;
|
|
1033
1138
|
}
|
|
1034
|
-
const spanId =
|
|
1139
|
+
const spanId = randomUuid();
|
|
1035
1140
|
this.startSpan(spanId, this.traceFunctionKey, "agent", this.rootInput, null);
|
|
1036
1141
|
this.rootSpanId = spanId;
|
|
1037
1142
|
}
|
|
@@ -1145,7 +1250,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1145
1250
|
// ── hook callbacks ───────────────────────────────────────────
|
|
1146
1251
|
async preToolUseHook(inputData, toolUseId, _context) {
|
|
1147
1252
|
try {
|
|
1148
|
-
const sid = inputData.tool_use_id ?? toolUseId ??
|
|
1253
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? randomUuid();
|
|
1149
1254
|
const toolName = inputData.tool_name ?? "tool";
|
|
1150
1255
|
const toolInput = inputData.tool_input ?? {};
|
|
1151
1256
|
const agentId = inputData.agent_id;
|
|
@@ -1175,10 +1280,10 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1175
1280
|
}
|
|
1176
1281
|
async subagentStartHook(inputData, _toolUseId, _context) {
|
|
1177
1282
|
try {
|
|
1178
|
-
const agentId = inputData.agent_id ??
|
|
1283
|
+
const agentId = inputData.agent_id ?? randomUuid();
|
|
1179
1284
|
const agentType = inputData.agent_type ?? "subagent";
|
|
1180
1285
|
const parentId = this.getParentId();
|
|
1181
|
-
const spanId =
|
|
1286
|
+
const spanId = randomUuid();
|
|
1182
1287
|
this.activeSubagentSpans.set(agentId, spanId);
|
|
1183
1288
|
this.startSpan(
|
|
1184
1289
|
spanId,
|
|
@@ -1319,7 +1424,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1319
1424
|
this.flushLlmTurn();
|
|
1320
1425
|
this.conversationHistory.push(...this.pendingMessages);
|
|
1321
1426
|
this.pendingMessages = [];
|
|
1322
|
-
this.currentLlmSpanId =
|
|
1427
|
+
this.currentLlmSpanId = randomUuid();
|
|
1323
1428
|
this.currentLlmMessageId = messageId ?? null;
|
|
1324
1429
|
this.currentLlmContent = [];
|
|
1325
1430
|
this.currentLlmModel = inner.model ?? null;
|
|
@@ -1763,6 +1868,7 @@ function buildSnapshotRef(config, sdkWallClockBeforeFn) {
|
|
|
1763
1868
|
}
|
|
1764
1869
|
|
|
1765
1870
|
// src/langgraph.ts
|
|
1871
|
+
init_randomUuid();
|
|
1766
1872
|
init_serialize();
|
|
1767
1873
|
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
1768
1874
|
var LANGGRAPH_METADATA_KEYS = [
|
|
@@ -2011,7 +2117,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2011
2117
|
} else {
|
|
2012
2118
|
const activeContext = this.getActiveSpanContext?.() ?? null;
|
|
2013
2119
|
invocation = {
|
|
2014
|
-
traceId: activeContext ? activeContext.traceId :
|
|
2120
|
+
traceId: activeContext ? activeContext.traceId : randomUuid(),
|
|
2015
2121
|
activeContext,
|
|
2016
2122
|
rootRunId: runId
|
|
2017
2123
|
};
|
|
@@ -2306,6 +2412,20 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2306
2412
|
this.withSpanFn = config.withSpan;
|
|
2307
2413
|
this.getActiveSpanContext = config.getActiveSpanContext;
|
|
2308
2414
|
}
|
|
2415
|
+
/**
|
|
2416
|
+
* Drop-in replacement for the OpenAI Agents SDK's `run()` that records a
|
|
2417
|
+
* replayable root `agent` span.
|
|
2418
|
+
*
|
|
2419
|
+
* The `input` is captured as the root span's input (as a single positional
|
|
2420
|
+
* argument, so `replay(key, fn)` re-feeds it), and the run's `finalOutput`
|
|
2421
|
+
* is recorded as the root output. For streaming runs (`{ stream: true }`),
|
|
2422
|
+
* the result is handed back immediately and the final output is recorded
|
|
2423
|
+
* once the stream completes — first-byte latency is untouched.
|
|
2424
|
+
*
|
|
2425
|
+
* The process-wide tracing processor (`getOpenAiTracingProcessor`) must still
|
|
2426
|
+
* be registered: it captures the LLM/tool/handoff spans that nest beneath
|
|
2427
|
+
* this root.
|
|
2428
|
+
*/
|
|
2309
2429
|
async wrapRun(agent, input, options) {
|
|
2310
2430
|
const { run } = await importOptionalPeer([
|
|
2311
2431
|
"@openai",
|
|
@@ -2344,6 +2464,7 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2344
2464
|
};
|
|
2345
2465
|
|
|
2346
2466
|
// src/client.ts
|
|
2467
|
+
init_randomUuid();
|
|
2347
2468
|
init_replayContext();
|
|
2348
2469
|
|
|
2349
2470
|
// src/replayEnvironment.ts
|
|
@@ -2763,6 +2884,7 @@ var BitfabVercelAiHandler = class {
|
|
|
2763
2884
|
};
|
|
2764
2885
|
|
|
2765
2886
|
// src/client.ts
|
|
2887
|
+
init_warnOnce();
|
|
2766
2888
|
var activeTraceStates = /* @__PURE__ */ new Map();
|
|
2767
2889
|
var pendingSpanPromises = /* @__PURE__ */ new Map();
|
|
2768
2890
|
var asyncLocalStorage = null;
|
|
@@ -3121,7 +3243,20 @@ var Bitfab = class {
|
|
|
3121
3243
|
functionVersion.providers,
|
|
3122
3244
|
this.envVars
|
|
3123
3245
|
);
|
|
3124
|
-
|
|
3246
|
+
let resultStr;
|
|
3247
|
+
if (typeof executionResult.result === "string") {
|
|
3248
|
+
resultStr = executionResult.result;
|
|
3249
|
+
} else {
|
|
3250
|
+
try {
|
|
3251
|
+
resultStr = JSON.stringify(executionResult.result) ?? String(executionResult.result);
|
|
3252
|
+
} catch {
|
|
3253
|
+
warnOnce(
|
|
3254
|
+
"call-result-serialize",
|
|
3255
|
+
"a local execution result could not be JSON-serialized; storing its String() form instead. The call still returns its real value."
|
|
3256
|
+
);
|
|
3257
|
+
resultStr = String(executionResult.result);
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3125
3260
|
this.httpClient.sendInternalTrace(functionVersion.id, {
|
|
3126
3261
|
result: resultStr,
|
|
3127
3262
|
source: "typescript-sdk",
|
|
@@ -3370,11 +3505,26 @@ var Bitfab = class {
|
|
|
3370
3505
|
return await bamlClient[methodName](...args);
|
|
3371
3506
|
}
|
|
3372
3507
|
const collector = new CollectorClass("bitfab-baml-tracing");
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
trackedClient
|
|
3377
|
-
|
|
3508
|
+
let trackedClient;
|
|
3509
|
+
let trackedMethod;
|
|
3510
|
+
try {
|
|
3511
|
+
trackedClient = bamlClient.withOptions({ collector });
|
|
3512
|
+
const method2 = trackedClient[methodName];
|
|
3513
|
+
if (typeof method2 !== "function") {
|
|
3514
|
+
throw new BitfabError(
|
|
3515
|
+
"bamlClient.withOptions did not return the wrapped method"
|
|
3516
|
+
);
|
|
3517
|
+
}
|
|
3518
|
+
trackedMethod = method2;
|
|
3519
|
+
} catch {
|
|
3520
|
+
warnOnce(
|
|
3521
|
+
`wrapBAML-setup:${methodName}`,
|
|
3522
|
+
`BAML tracing setup failed for "${methodName}" (incompatible bamlClient or BAML version); calling it untraced. The call still runs; no span is recorded.`
|
|
3523
|
+
);
|
|
3524
|
+
wrappedFn.collector = null;
|
|
3525
|
+
return await bamlClient[methodName](...args);
|
|
3526
|
+
}
|
|
3527
|
+
const result = await trackedMethod.bind(trackedClient)(...args);
|
|
3378
3528
|
wrappedFn.collector = collector;
|
|
3379
3529
|
try {
|
|
3380
3530
|
const prompt = extractPromptFromCollector(collector);
|
|
@@ -3450,200 +3600,229 @@ var Bitfab = class {
|
|
|
3450
3600
|
() => wrappedFn.apply(this, args)
|
|
3451
3601
|
);
|
|
3452
3602
|
}
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
const
|
|
3467
|
-
|
|
3603
|
+
let newStack;
|
|
3604
|
+
let executeWithContext;
|
|
3605
|
+
let registeredTraceId;
|
|
3606
|
+
try {
|
|
3607
|
+
const currentStack = getSpanStack();
|
|
3608
|
+
const parentContext = currentStack[currentStack.length - 1];
|
|
3609
|
+
const replayCtxForTraceId = parentContext ? null : getReplayContext();
|
|
3610
|
+
const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? randomUuid();
|
|
3611
|
+
const spanId = randomUuid();
|
|
3612
|
+
const parentSpanId = parentContext?.spanId ?? null;
|
|
3613
|
+
const isRootSpan = parentSpanId === null;
|
|
3614
|
+
const newContext = { traceId, spanId, contexts: [] };
|
|
3615
|
+
newStack = [...currentStack, newContext];
|
|
3616
|
+
const inputs = args;
|
|
3617
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3618
|
+
if (isRootSpan && !activeTraceStates.has(traceId)) {
|
|
3619
|
+
const replayCtxAtRoot = getReplayContext();
|
|
3620
|
+
const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
|
|
3621
|
+
activeTraceStates.set(traceId, {
|
|
3622
|
+
traceId,
|
|
3623
|
+
startedAt,
|
|
3624
|
+
contexts: [],
|
|
3625
|
+
...replayCtxAtRoot?.testRunId && {
|
|
3626
|
+
testRunId: replayCtxAtRoot.testRunId
|
|
3627
|
+
},
|
|
3628
|
+
...replayCtxAtRoot?.inputSourceTraceId && {
|
|
3629
|
+
inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
|
|
3630
|
+
},
|
|
3631
|
+
dbSnapshotRef
|
|
3632
|
+
});
|
|
3633
|
+
pendingSpanPromises.set(traceId, []);
|
|
3634
|
+
registeredTraceId = traceId;
|
|
3635
|
+
}
|
|
3636
|
+
const functionName = fn.name !== "" ? fn.name : void 0;
|
|
3637
|
+
const baseSpanParams = {
|
|
3638
|
+
traceFunctionKey,
|
|
3639
|
+
functionName,
|
|
3640
|
+
spanName: options.name ?? functionName ?? traceFunctionKey,
|
|
3468
3641
|
traceId,
|
|
3642
|
+
spanId,
|
|
3643
|
+
parentSpanId,
|
|
3644
|
+
inputs,
|
|
3469
3645
|
startedAt,
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
startedAt,
|
|
3491
|
-
spanType: options.type ?? "custom"
|
|
3492
|
-
};
|
|
3493
|
-
const sendSpan = async (params, spanOpts) => {
|
|
3494
|
-
const replayCtx = getReplayContext();
|
|
3495
|
-
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3496
|
-
let resolvePersistence;
|
|
3497
|
-
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3498
|
-
persistenceCollector.push(
|
|
3499
|
-
new Promise((resolve) => {
|
|
3500
|
-
resolvePersistence = resolve;
|
|
3501
|
-
})
|
|
3502
|
-
);
|
|
3503
|
-
}
|
|
3504
|
-
try {
|
|
3505
|
-
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3506
|
-
const spanPromise = self.sendWrapperSpan({
|
|
3507
|
-
...baseSpanParams,
|
|
3508
|
-
...params,
|
|
3509
|
-
contexts: newContext.contexts,
|
|
3510
|
-
prompt: newContext.prompt,
|
|
3511
|
-
endedAt,
|
|
3512
|
-
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3513
|
-
...replayCtx?.inputSourceSpanId && {
|
|
3514
|
-
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3515
|
-
}
|
|
3516
|
-
});
|
|
3517
|
-
if (isRootSpan) {
|
|
3518
|
-
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3519
|
-
pending.push(spanPromise);
|
|
3520
|
-
if (persistenceCollector) {
|
|
3521
|
-
await Promise.allSettled(pending);
|
|
3522
|
-
} else {
|
|
3523
|
-
await Promise.race([
|
|
3524
|
-
Promise.allSettled(pending),
|
|
3525
|
-
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
3526
|
-
]);
|
|
3527
|
-
}
|
|
3528
|
-
pendingSpanPromises.delete(traceId);
|
|
3529
|
-
const traceState = activeTraceStates.get(traceId);
|
|
3530
|
-
const completionPromise = self.sendTraceCompletion({
|
|
3531
|
-
traceFunctionKey,
|
|
3532
|
-
traceId,
|
|
3533
|
-
startedAt: traceState?.startedAt ?? startedAt,
|
|
3646
|
+
spanType: options.type ?? "custom"
|
|
3647
|
+
};
|
|
3648
|
+
const sendSpan = async (params, spanOpts) => {
|
|
3649
|
+
const replayCtx = getReplayContext();
|
|
3650
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3651
|
+
let resolvePersistence;
|
|
3652
|
+
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3653
|
+
persistenceCollector.push(
|
|
3654
|
+
new Promise((resolve) => {
|
|
3655
|
+
resolvePersistence = resolve;
|
|
3656
|
+
})
|
|
3657
|
+
);
|
|
3658
|
+
}
|
|
3659
|
+
try {
|
|
3660
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3661
|
+
const spanPromise = self.sendWrapperSpan({
|
|
3662
|
+
...baseSpanParams,
|
|
3663
|
+
...params,
|
|
3664
|
+
contexts: newContext.contexts,
|
|
3665
|
+
prompt: newContext.prompt,
|
|
3534
3666
|
endedAt,
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
testRunId: traceState?.testRunId,
|
|
3539
|
-
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3540
|
-
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3541
|
-
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3542
|
-
// whether customer code obtained the branch URL during this
|
|
3543
|
-
// item. Omitted entirely when no lease was attached, so the
|
|
3544
|
-
// server can distinguish "no branch" from "branch ignored".
|
|
3545
|
-
...replayCtx?.dbBranchLease && {
|
|
3546
|
-
dbSnapshotUsage: {
|
|
3547
|
-
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3548
|
-
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3549
|
-
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3550
|
-
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3551
|
-
}
|
|
3667
|
+
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3668
|
+
...replayCtx?.inputSourceSpanId && {
|
|
3669
|
+
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3552
3670
|
}
|
|
3553
3671
|
});
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
await completionPromise;
|
|
3557
|
-
}
|
|
3558
|
-
} else {
|
|
3559
|
-
const pending = pendingSpanPromises.get(traceId);
|
|
3560
|
-
if (pending) {
|
|
3672
|
+
if (isRootSpan) {
|
|
3673
|
+
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3561
3674
|
pending.push(spanPromise);
|
|
3675
|
+
if (persistenceCollector) {
|
|
3676
|
+
await Promise.allSettled(pending);
|
|
3677
|
+
} else {
|
|
3678
|
+
let raceTimer;
|
|
3679
|
+
try {
|
|
3680
|
+
await Promise.race([
|
|
3681
|
+
Promise.allSettled(pending),
|
|
3682
|
+
new Promise((resolve) => {
|
|
3683
|
+
raceTimer = setTimeout(resolve, 5e3);
|
|
3684
|
+
unrefTimer(raceTimer);
|
|
3685
|
+
})
|
|
3686
|
+
]);
|
|
3687
|
+
} finally {
|
|
3688
|
+
if (raceTimer) {
|
|
3689
|
+
clearTimeout(raceTimer);
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
pendingSpanPromises.delete(traceId);
|
|
3694
|
+
const traceState = activeTraceStates.get(traceId);
|
|
3695
|
+
const completionPromise = self.sendTraceCompletion({
|
|
3696
|
+
traceFunctionKey,
|
|
3697
|
+
traceId,
|
|
3698
|
+
startedAt: traceState?.startedAt ?? startedAt,
|
|
3699
|
+
endedAt,
|
|
3700
|
+
sessionId: traceState?.sessionId,
|
|
3701
|
+
metadata: traceState?.metadata,
|
|
3702
|
+
contexts: traceState?.contexts ?? [],
|
|
3703
|
+
testRunId: traceState?.testRunId,
|
|
3704
|
+
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3705
|
+
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3706
|
+
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3707
|
+
// whether customer code obtained the branch URL during this
|
|
3708
|
+
// item. Omitted entirely when no lease was attached, so the
|
|
3709
|
+
// server can distinguish "no branch" from "branch ignored".
|
|
3710
|
+
...replayCtx?.dbBranchLease && {
|
|
3711
|
+
dbSnapshotUsage: {
|
|
3712
|
+
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3713
|
+
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3714
|
+
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3715
|
+
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
});
|
|
3719
|
+
activeTraceStates.delete(traceId);
|
|
3720
|
+
if (persistenceCollector) {
|
|
3721
|
+
await completionPromise;
|
|
3722
|
+
}
|
|
3562
3723
|
} else {
|
|
3563
|
-
pendingSpanPromises.
|
|
3724
|
+
const pending = pendingSpanPromises.get(traceId);
|
|
3725
|
+
if (pending) {
|
|
3726
|
+
pending.push(spanPromise);
|
|
3727
|
+
} else {
|
|
3728
|
+
pendingSpanPromises.set(traceId, [spanPromise]);
|
|
3729
|
+
}
|
|
3564
3730
|
}
|
|
3731
|
+
} catch {
|
|
3732
|
+
} finally {
|
|
3733
|
+
resolvePersistence?.();
|
|
3565
3734
|
}
|
|
3566
|
-
}
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
if (fnReturnsPromise) {
|
|
3591
|
-
return Promise.resolve(output);
|
|
3735
|
+
};
|
|
3736
|
+
const replayCtxForMock = getReplayContext();
|
|
3737
|
+
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
3738
|
+
const counters = replayCtxForMock.callCounters;
|
|
3739
|
+
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
3740
|
+
const callIndex = counters.get(counterKey) ?? 0;
|
|
3741
|
+
counters.set(counterKey, callIndex + 1);
|
|
3742
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
3743
|
+
if (shouldMock) {
|
|
3744
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
3745
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
3746
|
+
if (mockSpan) {
|
|
3747
|
+
let output = mockSpan.output;
|
|
3748
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
3749
|
+
output = deserializeValue({
|
|
3750
|
+
json: mockSpan.output,
|
|
3751
|
+
meta: mockSpan.outputMeta
|
|
3752
|
+
});
|
|
3753
|
+
}
|
|
3754
|
+
void sendSpan({ result: output });
|
|
3755
|
+
if (fnReturnsPromise) {
|
|
3756
|
+
return Promise.resolve(output);
|
|
3757
|
+
}
|
|
3758
|
+
return output;
|
|
3592
3759
|
}
|
|
3593
|
-
return output;
|
|
3594
3760
|
}
|
|
3595
3761
|
}
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3762
|
+
const recordSpan = (result) => {
|
|
3763
|
+
if (options.finalize) {
|
|
3764
|
+
const replayCtx = getReplayContext();
|
|
3765
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3766
|
+
let resolvePersistence;
|
|
3767
|
+
if (persistenceCollector) {
|
|
3768
|
+
persistenceCollector.push(
|
|
3769
|
+
new Promise((resolve) => {
|
|
3770
|
+
resolvePersistence = resolve;
|
|
3771
|
+
})
|
|
3772
|
+
);
|
|
3773
|
+
}
|
|
3774
|
+
void Promise.resolve().then(() => options.finalize(result)).then(
|
|
3775
|
+
(output) => sendSpan(
|
|
3776
|
+
{ result: output },
|
|
3777
|
+
{ skipPersistenceRegistration: true }
|
|
3778
|
+
)
|
|
3779
|
+
).catch(
|
|
3780
|
+
(error) => sendSpan(
|
|
3781
|
+
{
|
|
3782
|
+
result: void 0,
|
|
3783
|
+
error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
|
|
3784
|
+
},
|
|
3785
|
+
{ skipPersistenceRegistration: true }
|
|
3786
|
+
)
|
|
3787
|
+
).finally(() => resolvePersistence?.());
|
|
3788
|
+
} else {
|
|
3789
|
+
void sendSpan({ result });
|
|
3608
3790
|
}
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
)
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3791
|
+
};
|
|
3792
|
+
executeWithContext = () => {
|
|
3793
|
+
const result = fn(...args);
|
|
3794
|
+
if (result instanceof Promise) {
|
|
3795
|
+
return result.then((resolvedResult) => {
|
|
3796
|
+
recordSpan(resolvedResult);
|
|
3797
|
+
return resolvedResult;
|
|
3798
|
+
}).catch((error) => {
|
|
3799
|
+
void sendSpan({
|
|
3617
3800
|
result: void 0,
|
|
3618
|
-
error: error instanceof Error ?
|
|
3619
|
-
}
|
|
3620
|
-
|
|
3621
|
-
)
|
|
3622
|
-
).finally(() => resolvePersistence?.());
|
|
3623
|
-
} else {
|
|
3624
|
-
void sendSpan({ result });
|
|
3625
|
-
}
|
|
3626
|
-
};
|
|
3627
|
-
const executeWithContext = () => {
|
|
3628
|
-
const result = fn(...args);
|
|
3629
|
-
if (result instanceof Promise) {
|
|
3630
|
-
return result.then((resolvedResult) => {
|
|
3631
|
-
recordSpan(resolvedResult);
|
|
3632
|
-
return resolvedResult;
|
|
3633
|
-
}).catch((error) => {
|
|
3634
|
-
void sendSpan({
|
|
3635
|
-
result: void 0,
|
|
3636
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3801
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3802
|
+
});
|
|
3803
|
+
throw error;
|
|
3637
3804
|
});
|
|
3638
|
-
|
|
3639
|
-
|
|
3805
|
+
}
|
|
3806
|
+
if (isAsyncGenerator(result)) {
|
|
3807
|
+
return wrapAsyncGenerator(result, newStack, sendSpan);
|
|
3808
|
+
}
|
|
3809
|
+
recordSpan(result);
|
|
3810
|
+
return result;
|
|
3811
|
+
};
|
|
3812
|
+
} catch (setupError) {
|
|
3813
|
+
if (registeredTraceId) {
|
|
3814
|
+
activeTraceStates.delete(registeredTraceId);
|
|
3815
|
+
pendingSpanPromises.delete(registeredTraceId);
|
|
3640
3816
|
}
|
|
3641
|
-
if (
|
|
3642
|
-
|
|
3817
|
+
if (getReplayContext()) {
|
|
3818
|
+
throw setupError;
|
|
3643
3819
|
}
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3820
|
+
warnOnce(
|
|
3821
|
+
`withSpan-setup:${traceFunctionKey}`,
|
|
3822
|
+
`tracing setup failed for "${traceFunctionKey}"; running it untraced. The function still runs and returns normally; no span is recorded.`
|
|
3823
|
+
);
|
|
3824
|
+
return fn(...args);
|
|
3825
|
+
}
|
|
3647
3826
|
return runWithSpanStack(newStack, executeWithContext);
|
|
3648
3827
|
};
|
|
3649
3828
|
Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
|