@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.
@@ -5,16 +5,26 @@ import {
5
5
  deserializeValue,
6
6
  getReplayContext,
7
7
  isAsyncStorageInitDone,
8
+ randomUuid,
8
9
  serializeValue,
9
- toJsonSafe
10
- } from "./chunk-EQI6ZJC3.js";
10
+ toJsonSafe,
11
+ warnOnce
12
+ } from "./chunk-26MUT4IP.js";
11
13
 
12
14
  // src/version.generated.ts
13
- var __version__ = "0.23.3";
15
+ var __version__ = "0.24.1";
14
16
 
15
17
  // src/constants.ts
16
18
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
17
19
 
20
+ // src/unrefTimer.ts
21
+ function unrefTimer(timer) {
22
+ const handle = timer;
23
+ if (typeof handle.unref === "function") {
24
+ handle.unref();
25
+ }
26
+ }
27
+
18
28
  // src/http.ts
19
29
  function serializePayloadBody(payload) {
20
30
  try {
@@ -60,11 +70,20 @@ function serializePayloadBody(payload) {
60
70
  result = `<unserializable: ${className}>`;
61
71
  }
62
72
  } else {
63
- const out = {};
64
- for (const [k, v] of Object.entries(obj)) {
65
- out[k] = sanitize(v, seen);
73
+ try {
74
+ const out = {};
75
+ for (const [k, v] of Object.entries(obj)) {
76
+ out[k] = sanitize(v, seen);
77
+ }
78
+ result = out;
79
+ } catch {
80
+ warnOnce(
81
+ "payload:field-getter-threw",
82
+ "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."
83
+ );
84
+ dropped.push(className);
85
+ result = `<unserializable: ${className}>`;
66
86
  }
67
- result = out;
68
87
  }
69
88
  seen.delete(obj);
70
89
  return result;
@@ -109,10 +128,20 @@ async function flushTraces(timeoutMs = 5e3) {
109
128
  if (pendingTracePromises.size === 0) {
110
129
  return;
111
130
  }
112
- await Promise.race([
113
- Promise.allSettled(Array.from(pendingTracePromises)),
114
- new Promise((resolve) => setTimeout(resolve, timeoutMs))
115
- ]);
131
+ let timer;
132
+ try {
133
+ await Promise.race([
134
+ Promise.allSettled(Array.from(pendingTracePromises)),
135
+ new Promise((resolve) => {
136
+ timer = setTimeout(resolve, timeoutMs);
137
+ unrefTimer(timer);
138
+ })
139
+ ]);
140
+ } finally {
141
+ if (timer) {
142
+ clearTimeout(timer);
143
+ }
144
+ }
116
145
  }
117
146
  if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
118
147
  let isFlushing = false;
@@ -508,7 +537,7 @@ var BitfabClaudeAgentHandler = class {
508
537
  if (this.activeContext) {
509
538
  this.traceId = this.activeContext.traceId;
510
539
  } else {
511
- this.traceId = crypto.randomUUID();
540
+ this.traceId = randomUuid();
512
541
  }
513
542
  this.traceStartedAt = nowIso();
514
543
  return this.traceId;
@@ -533,7 +562,7 @@ var BitfabClaudeAgentHandler = class {
533
562
  if (this.activeContext !== null) {
534
563
  return;
535
564
  }
536
- const spanId = crypto.randomUUID();
565
+ const spanId = randomUuid();
537
566
  this.startSpan(spanId, this.traceFunctionKey, "agent", this.rootInput, null);
538
567
  this.rootSpanId = spanId;
539
568
  }
@@ -647,7 +676,7 @@ var BitfabClaudeAgentHandler = class {
647
676
  // ── hook callbacks ───────────────────────────────────────────
648
677
  async preToolUseHook(inputData, toolUseId, _context) {
649
678
  try {
650
- const sid = inputData.tool_use_id ?? toolUseId ?? crypto.randomUUID();
679
+ const sid = inputData.tool_use_id ?? toolUseId ?? randomUuid();
651
680
  const toolName = inputData.tool_name ?? "tool";
652
681
  const toolInput = inputData.tool_input ?? {};
653
682
  const agentId = inputData.agent_id;
@@ -677,10 +706,10 @@ var BitfabClaudeAgentHandler = class {
677
706
  }
678
707
  async subagentStartHook(inputData, _toolUseId, _context) {
679
708
  try {
680
- const agentId = inputData.agent_id ?? crypto.randomUUID();
709
+ const agentId = inputData.agent_id ?? randomUuid();
681
710
  const agentType = inputData.agent_type ?? "subagent";
682
711
  const parentId = this.getParentId();
683
- const spanId = crypto.randomUUID();
712
+ const spanId = randomUuid();
684
713
  this.activeSubagentSpans.set(agentId, spanId);
685
714
  this.startSpan(
686
715
  spanId,
@@ -821,7 +850,7 @@ var BitfabClaudeAgentHandler = class {
821
850
  this.flushLlmTurn();
822
851
  this.conversationHistory.push(...this.pendingMessages);
823
852
  this.pendingMessages = [];
824
- this.currentLlmSpanId = crypto.randomUUID();
853
+ this.currentLlmSpanId = randomUuid();
825
854
  this.currentLlmMessageId = messageId ?? null;
826
855
  this.currentLlmContent = [];
827
856
  this.currentLlmModel = inner.model ?? null;
@@ -1508,7 +1537,7 @@ var BitfabLangGraphCallbackHandler = class {
1508
1537
  } else {
1509
1538
  const activeContext = this.getActiveSpanContext?.() ?? null;
1510
1539
  invocation = {
1511
- traceId: activeContext ? activeContext.traceId : crypto.randomUUID(),
1540
+ traceId: activeContext ? activeContext.traceId : randomUuid(),
1512
1541
  activeContext,
1513
1542
  rootRunId: runId
1514
1543
  };
@@ -1803,6 +1832,20 @@ var BitfabOpenAIAgentHandler = class {
1803
1832
  this.withSpanFn = config.withSpan;
1804
1833
  this.getActiveSpanContext = config.getActiveSpanContext;
1805
1834
  }
1835
+ /**
1836
+ * Drop-in replacement for the OpenAI Agents SDK's `run()` that records a
1837
+ * replayable root `agent` span.
1838
+ *
1839
+ * The `input` is captured as the root span's input (as a single positional
1840
+ * argument, so `replay(key, fn)` re-feeds it), and the run's `finalOutput`
1841
+ * is recorded as the root output. For streaming runs (`{ stream: true }`),
1842
+ * the result is handed back immediately and the final output is recorded
1843
+ * once the stream completes — first-byte latency is untouched.
1844
+ *
1845
+ * The process-wide tracing processor (`getOpenAiTracingProcessor`) must still
1846
+ * be registered: it captures the LLM/tool/handoff spans that nest beneath
1847
+ * this root.
1848
+ */
1806
1849
  async wrapRun(agent, input, options) {
1807
1850
  const { run } = await importOptionalPeer([
1808
1851
  "@openai",
@@ -2611,7 +2654,20 @@ var Bitfab = class {
2611
2654
  functionVersion.providers,
2612
2655
  this.envVars
2613
2656
  );
2614
- const resultStr = typeof executionResult.result === "string" ? executionResult.result : JSON.stringify(executionResult.result);
2657
+ let resultStr;
2658
+ if (typeof executionResult.result === "string") {
2659
+ resultStr = executionResult.result;
2660
+ } else {
2661
+ try {
2662
+ resultStr = JSON.stringify(executionResult.result) ?? String(executionResult.result);
2663
+ } catch {
2664
+ warnOnce(
2665
+ "call-result-serialize",
2666
+ "a local execution result could not be JSON-serialized; storing its String() form instead. The call still returns its real value."
2667
+ );
2668
+ resultStr = String(executionResult.result);
2669
+ }
2670
+ }
2615
2671
  this.httpClient.sendInternalTrace(functionVersion.id, {
2616
2672
  result: resultStr,
2617
2673
  source: "typescript-sdk",
@@ -2860,11 +2916,26 @@ var Bitfab = class {
2860
2916
  return await bamlClient[methodName](...args);
2861
2917
  }
2862
2918
  const collector = new CollectorClass("bitfab-baml-tracing");
2863
- const trackedClient = bamlClient.withOptions({ collector });
2864
- const trackedMethod = trackedClient[methodName];
2865
- const result = await trackedMethod.bind(
2866
- trackedClient
2867
- )(...args);
2919
+ let trackedClient;
2920
+ let trackedMethod;
2921
+ try {
2922
+ trackedClient = bamlClient.withOptions({ collector });
2923
+ const method2 = trackedClient[methodName];
2924
+ if (typeof method2 !== "function") {
2925
+ throw new BitfabError(
2926
+ "bamlClient.withOptions did not return the wrapped method"
2927
+ );
2928
+ }
2929
+ trackedMethod = method2;
2930
+ } catch {
2931
+ warnOnce(
2932
+ `wrapBAML-setup:${methodName}`,
2933
+ `BAML tracing setup failed for "${methodName}" (incompatible bamlClient or BAML version); calling it untraced. The call still runs; no span is recorded.`
2934
+ );
2935
+ wrappedFn.collector = null;
2936
+ return await bamlClient[methodName](...args);
2937
+ }
2938
+ const result = await trackedMethod.bind(trackedClient)(...args);
2868
2939
  wrappedFn.collector = collector;
2869
2940
  try {
2870
2941
  const prompt = extractPromptFromCollector(collector);
@@ -2940,200 +3011,229 @@ var Bitfab = class {
2940
3011
  () => wrappedFn.apply(this, args)
2941
3012
  );
2942
3013
  }
2943
- const currentStack = getSpanStack();
2944
- const parentContext = currentStack[currentStack.length - 1];
2945
- const replayCtxForTraceId = parentContext ? null : getReplayContext();
2946
- const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? crypto.randomUUID();
2947
- const spanId = crypto.randomUUID();
2948
- const parentSpanId = parentContext?.spanId ?? null;
2949
- const isRootSpan = parentSpanId === null;
2950
- const newContext = { traceId, spanId, contexts: [] };
2951
- const newStack = [...currentStack, newContext];
2952
- const inputs = args;
2953
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
2954
- if (isRootSpan && !activeTraceStates.has(traceId)) {
2955
- const replayCtxAtRoot = getReplayContext();
2956
- const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
2957
- activeTraceStates.set(traceId, {
3014
+ let newStack;
3015
+ let executeWithContext;
3016
+ let registeredTraceId;
3017
+ try {
3018
+ const currentStack = getSpanStack();
3019
+ const parentContext = currentStack[currentStack.length - 1];
3020
+ const replayCtxForTraceId = parentContext ? null : getReplayContext();
3021
+ const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? randomUuid();
3022
+ const spanId = randomUuid();
3023
+ const parentSpanId = parentContext?.spanId ?? null;
3024
+ const isRootSpan = parentSpanId === null;
3025
+ const newContext = { traceId, spanId, contexts: [] };
3026
+ newStack = [...currentStack, newContext];
3027
+ const inputs = args;
3028
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
3029
+ if (isRootSpan && !activeTraceStates.has(traceId)) {
3030
+ const replayCtxAtRoot = getReplayContext();
3031
+ const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
3032
+ activeTraceStates.set(traceId, {
3033
+ traceId,
3034
+ startedAt,
3035
+ contexts: [],
3036
+ ...replayCtxAtRoot?.testRunId && {
3037
+ testRunId: replayCtxAtRoot.testRunId
3038
+ },
3039
+ ...replayCtxAtRoot?.inputSourceTraceId && {
3040
+ inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
3041
+ },
3042
+ dbSnapshotRef
3043
+ });
3044
+ pendingSpanPromises.set(traceId, []);
3045
+ registeredTraceId = traceId;
3046
+ }
3047
+ const functionName = fn.name !== "" ? fn.name : void 0;
3048
+ const baseSpanParams = {
3049
+ traceFunctionKey,
3050
+ functionName,
3051
+ spanName: options.name ?? functionName ?? traceFunctionKey,
2958
3052
  traceId,
3053
+ spanId,
3054
+ parentSpanId,
3055
+ inputs,
2959
3056
  startedAt,
2960
- contexts: [],
2961
- ...replayCtxAtRoot?.testRunId && {
2962
- testRunId: replayCtxAtRoot.testRunId
2963
- },
2964
- ...replayCtxAtRoot?.inputSourceTraceId && {
2965
- inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
2966
- },
2967
- dbSnapshotRef
2968
- });
2969
- pendingSpanPromises.set(traceId, []);
2970
- }
2971
- const functionName = fn.name !== "" ? fn.name : void 0;
2972
- const baseSpanParams = {
2973
- traceFunctionKey,
2974
- functionName,
2975
- spanName: options.name ?? functionName ?? traceFunctionKey,
2976
- traceId,
2977
- spanId,
2978
- parentSpanId,
2979
- inputs,
2980
- startedAt,
2981
- spanType: options.type ?? "custom"
2982
- };
2983
- const sendSpan = async (params, spanOpts) => {
2984
- const replayCtx = getReplayContext();
2985
- const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
2986
- let resolvePersistence;
2987
- if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
2988
- persistenceCollector.push(
2989
- new Promise((resolve) => {
2990
- resolvePersistence = resolve;
2991
- })
2992
- );
2993
- }
2994
- try {
2995
- const endedAt = (/* @__PURE__ */ new Date()).toISOString();
2996
- const spanPromise = self.sendWrapperSpan({
2997
- ...baseSpanParams,
2998
- ...params,
2999
- contexts: newContext.contexts,
3000
- prompt: newContext.prompt,
3001
- endedAt,
3002
- ...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
3003
- ...replayCtx?.inputSourceSpanId && {
3004
- inputSourceSpanId: replayCtx.inputSourceSpanId
3005
- }
3006
- });
3007
- if (isRootSpan) {
3008
- const pending = pendingSpanPromises.get(traceId) ?? [];
3009
- pending.push(spanPromise);
3010
- if (persistenceCollector) {
3011
- await Promise.allSettled(pending);
3012
- } else {
3013
- await Promise.race([
3014
- Promise.allSettled(pending),
3015
- new Promise((resolve) => setTimeout(resolve, 5e3))
3016
- ]);
3017
- }
3018
- pendingSpanPromises.delete(traceId);
3019
- const traceState = activeTraceStates.get(traceId);
3020
- const completionPromise = self.sendTraceCompletion({
3021
- traceFunctionKey,
3022
- traceId,
3023
- startedAt: traceState?.startedAt ?? startedAt,
3057
+ spanType: options.type ?? "custom"
3058
+ };
3059
+ const sendSpan = async (params, spanOpts) => {
3060
+ const replayCtx = getReplayContext();
3061
+ const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
3062
+ let resolvePersistence;
3063
+ if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
3064
+ persistenceCollector.push(
3065
+ new Promise((resolve) => {
3066
+ resolvePersistence = resolve;
3067
+ })
3068
+ );
3069
+ }
3070
+ try {
3071
+ const endedAt = (/* @__PURE__ */ new Date()).toISOString();
3072
+ const spanPromise = self.sendWrapperSpan({
3073
+ ...baseSpanParams,
3074
+ ...params,
3075
+ contexts: newContext.contexts,
3076
+ prompt: newContext.prompt,
3024
3077
  endedAt,
3025
- sessionId: traceState?.sessionId,
3026
- metadata: traceState?.metadata,
3027
- contexts: traceState?.contexts ?? [],
3028
- testRunId: traceState?.testRunId,
3029
- inputSourceTraceId: traceState?.inputSourceTraceId,
3030
- dbSnapshotRef: traceState?.dbSnapshotRef,
3031
- // Built AFTER the wrapped fn finished, so `accessed` reflects
3032
- // whether customer code obtained the branch URL during this
3033
- // item. Omitted entirely when no lease was attached, so the
3034
- // server can distinguish "no branch" from "branch ignored".
3035
- ...replayCtx?.dbBranchLease && {
3036
- dbSnapshotUsage: {
3037
- neonBranchId: replayCtx.dbBranchLease.neonBranchId,
3038
- snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
3039
- sourceTraceId: replayCtx.sourceBitfabTraceId,
3040
- accessed: replayCtx.dbSnapshotAccessed === true
3041
- }
3078
+ ...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
3079
+ ...replayCtx?.inputSourceSpanId && {
3080
+ inputSourceSpanId: replayCtx.inputSourceSpanId
3042
3081
  }
3043
3082
  });
3044
- activeTraceStates.delete(traceId);
3045
- if (persistenceCollector) {
3046
- await completionPromise;
3047
- }
3048
- } else {
3049
- const pending = pendingSpanPromises.get(traceId);
3050
- if (pending) {
3083
+ if (isRootSpan) {
3084
+ const pending = pendingSpanPromises.get(traceId) ?? [];
3051
3085
  pending.push(spanPromise);
3086
+ if (persistenceCollector) {
3087
+ await Promise.allSettled(pending);
3088
+ } else {
3089
+ let raceTimer;
3090
+ try {
3091
+ await Promise.race([
3092
+ Promise.allSettled(pending),
3093
+ new Promise((resolve) => {
3094
+ raceTimer = setTimeout(resolve, 5e3);
3095
+ unrefTimer(raceTimer);
3096
+ })
3097
+ ]);
3098
+ } finally {
3099
+ if (raceTimer) {
3100
+ clearTimeout(raceTimer);
3101
+ }
3102
+ }
3103
+ }
3104
+ pendingSpanPromises.delete(traceId);
3105
+ const traceState = activeTraceStates.get(traceId);
3106
+ const completionPromise = self.sendTraceCompletion({
3107
+ traceFunctionKey,
3108
+ traceId,
3109
+ startedAt: traceState?.startedAt ?? startedAt,
3110
+ endedAt,
3111
+ sessionId: traceState?.sessionId,
3112
+ metadata: traceState?.metadata,
3113
+ contexts: traceState?.contexts ?? [],
3114
+ testRunId: traceState?.testRunId,
3115
+ inputSourceTraceId: traceState?.inputSourceTraceId,
3116
+ dbSnapshotRef: traceState?.dbSnapshotRef,
3117
+ // Built AFTER the wrapped fn finished, so `accessed` reflects
3118
+ // whether customer code obtained the branch URL during this
3119
+ // item. Omitted entirely when no lease was attached, so the
3120
+ // server can distinguish "no branch" from "branch ignored".
3121
+ ...replayCtx?.dbBranchLease && {
3122
+ dbSnapshotUsage: {
3123
+ neonBranchId: replayCtx.dbBranchLease.neonBranchId,
3124
+ snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
3125
+ sourceTraceId: replayCtx.sourceBitfabTraceId,
3126
+ accessed: replayCtx.dbSnapshotAccessed === true
3127
+ }
3128
+ }
3129
+ });
3130
+ activeTraceStates.delete(traceId);
3131
+ if (persistenceCollector) {
3132
+ await completionPromise;
3133
+ }
3052
3134
  } else {
3053
- pendingSpanPromises.set(traceId, [spanPromise]);
3135
+ const pending = pendingSpanPromises.get(traceId);
3136
+ if (pending) {
3137
+ pending.push(spanPromise);
3138
+ } else {
3139
+ pendingSpanPromises.set(traceId, [spanPromise]);
3140
+ }
3054
3141
  }
3142
+ } catch {
3143
+ } finally {
3144
+ resolvePersistence?.();
3055
3145
  }
3056
- } catch {
3057
- } finally {
3058
- resolvePersistence?.();
3059
- }
3060
- };
3061
- const replayCtxForMock = getReplayContext();
3062
- if (replayCtxForMock?.mockTree && !isRootSpan) {
3063
- const counters = replayCtxForMock.callCounters;
3064
- const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
3065
- const callIndex = counters.get(counterKey) ?? 0;
3066
- counters.set(counterKey, callIndex + 1);
3067
- const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
3068
- if (shouldMock) {
3069
- const mockKey = `${counterKey}:${callIndex}`;
3070
- const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
3071
- if (mockSpan) {
3072
- let output = mockSpan.output;
3073
- if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
3074
- output = deserializeValue({
3075
- json: mockSpan.output,
3076
- meta: mockSpan.outputMeta
3077
- });
3078
- }
3079
- void sendSpan({ result: output });
3080
- if (fnReturnsPromise) {
3081
- return Promise.resolve(output);
3146
+ };
3147
+ const replayCtxForMock = getReplayContext();
3148
+ if (replayCtxForMock?.mockTree && !isRootSpan) {
3149
+ const counters = replayCtxForMock.callCounters;
3150
+ const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
3151
+ const callIndex = counters.get(counterKey) ?? 0;
3152
+ counters.set(counterKey, callIndex + 1);
3153
+ const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
3154
+ if (shouldMock) {
3155
+ const mockKey = `${counterKey}:${callIndex}`;
3156
+ const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
3157
+ if (mockSpan) {
3158
+ let output = mockSpan.output;
3159
+ if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
3160
+ output = deserializeValue({
3161
+ json: mockSpan.output,
3162
+ meta: mockSpan.outputMeta
3163
+ });
3164
+ }
3165
+ void sendSpan({ result: output });
3166
+ if (fnReturnsPromise) {
3167
+ return Promise.resolve(output);
3168
+ }
3169
+ return output;
3082
3170
  }
3083
- return output;
3084
3171
  }
3085
3172
  }
3086
- }
3087
- const recordSpan = (result) => {
3088
- if (options.finalize) {
3089
- const replayCtx = getReplayContext();
3090
- const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
3091
- let resolvePersistence;
3092
- if (persistenceCollector) {
3093
- persistenceCollector.push(
3094
- new Promise((resolve) => {
3095
- resolvePersistence = resolve;
3096
- })
3097
- );
3173
+ const recordSpan = (result) => {
3174
+ if (options.finalize) {
3175
+ const replayCtx = getReplayContext();
3176
+ const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
3177
+ let resolvePersistence;
3178
+ if (persistenceCollector) {
3179
+ persistenceCollector.push(
3180
+ new Promise((resolve) => {
3181
+ resolvePersistence = resolve;
3182
+ })
3183
+ );
3184
+ }
3185
+ void Promise.resolve().then(() => options.finalize(result)).then(
3186
+ (output) => sendSpan(
3187
+ { result: output },
3188
+ { skipPersistenceRegistration: true }
3189
+ )
3190
+ ).catch(
3191
+ (error) => sendSpan(
3192
+ {
3193
+ result: void 0,
3194
+ error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
3195
+ },
3196
+ { skipPersistenceRegistration: true }
3197
+ )
3198
+ ).finally(() => resolvePersistence?.());
3199
+ } else {
3200
+ void sendSpan({ result });
3098
3201
  }
3099
- void Promise.resolve().then(() => options.finalize(result)).then(
3100
- (output) => sendSpan(
3101
- { result: output },
3102
- { skipPersistenceRegistration: true }
3103
- )
3104
- ).catch(
3105
- (error) => sendSpan(
3106
- {
3202
+ };
3203
+ executeWithContext = () => {
3204
+ const result = fn(...args);
3205
+ if (result instanceof Promise) {
3206
+ return result.then((resolvedResult) => {
3207
+ recordSpan(resolvedResult);
3208
+ return resolvedResult;
3209
+ }).catch((error) => {
3210
+ void sendSpan({
3107
3211
  result: void 0,
3108
- error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
3109
- },
3110
- { skipPersistenceRegistration: true }
3111
- )
3112
- ).finally(() => resolvePersistence?.());
3113
- } else {
3114
- void sendSpan({ result });
3115
- }
3116
- };
3117
- const executeWithContext = () => {
3118
- const result = fn(...args);
3119
- if (result instanceof Promise) {
3120
- return result.then((resolvedResult) => {
3121
- recordSpan(resolvedResult);
3122
- return resolvedResult;
3123
- }).catch((error) => {
3124
- void sendSpan({
3125
- result: void 0,
3126
- error: error instanceof Error ? error.message : String(error)
3212
+ error: error instanceof Error ? error.message : String(error)
3213
+ });
3214
+ throw error;
3127
3215
  });
3128
- throw error;
3129
- });
3216
+ }
3217
+ if (isAsyncGenerator(result)) {
3218
+ return wrapAsyncGenerator(result, newStack, sendSpan);
3219
+ }
3220
+ recordSpan(result);
3221
+ return result;
3222
+ };
3223
+ } catch (setupError) {
3224
+ if (registeredTraceId) {
3225
+ activeTraceStates.delete(registeredTraceId);
3226
+ pendingSpanPromises.delete(registeredTraceId);
3130
3227
  }
3131
- if (isAsyncGenerator(result)) {
3132
- return wrapAsyncGenerator(result, newStack, sendSpan);
3228
+ if (getReplayContext()) {
3229
+ throw setupError;
3133
3230
  }
3134
- recordSpan(result);
3135
- return result;
3136
- };
3231
+ warnOnce(
3232
+ `withSpan-setup:${traceFunctionKey}`,
3233
+ `tracing setup failed for "${traceFunctionKey}"; running it untraced. The function still runs and returns normally; no span is recorded.`
3234
+ );
3235
+ return fn(...args);
3236
+ }
3137
3237
  return runWithSpanStack(newStack, executeWithContext);
3138
3238
  };
3139
3239
  Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
@@ -3350,7 +3450,7 @@ var Bitfab = class {
3350
3450
  `Function is wrapped with trace function key '${wrappedKey}' but replay was called with '${traceFunctionKey}'. Pass matching keys, or pass the unwrapped function to replay it under the explicit key.`
3351
3451
  );
3352
3452
  }
3353
- const { replay: doReplay } = await import("./replay-V6RPJYXZ.js");
3453
+ const { replay: doReplay } = await import("./replay-NMQA7XY6.js");
3354
3454
  return doReplay(
3355
3455
  this.httpClient,
3356
3456
  this.serviceUrl,
@@ -3480,4 +3580,4 @@ export {
3480
3580
  BitfabFunction,
3481
3581
  finalizers
3482
3582
  };
3483
- //# sourceMappingURL=chunk-CG6LVLBK.js.map
3583
+ //# sourceMappingURL=chunk-J4D6PRM4.js.map