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/node.cjs
CHANGED
|
@@ -100,6 +100,54 @@ var init_errors = __esm({
|
|
|
100
100
|
}
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
+
// src/warnOnce.ts
|
|
104
|
+
function warnOnce(key, message) {
|
|
105
|
+
if (warned.has(key)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
warned.add(key);
|
|
109
|
+
try {
|
|
110
|
+
console.warn(`[bitfab] ${message}`);
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
var warned;
|
|
115
|
+
var init_warnOnce = __esm({
|
|
116
|
+
"src/warnOnce.ts"() {
|
|
117
|
+
"use strict";
|
|
118
|
+
warned = /* @__PURE__ */ new Set();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// src/randomUuid.ts
|
|
123
|
+
function randomUuid() {
|
|
124
|
+
const globalCrypto = globalThis.crypto;
|
|
125
|
+
if (typeof globalCrypto?.randomUUID === "function") {
|
|
126
|
+
try {
|
|
127
|
+
return globalCrypto.randomUUID();
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
warnOnce(
|
|
132
|
+
"crypto-unavailable",
|
|
133
|
+
"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
|
|
134
|
+
);
|
|
135
|
+
return fallbackUuidV4();
|
|
136
|
+
}
|
|
137
|
+
function fallbackUuidV4() {
|
|
138
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
139
|
+
const rand = Math.random() * 16 | 0;
|
|
140
|
+
const value = char === "x" ? rand : rand & 3 | 8;
|
|
141
|
+
return value.toString(16);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
var init_randomUuid = __esm({
|
|
145
|
+
"src/randomUuid.ts"() {
|
|
146
|
+
"use strict";
|
|
147
|
+
init_warnOnce();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
103
151
|
// src/serialize.ts
|
|
104
152
|
function describeValue(value) {
|
|
105
153
|
try {
|
|
@@ -112,6 +160,10 @@ function describeValue(value) {
|
|
|
112
160
|
return typeof value;
|
|
113
161
|
}
|
|
114
162
|
function unserializableStub(value, reason) {
|
|
163
|
+
warnOnce(
|
|
164
|
+
`serialize:${reason.replace(/\d+/g, "N")}`,
|
|
165
|
+
`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.`
|
|
166
|
+
);
|
|
115
167
|
let summary;
|
|
116
168
|
try {
|
|
117
169
|
summary = `<unserializable: ${describeValue(value)} (${reason})>`;
|
|
@@ -151,7 +203,19 @@ function deserializeValue(serialized) {
|
|
|
151
203
|
});
|
|
152
204
|
}
|
|
153
205
|
function toJsonSafe(value) {
|
|
154
|
-
|
|
206
|
+
const safe = toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet());
|
|
207
|
+
try {
|
|
208
|
+
const size = JSON.stringify(safe)?.length ?? 0;
|
|
209
|
+
if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {
|
|
210
|
+
warnOnce(
|
|
211
|
+
"toJsonSafe:too_large",
|
|
212
|
+
`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.`
|
|
213
|
+
);
|
|
214
|
+
return `<unserializable: too_large_${size}_bytes>`;
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
}
|
|
218
|
+
return safe;
|
|
155
219
|
}
|
|
156
220
|
function toJsonSafeInner(value, depth, seen) {
|
|
157
221
|
if (value === null || value === void 0) {
|
|
@@ -204,12 +268,14 @@ function toJsonSafeInner(value, depth, seen) {
|
|
|
204
268
|
seen.delete(value);
|
|
205
269
|
return result;
|
|
206
270
|
}
|
|
207
|
-
var import_superjson, MAX_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
271
|
+
var import_superjson, MAX_SERIALIZED_BYTES, MAX_FRAMEWORK_SERIALIZED_BYTES, MAX_SAFE_DEPTH;
|
|
208
272
|
var init_serialize = __esm({
|
|
209
273
|
"src/serialize.ts"() {
|
|
210
274
|
"use strict";
|
|
211
275
|
import_superjson = __toESM(require("superjson"), 1);
|
|
276
|
+
init_warnOnce();
|
|
212
277
|
MAX_SERIALIZED_BYTES = 512e3;
|
|
278
|
+
MAX_FRAMEWORK_SERIALIZED_BYTES = 2e6;
|
|
213
279
|
MAX_SAFE_DEPTH = 6;
|
|
214
280
|
}
|
|
215
281
|
});
|
|
@@ -295,7 +361,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
295
361
|
let originalOutput;
|
|
296
362
|
let result;
|
|
297
363
|
let error = null;
|
|
298
|
-
const replayedTraceId =
|
|
364
|
+
const replayedTraceId = randomUuid();
|
|
299
365
|
const pendingPersistence = [];
|
|
300
366
|
try {
|
|
301
367
|
const span = await httpClient.getExternalSpan(serverItem.externalSpanId);
|
|
@@ -355,7 +421,10 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
355
421
|
originalOutput,
|
|
356
422
|
error,
|
|
357
423
|
durationMs: serverItem.durationMs ?? null,
|
|
358
|
-
|
|
424
|
+
// Filled in by replay() from the complete-replay response once the
|
|
425
|
+
// replay traces are persisted and their spans aggregated server-side.
|
|
426
|
+
// Null here (and on older servers) means "replay tokens not known".
|
|
427
|
+
tokens: null,
|
|
359
428
|
model: serverItem.model ?? null,
|
|
360
429
|
dbSnapshotRef: serverItem.dbSnapshotRef ?? null
|
|
361
430
|
};
|
|
@@ -429,6 +498,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
429
498
|
const resultItems = await mapWithConcurrency(tasks, maxConcurrency);
|
|
430
499
|
const completeResult = await httpClient.completeReplay(testRunId);
|
|
431
500
|
const serverTraceIds = completeResult.traceIds;
|
|
501
|
+
const replayTokens = completeResult.tokens;
|
|
432
502
|
if (serverTraceIds === void 0) {
|
|
433
503
|
try {
|
|
434
504
|
console.warn(
|
|
@@ -451,6 +521,9 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
451
521
|
missing.push(item.traceId);
|
|
452
522
|
}
|
|
453
523
|
}
|
|
524
|
+
if (mapped !== void 0) {
|
|
525
|
+
item.tokens = replayTokens?.[mapped] ?? null;
|
|
526
|
+
}
|
|
454
527
|
item.traceId = mapped ?? null;
|
|
455
528
|
}
|
|
456
529
|
}
|
|
@@ -479,6 +552,7 @@ var init_replay = __esm({
|
|
|
479
552
|
"src/replay.ts"() {
|
|
480
553
|
"use strict";
|
|
481
554
|
init_errors();
|
|
555
|
+
init_randomUuid();
|
|
482
556
|
init_replayContext();
|
|
483
557
|
init_serialize();
|
|
484
558
|
}
|
|
@@ -515,13 +589,24 @@ registerAsyncLocalStorageClass(
|
|
|
515
589
|
);
|
|
516
590
|
|
|
517
591
|
// src/version.generated.ts
|
|
518
|
-
var __version__ = "0.
|
|
592
|
+
var __version__ = "0.24.1";
|
|
519
593
|
|
|
520
594
|
// src/constants.ts
|
|
521
595
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
522
596
|
|
|
523
597
|
// src/http.ts
|
|
524
598
|
init_errors();
|
|
599
|
+
|
|
600
|
+
// src/unrefTimer.ts
|
|
601
|
+
function unrefTimer(timer) {
|
|
602
|
+
const handle = timer;
|
|
603
|
+
if (typeof handle.unref === "function") {
|
|
604
|
+
handle.unref();
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// src/http.ts
|
|
609
|
+
init_warnOnce();
|
|
525
610
|
function serializePayloadBody(payload) {
|
|
526
611
|
try {
|
|
527
612
|
return { body: JSON.stringify(payload), dropped: [] };
|
|
@@ -566,11 +651,20 @@ function serializePayloadBody(payload) {
|
|
|
566
651
|
result = `<unserializable: ${className}>`;
|
|
567
652
|
}
|
|
568
653
|
} else {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
654
|
+
try {
|
|
655
|
+
const out = {};
|
|
656
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
657
|
+
out[k] = sanitize(v, seen);
|
|
658
|
+
}
|
|
659
|
+
result = out;
|
|
660
|
+
} catch {
|
|
661
|
+
warnOnce(
|
|
662
|
+
"payload:field-getter-threw",
|
|
663
|
+
"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."
|
|
664
|
+
);
|
|
665
|
+
dropped.push(className);
|
|
666
|
+
result = `<unserializable: ${className}>`;
|
|
572
667
|
}
|
|
573
|
-
result = out;
|
|
574
668
|
}
|
|
575
669
|
seen.delete(obj);
|
|
576
670
|
return result;
|
|
@@ -615,10 +709,20 @@ async function flushTraces(timeoutMs = 5e3) {
|
|
|
615
709
|
if (pendingTracePromises.size === 0) {
|
|
616
710
|
return;
|
|
617
711
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
712
|
+
let timer;
|
|
713
|
+
try {
|
|
714
|
+
await Promise.race([
|
|
715
|
+
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
716
|
+
new Promise((resolve) => {
|
|
717
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
718
|
+
unrefTimer(timer);
|
|
719
|
+
})
|
|
720
|
+
]);
|
|
721
|
+
} finally {
|
|
722
|
+
if (timer) {
|
|
723
|
+
clearTimeout(timer);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
622
726
|
}
|
|
623
727
|
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
624
728
|
let isFlushing = false;
|
|
@@ -928,6 +1032,7 @@ var HttpClient = class {
|
|
|
928
1032
|
};
|
|
929
1033
|
|
|
930
1034
|
// src/claudeAgentSdk.ts
|
|
1035
|
+
init_randomUuid();
|
|
931
1036
|
init_serialize();
|
|
932
1037
|
function nowIso() {
|
|
933
1038
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1015,7 +1120,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1015
1120
|
if (this.activeContext) {
|
|
1016
1121
|
this.traceId = this.activeContext.traceId;
|
|
1017
1122
|
} else {
|
|
1018
|
-
this.traceId =
|
|
1123
|
+
this.traceId = randomUuid();
|
|
1019
1124
|
}
|
|
1020
1125
|
this.traceStartedAt = nowIso();
|
|
1021
1126
|
return this.traceId;
|
|
@@ -1040,7 +1145,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1040
1145
|
if (this.activeContext !== null) {
|
|
1041
1146
|
return;
|
|
1042
1147
|
}
|
|
1043
|
-
const spanId =
|
|
1148
|
+
const spanId = randomUuid();
|
|
1044
1149
|
this.startSpan(spanId, this.traceFunctionKey, "agent", this.rootInput, null);
|
|
1045
1150
|
this.rootSpanId = spanId;
|
|
1046
1151
|
}
|
|
@@ -1154,7 +1259,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1154
1259
|
// ── hook callbacks ───────────────────────────────────────────
|
|
1155
1260
|
async preToolUseHook(inputData, toolUseId, _context) {
|
|
1156
1261
|
try {
|
|
1157
|
-
const sid = inputData.tool_use_id ?? toolUseId ??
|
|
1262
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? randomUuid();
|
|
1158
1263
|
const toolName = inputData.tool_name ?? "tool";
|
|
1159
1264
|
const toolInput = inputData.tool_input ?? {};
|
|
1160
1265
|
const agentId = inputData.agent_id;
|
|
@@ -1184,10 +1289,10 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1184
1289
|
}
|
|
1185
1290
|
async subagentStartHook(inputData, _toolUseId, _context) {
|
|
1186
1291
|
try {
|
|
1187
|
-
const agentId = inputData.agent_id ??
|
|
1292
|
+
const agentId = inputData.agent_id ?? randomUuid();
|
|
1188
1293
|
const agentType = inputData.agent_type ?? "subagent";
|
|
1189
1294
|
const parentId = this.getParentId();
|
|
1190
|
-
const spanId =
|
|
1295
|
+
const spanId = randomUuid();
|
|
1191
1296
|
this.activeSubagentSpans.set(agentId, spanId);
|
|
1192
1297
|
this.startSpan(
|
|
1193
1298
|
spanId,
|
|
@@ -1328,7 +1433,7 @@ var BitfabClaudeAgentHandler = class {
|
|
|
1328
1433
|
this.flushLlmTurn();
|
|
1329
1434
|
this.conversationHistory.push(...this.pendingMessages);
|
|
1330
1435
|
this.pendingMessages = [];
|
|
1331
|
-
this.currentLlmSpanId =
|
|
1436
|
+
this.currentLlmSpanId = randomUuid();
|
|
1332
1437
|
this.currentLlmMessageId = messageId ?? null;
|
|
1333
1438
|
this.currentLlmContent = [];
|
|
1334
1439
|
this.currentLlmModel = inner.model ?? null;
|
|
@@ -1772,6 +1877,7 @@ function buildSnapshotRef(config, sdkWallClockBeforeFn) {
|
|
|
1772
1877
|
}
|
|
1773
1878
|
|
|
1774
1879
|
// src/langgraph.ts
|
|
1880
|
+
init_randomUuid();
|
|
1775
1881
|
init_serialize();
|
|
1776
1882
|
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
1777
1883
|
var LANGGRAPH_METADATA_KEYS = [
|
|
@@ -2020,7 +2126,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
2020
2126
|
} else {
|
|
2021
2127
|
const activeContext = this.getActiveSpanContext?.() ?? null;
|
|
2022
2128
|
invocation = {
|
|
2023
|
-
traceId: activeContext ? activeContext.traceId :
|
|
2129
|
+
traceId: activeContext ? activeContext.traceId : randomUuid(),
|
|
2024
2130
|
activeContext,
|
|
2025
2131
|
rootRunId: runId
|
|
2026
2132
|
};
|
|
@@ -2315,6 +2421,20 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2315
2421
|
this.withSpanFn = config.withSpan;
|
|
2316
2422
|
this.getActiveSpanContext = config.getActiveSpanContext;
|
|
2317
2423
|
}
|
|
2424
|
+
/**
|
|
2425
|
+
* Drop-in replacement for the OpenAI Agents SDK's `run()` that records a
|
|
2426
|
+
* replayable root `agent` span.
|
|
2427
|
+
*
|
|
2428
|
+
* The `input` is captured as the root span's input (as a single positional
|
|
2429
|
+
* argument, so `replay(key, fn)` re-feeds it), and the run's `finalOutput`
|
|
2430
|
+
* is recorded as the root output. For streaming runs (`{ stream: true }`),
|
|
2431
|
+
* the result is handed back immediately and the final output is recorded
|
|
2432
|
+
* once the stream completes — first-byte latency is untouched.
|
|
2433
|
+
*
|
|
2434
|
+
* The process-wide tracing processor (`getOpenAiTracingProcessor`) must still
|
|
2435
|
+
* be registered: it captures the LLM/tool/handoff spans that nest beneath
|
|
2436
|
+
* this root.
|
|
2437
|
+
*/
|
|
2318
2438
|
async wrapRun(agent, input, options) {
|
|
2319
2439
|
const { run } = await importOptionalPeer([
|
|
2320
2440
|
"@openai",
|
|
@@ -2353,6 +2473,7 @@ var BitfabOpenAIAgentHandler = class {
|
|
|
2353
2473
|
};
|
|
2354
2474
|
|
|
2355
2475
|
// src/client.ts
|
|
2476
|
+
init_randomUuid();
|
|
2356
2477
|
init_replayContext();
|
|
2357
2478
|
|
|
2358
2479
|
// src/replayEnvironment.ts
|
|
@@ -2772,6 +2893,7 @@ var BitfabVercelAiHandler = class {
|
|
|
2772
2893
|
};
|
|
2773
2894
|
|
|
2774
2895
|
// src/client.ts
|
|
2896
|
+
init_warnOnce();
|
|
2775
2897
|
var activeTraceStates = /* @__PURE__ */ new Map();
|
|
2776
2898
|
var pendingSpanPromises = /* @__PURE__ */ new Map();
|
|
2777
2899
|
var asyncLocalStorage = null;
|
|
@@ -3130,7 +3252,20 @@ var Bitfab = class {
|
|
|
3130
3252
|
functionVersion.providers,
|
|
3131
3253
|
this.envVars
|
|
3132
3254
|
);
|
|
3133
|
-
|
|
3255
|
+
let resultStr;
|
|
3256
|
+
if (typeof executionResult.result === "string") {
|
|
3257
|
+
resultStr = executionResult.result;
|
|
3258
|
+
} else {
|
|
3259
|
+
try {
|
|
3260
|
+
resultStr = JSON.stringify(executionResult.result) ?? String(executionResult.result);
|
|
3261
|
+
} catch {
|
|
3262
|
+
warnOnce(
|
|
3263
|
+
"call-result-serialize",
|
|
3264
|
+
"a local execution result could not be JSON-serialized; storing its String() form instead. The call still returns its real value."
|
|
3265
|
+
);
|
|
3266
|
+
resultStr = String(executionResult.result);
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3134
3269
|
this.httpClient.sendInternalTrace(functionVersion.id, {
|
|
3135
3270
|
result: resultStr,
|
|
3136
3271
|
source: "typescript-sdk",
|
|
@@ -3379,11 +3514,26 @@ var Bitfab = class {
|
|
|
3379
3514
|
return await bamlClient[methodName](...args);
|
|
3380
3515
|
}
|
|
3381
3516
|
const collector = new CollectorClass("bitfab-baml-tracing");
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
trackedClient
|
|
3386
|
-
|
|
3517
|
+
let trackedClient;
|
|
3518
|
+
let trackedMethod;
|
|
3519
|
+
try {
|
|
3520
|
+
trackedClient = bamlClient.withOptions({ collector });
|
|
3521
|
+
const method2 = trackedClient[methodName];
|
|
3522
|
+
if (typeof method2 !== "function") {
|
|
3523
|
+
throw new BitfabError(
|
|
3524
|
+
"bamlClient.withOptions did not return the wrapped method"
|
|
3525
|
+
);
|
|
3526
|
+
}
|
|
3527
|
+
trackedMethod = method2;
|
|
3528
|
+
} catch {
|
|
3529
|
+
warnOnce(
|
|
3530
|
+
`wrapBAML-setup:${methodName}`,
|
|
3531
|
+
`BAML tracing setup failed for "${methodName}" (incompatible bamlClient or BAML version); calling it untraced. The call still runs; no span is recorded.`
|
|
3532
|
+
);
|
|
3533
|
+
wrappedFn.collector = null;
|
|
3534
|
+
return await bamlClient[methodName](...args);
|
|
3535
|
+
}
|
|
3536
|
+
const result = await trackedMethod.bind(trackedClient)(...args);
|
|
3387
3537
|
wrappedFn.collector = collector;
|
|
3388
3538
|
try {
|
|
3389
3539
|
const prompt = extractPromptFromCollector(collector);
|
|
@@ -3459,200 +3609,229 @@ var Bitfab = class {
|
|
|
3459
3609
|
() => wrappedFn.apply(this, args)
|
|
3460
3610
|
);
|
|
3461
3611
|
}
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
const
|
|
3476
|
-
|
|
3612
|
+
let newStack;
|
|
3613
|
+
let executeWithContext;
|
|
3614
|
+
let registeredTraceId;
|
|
3615
|
+
try {
|
|
3616
|
+
const currentStack = getSpanStack();
|
|
3617
|
+
const parentContext = currentStack[currentStack.length - 1];
|
|
3618
|
+
const replayCtxForTraceId = parentContext ? null : getReplayContext();
|
|
3619
|
+
const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? randomUuid();
|
|
3620
|
+
const spanId = randomUuid();
|
|
3621
|
+
const parentSpanId = parentContext?.spanId ?? null;
|
|
3622
|
+
const isRootSpan = parentSpanId === null;
|
|
3623
|
+
const newContext = { traceId, spanId, contexts: [] };
|
|
3624
|
+
newStack = [...currentStack, newContext];
|
|
3625
|
+
const inputs = args;
|
|
3626
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3627
|
+
if (isRootSpan && !activeTraceStates.has(traceId)) {
|
|
3628
|
+
const replayCtxAtRoot = getReplayContext();
|
|
3629
|
+
const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
|
|
3630
|
+
activeTraceStates.set(traceId, {
|
|
3631
|
+
traceId,
|
|
3632
|
+
startedAt,
|
|
3633
|
+
contexts: [],
|
|
3634
|
+
...replayCtxAtRoot?.testRunId && {
|
|
3635
|
+
testRunId: replayCtxAtRoot.testRunId
|
|
3636
|
+
},
|
|
3637
|
+
...replayCtxAtRoot?.inputSourceTraceId && {
|
|
3638
|
+
inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
|
|
3639
|
+
},
|
|
3640
|
+
dbSnapshotRef
|
|
3641
|
+
});
|
|
3642
|
+
pendingSpanPromises.set(traceId, []);
|
|
3643
|
+
registeredTraceId = traceId;
|
|
3644
|
+
}
|
|
3645
|
+
const functionName = fn.name !== "" ? fn.name : void 0;
|
|
3646
|
+
const baseSpanParams = {
|
|
3647
|
+
traceFunctionKey,
|
|
3648
|
+
functionName,
|
|
3649
|
+
spanName: options.name ?? functionName ?? traceFunctionKey,
|
|
3477
3650
|
traceId,
|
|
3651
|
+
spanId,
|
|
3652
|
+
parentSpanId,
|
|
3653
|
+
inputs,
|
|
3478
3654
|
startedAt,
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
startedAt,
|
|
3500
|
-
spanType: options.type ?? "custom"
|
|
3501
|
-
};
|
|
3502
|
-
const sendSpan = async (params, spanOpts) => {
|
|
3503
|
-
const replayCtx = getReplayContext();
|
|
3504
|
-
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3505
|
-
let resolvePersistence;
|
|
3506
|
-
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3507
|
-
persistenceCollector.push(
|
|
3508
|
-
new Promise((resolve) => {
|
|
3509
|
-
resolvePersistence = resolve;
|
|
3510
|
-
})
|
|
3511
|
-
);
|
|
3512
|
-
}
|
|
3513
|
-
try {
|
|
3514
|
-
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3515
|
-
const spanPromise = self.sendWrapperSpan({
|
|
3516
|
-
...baseSpanParams,
|
|
3517
|
-
...params,
|
|
3518
|
-
contexts: newContext.contexts,
|
|
3519
|
-
prompt: newContext.prompt,
|
|
3520
|
-
endedAt,
|
|
3521
|
-
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3522
|
-
...replayCtx?.inputSourceSpanId && {
|
|
3523
|
-
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3524
|
-
}
|
|
3525
|
-
});
|
|
3526
|
-
if (isRootSpan) {
|
|
3527
|
-
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3528
|
-
pending.push(spanPromise);
|
|
3529
|
-
if (persistenceCollector) {
|
|
3530
|
-
await Promise.allSettled(pending);
|
|
3531
|
-
} else {
|
|
3532
|
-
await Promise.race([
|
|
3533
|
-
Promise.allSettled(pending),
|
|
3534
|
-
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
3535
|
-
]);
|
|
3536
|
-
}
|
|
3537
|
-
pendingSpanPromises.delete(traceId);
|
|
3538
|
-
const traceState = activeTraceStates.get(traceId);
|
|
3539
|
-
const completionPromise = self.sendTraceCompletion({
|
|
3540
|
-
traceFunctionKey,
|
|
3541
|
-
traceId,
|
|
3542
|
-
startedAt: traceState?.startedAt ?? startedAt,
|
|
3655
|
+
spanType: options.type ?? "custom"
|
|
3656
|
+
};
|
|
3657
|
+
const sendSpan = async (params, spanOpts) => {
|
|
3658
|
+
const replayCtx = getReplayContext();
|
|
3659
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3660
|
+
let resolvePersistence;
|
|
3661
|
+
if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
|
|
3662
|
+
persistenceCollector.push(
|
|
3663
|
+
new Promise((resolve) => {
|
|
3664
|
+
resolvePersistence = resolve;
|
|
3665
|
+
})
|
|
3666
|
+
);
|
|
3667
|
+
}
|
|
3668
|
+
try {
|
|
3669
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3670
|
+
const spanPromise = self.sendWrapperSpan({
|
|
3671
|
+
...baseSpanParams,
|
|
3672
|
+
...params,
|
|
3673
|
+
contexts: newContext.contexts,
|
|
3674
|
+
prompt: newContext.prompt,
|
|
3543
3675
|
endedAt,
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
testRunId: traceState?.testRunId,
|
|
3548
|
-
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3549
|
-
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3550
|
-
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3551
|
-
// whether customer code obtained the branch URL during this
|
|
3552
|
-
// item. Omitted entirely when no lease was attached, so the
|
|
3553
|
-
// server can distinguish "no branch" from "branch ignored".
|
|
3554
|
-
...replayCtx?.dbBranchLease && {
|
|
3555
|
-
dbSnapshotUsage: {
|
|
3556
|
-
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3557
|
-
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3558
|
-
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3559
|
-
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3560
|
-
}
|
|
3676
|
+
...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
|
|
3677
|
+
...replayCtx?.inputSourceSpanId && {
|
|
3678
|
+
inputSourceSpanId: replayCtx.inputSourceSpanId
|
|
3561
3679
|
}
|
|
3562
3680
|
});
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
await completionPromise;
|
|
3566
|
-
}
|
|
3567
|
-
} else {
|
|
3568
|
-
const pending = pendingSpanPromises.get(traceId);
|
|
3569
|
-
if (pending) {
|
|
3681
|
+
if (isRootSpan) {
|
|
3682
|
+
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
3570
3683
|
pending.push(spanPromise);
|
|
3684
|
+
if (persistenceCollector) {
|
|
3685
|
+
await Promise.allSettled(pending);
|
|
3686
|
+
} else {
|
|
3687
|
+
let raceTimer;
|
|
3688
|
+
try {
|
|
3689
|
+
await Promise.race([
|
|
3690
|
+
Promise.allSettled(pending),
|
|
3691
|
+
new Promise((resolve) => {
|
|
3692
|
+
raceTimer = setTimeout(resolve, 5e3);
|
|
3693
|
+
unrefTimer(raceTimer);
|
|
3694
|
+
})
|
|
3695
|
+
]);
|
|
3696
|
+
} finally {
|
|
3697
|
+
if (raceTimer) {
|
|
3698
|
+
clearTimeout(raceTimer);
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
pendingSpanPromises.delete(traceId);
|
|
3703
|
+
const traceState = activeTraceStates.get(traceId);
|
|
3704
|
+
const completionPromise = self.sendTraceCompletion({
|
|
3705
|
+
traceFunctionKey,
|
|
3706
|
+
traceId,
|
|
3707
|
+
startedAt: traceState?.startedAt ?? startedAt,
|
|
3708
|
+
endedAt,
|
|
3709
|
+
sessionId: traceState?.sessionId,
|
|
3710
|
+
metadata: traceState?.metadata,
|
|
3711
|
+
contexts: traceState?.contexts ?? [],
|
|
3712
|
+
testRunId: traceState?.testRunId,
|
|
3713
|
+
inputSourceTraceId: traceState?.inputSourceTraceId,
|
|
3714
|
+
dbSnapshotRef: traceState?.dbSnapshotRef,
|
|
3715
|
+
// Built AFTER the wrapped fn finished, so `accessed` reflects
|
|
3716
|
+
// whether customer code obtained the branch URL during this
|
|
3717
|
+
// item. Omitted entirely when no lease was attached, so the
|
|
3718
|
+
// server can distinguish "no branch" from "branch ignored".
|
|
3719
|
+
...replayCtx?.dbBranchLease && {
|
|
3720
|
+
dbSnapshotUsage: {
|
|
3721
|
+
neonBranchId: replayCtx.dbBranchLease.neonBranchId,
|
|
3722
|
+
snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
|
|
3723
|
+
sourceTraceId: replayCtx.sourceBitfabTraceId,
|
|
3724
|
+
accessed: replayCtx.dbSnapshotAccessed === true
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
});
|
|
3728
|
+
activeTraceStates.delete(traceId);
|
|
3729
|
+
if (persistenceCollector) {
|
|
3730
|
+
await completionPromise;
|
|
3731
|
+
}
|
|
3571
3732
|
} else {
|
|
3572
|
-
pendingSpanPromises.
|
|
3733
|
+
const pending = pendingSpanPromises.get(traceId);
|
|
3734
|
+
if (pending) {
|
|
3735
|
+
pending.push(spanPromise);
|
|
3736
|
+
} else {
|
|
3737
|
+
pendingSpanPromises.set(traceId, [spanPromise]);
|
|
3738
|
+
}
|
|
3573
3739
|
}
|
|
3740
|
+
} catch {
|
|
3741
|
+
} finally {
|
|
3742
|
+
resolvePersistence?.();
|
|
3574
3743
|
}
|
|
3575
|
-
}
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
if (fnReturnsPromise) {
|
|
3600
|
-
return Promise.resolve(output);
|
|
3744
|
+
};
|
|
3745
|
+
const replayCtxForMock = getReplayContext();
|
|
3746
|
+
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
3747
|
+
const counters = replayCtxForMock.callCounters;
|
|
3748
|
+
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
3749
|
+
const callIndex = counters.get(counterKey) ?? 0;
|
|
3750
|
+
counters.set(counterKey, callIndex + 1);
|
|
3751
|
+
const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
|
|
3752
|
+
if (shouldMock) {
|
|
3753
|
+
const mockKey = `${counterKey}:${callIndex}`;
|
|
3754
|
+
const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
|
|
3755
|
+
if (mockSpan) {
|
|
3756
|
+
let output = mockSpan.output;
|
|
3757
|
+
if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
|
|
3758
|
+
output = deserializeValue({
|
|
3759
|
+
json: mockSpan.output,
|
|
3760
|
+
meta: mockSpan.outputMeta
|
|
3761
|
+
});
|
|
3762
|
+
}
|
|
3763
|
+
void sendSpan({ result: output });
|
|
3764
|
+
if (fnReturnsPromise) {
|
|
3765
|
+
return Promise.resolve(output);
|
|
3766
|
+
}
|
|
3767
|
+
return output;
|
|
3601
3768
|
}
|
|
3602
|
-
return output;
|
|
3603
3769
|
}
|
|
3604
3770
|
}
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3771
|
+
const recordSpan = (result) => {
|
|
3772
|
+
if (options.finalize) {
|
|
3773
|
+
const replayCtx = getReplayContext();
|
|
3774
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
3775
|
+
let resolvePersistence;
|
|
3776
|
+
if (persistenceCollector) {
|
|
3777
|
+
persistenceCollector.push(
|
|
3778
|
+
new Promise((resolve) => {
|
|
3779
|
+
resolvePersistence = resolve;
|
|
3780
|
+
})
|
|
3781
|
+
);
|
|
3782
|
+
}
|
|
3783
|
+
void Promise.resolve().then(() => options.finalize(result)).then(
|
|
3784
|
+
(output) => sendSpan(
|
|
3785
|
+
{ result: output },
|
|
3786
|
+
{ skipPersistenceRegistration: true }
|
|
3787
|
+
)
|
|
3788
|
+
).catch(
|
|
3789
|
+
(error) => sendSpan(
|
|
3790
|
+
{
|
|
3791
|
+
result: void 0,
|
|
3792
|
+
error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
|
|
3793
|
+
},
|
|
3794
|
+
{ skipPersistenceRegistration: true }
|
|
3795
|
+
)
|
|
3796
|
+
).finally(() => resolvePersistence?.());
|
|
3797
|
+
} else {
|
|
3798
|
+
void sendSpan({ result });
|
|
3617
3799
|
}
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
)
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3800
|
+
};
|
|
3801
|
+
executeWithContext = () => {
|
|
3802
|
+
const result = fn(...args);
|
|
3803
|
+
if (result instanceof Promise) {
|
|
3804
|
+
return result.then((resolvedResult) => {
|
|
3805
|
+
recordSpan(resolvedResult);
|
|
3806
|
+
return resolvedResult;
|
|
3807
|
+
}).catch((error) => {
|
|
3808
|
+
void sendSpan({
|
|
3626
3809
|
result: void 0,
|
|
3627
|
-
error: error instanceof Error ?
|
|
3628
|
-
}
|
|
3629
|
-
|
|
3630
|
-
)
|
|
3631
|
-
).finally(() => resolvePersistence?.());
|
|
3632
|
-
} else {
|
|
3633
|
-
void sendSpan({ result });
|
|
3634
|
-
}
|
|
3635
|
-
};
|
|
3636
|
-
const executeWithContext = () => {
|
|
3637
|
-
const result = fn(...args);
|
|
3638
|
-
if (result instanceof Promise) {
|
|
3639
|
-
return result.then((resolvedResult) => {
|
|
3640
|
-
recordSpan(resolvedResult);
|
|
3641
|
-
return resolvedResult;
|
|
3642
|
-
}).catch((error) => {
|
|
3643
|
-
void sendSpan({
|
|
3644
|
-
result: void 0,
|
|
3645
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3810
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3811
|
+
});
|
|
3812
|
+
throw error;
|
|
3646
3813
|
});
|
|
3647
|
-
|
|
3648
|
-
|
|
3814
|
+
}
|
|
3815
|
+
if (isAsyncGenerator(result)) {
|
|
3816
|
+
return wrapAsyncGenerator(result, newStack, sendSpan);
|
|
3817
|
+
}
|
|
3818
|
+
recordSpan(result);
|
|
3819
|
+
return result;
|
|
3820
|
+
};
|
|
3821
|
+
} catch (setupError) {
|
|
3822
|
+
if (registeredTraceId) {
|
|
3823
|
+
activeTraceStates.delete(registeredTraceId);
|
|
3824
|
+
pendingSpanPromises.delete(registeredTraceId);
|
|
3649
3825
|
}
|
|
3650
|
-
if (
|
|
3651
|
-
|
|
3826
|
+
if (getReplayContext()) {
|
|
3827
|
+
throw setupError;
|
|
3652
3828
|
}
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3829
|
+
warnOnce(
|
|
3830
|
+
`withSpan-setup:${traceFunctionKey}`,
|
|
3831
|
+
`tracing setup failed for "${traceFunctionKey}"; running it untraced. The function still runs and returns normally; no span is recorded.`
|
|
3832
|
+
);
|
|
3833
|
+
return fn(...args);
|
|
3834
|
+
}
|
|
3656
3835
|
return runWithSpanStack(newStack, executeWithContext);
|
|
3657
3836
|
};
|
|
3658
3837
|
Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
|