@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.
@@ -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.2";
15
+ var __version__ = "0.24.0";
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;
@@ -946,6 +975,16 @@ var BitfabClaudeAgentHandler = class {
946
975
  }
947
976
  };
948
977
 
978
+ // src/optionalPeer.ts
979
+ function importOptionalPeer(specifierParts) {
980
+ const specifier = specifierParts.join("/");
981
+ return import(
982
+ /* webpackIgnore: true */
983
+ /* @vite-ignore */
984
+ specifier
985
+ );
986
+ }
987
+
949
988
  // src/baml.ts
950
989
  var cachedBaml = null;
951
990
  async function loadBaml() {
@@ -953,7 +992,7 @@ async function loadBaml() {
953
992
  return cachedBaml;
954
993
  }
955
994
  try {
956
- cachedBaml = await import("@boundaryml/baml");
995
+ cachedBaml = await importOptionalPeer(["@boundaryml", "baml"]);
957
996
  return cachedBaml;
958
997
  } catch {
959
998
  throw new Error(
@@ -1498,7 +1537,7 @@ var BitfabLangGraphCallbackHandler = class {
1498
1537
  } else {
1499
1538
  const activeContext = this.getActiveSpanContext?.() ?? null;
1500
1539
  invocation = {
1501
- traceId: activeContext ? activeContext.traceId : crypto.randomUUID(),
1540
+ traceId: activeContext ? activeContext.traceId : randomUuid(),
1502
1541
  activeContext,
1503
1542
  rootRunId: runId
1504
1543
  };
@@ -1793,8 +1832,25 @@ var BitfabOpenAIAgentHandler = class {
1793
1832
  this.withSpanFn = config.withSpan;
1794
1833
  this.getActiveSpanContext = config.getActiveSpanContext;
1795
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
+ */
1796
1849
  async wrapRun(agent, input, options) {
1797
- const { run } = await import("@openai/agents");
1850
+ const { run } = await importOptionalPeer([
1851
+ "@openai",
1852
+ "agents"
1853
+ ]);
1798
1854
  if (this.getActiveSpanContext?.() != null) {
1799
1855
  return run(
1800
1856
  agent,
@@ -2338,7 +2394,10 @@ async function loadCollectorClass() {
2338
2394
  return cachedCollectorClass;
2339
2395
  }
2340
2396
  try {
2341
- const baml = await import("@boundaryml/baml");
2397
+ const baml = await importOptionalPeer([
2398
+ "@boundaryml",
2399
+ "baml"
2400
+ ]);
2342
2401
  cachedCollectorClass = baml.Collector;
2343
2402
  return cachedCollectorClass;
2344
2403
  } catch {
@@ -2595,7 +2654,20 @@ var Bitfab = class {
2595
2654
  functionVersion.providers,
2596
2655
  this.envVars
2597
2656
  );
2598
- 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
+ }
2599
2671
  this.httpClient.sendInternalTrace(functionVersion.id, {
2600
2672
  result: resultStr,
2601
2673
  source: "typescript-sdk",
@@ -2844,11 +2916,26 @@ var Bitfab = class {
2844
2916
  return await bamlClient[methodName](...args);
2845
2917
  }
2846
2918
  const collector = new CollectorClass("bitfab-baml-tracing");
2847
- const trackedClient = bamlClient.withOptions({ collector });
2848
- const trackedMethod = trackedClient[methodName];
2849
- const result = await trackedMethod.bind(
2850
- trackedClient
2851
- )(...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);
2852
2939
  wrappedFn.collector = collector;
2853
2940
  try {
2854
2941
  const prompt = extractPromptFromCollector(collector);
@@ -2924,200 +3011,229 @@ var Bitfab = class {
2924
3011
  () => wrappedFn.apply(this, args)
2925
3012
  );
2926
3013
  }
2927
- const currentStack = getSpanStack();
2928
- const parentContext = currentStack[currentStack.length - 1];
2929
- const replayCtxForTraceId = parentContext ? null : getReplayContext();
2930
- const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? crypto.randomUUID();
2931
- const spanId = crypto.randomUUID();
2932
- const parentSpanId = parentContext?.spanId ?? null;
2933
- const isRootSpan = parentSpanId === null;
2934
- const newContext = { traceId, spanId, contexts: [] };
2935
- const newStack = [...currentStack, newContext];
2936
- const inputs = args;
2937
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
2938
- if (isRootSpan && !activeTraceStates.has(traceId)) {
2939
- const replayCtxAtRoot = getReplayContext();
2940
- const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
2941
- 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,
2942
3052
  traceId,
3053
+ spanId,
3054
+ parentSpanId,
3055
+ inputs,
2943
3056
  startedAt,
2944
- contexts: [],
2945
- ...replayCtxAtRoot?.testRunId && {
2946
- testRunId: replayCtxAtRoot.testRunId
2947
- },
2948
- ...replayCtxAtRoot?.inputSourceTraceId && {
2949
- inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
2950
- },
2951
- dbSnapshotRef
2952
- });
2953
- pendingSpanPromises.set(traceId, []);
2954
- }
2955
- const functionName = fn.name !== "" ? fn.name : void 0;
2956
- const baseSpanParams = {
2957
- traceFunctionKey,
2958
- functionName,
2959
- spanName: options.name ?? functionName ?? traceFunctionKey,
2960
- traceId,
2961
- spanId,
2962
- parentSpanId,
2963
- inputs,
2964
- startedAt,
2965
- spanType: options.type ?? "custom"
2966
- };
2967
- const sendSpan = async (params, spanOpts) => {
2968
- const replayCtx = getReplayContext();
2969
- const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
2970
- let resolvePersistence;
2971
- if (persistenceCollector && !spanOpts?.skipPersistenceRegistration) {
2972
- persistenceCollector.push(
2973
- new Promise((resolve) => {
2974
- resolvePersistence = resolve;
2975
- })
2976
- );
2977
- }
2978
- try {
2979
- const endedAt = (/* @__PURE__ */ new Date()).toISOString();
2980
- const spanPromise = self.sendWrapperSpan({
2981
- ...baseSpanParams,
2982
- ...params,
2983
- contexts: newContext.contexts,
2984
- prompt: newContext.prompt,
2985
- endedAt,
2986
- ...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
2987
- ...replayCtx?.inputSourceSpanId && {
2988
- inputSourceSpanId: replayCtx.inputSourceSpanId
2989
- }
2990
- });
2991
- if (isRootSpan) {
2992
- const pending = pendingSpanPromises.get(traceId) ?? [];
2993
- pending.push(spanPromise);
2994
- if (persistenceCollector) {
2995
- await Promise.allSettled(pending);
2996
- } else {
2997
- await Promise.race([
2998
- Promise.allSettled(pending),
2999
- new Promise((resolve) => setTimeout(resolve, 5e3))
3000
- ]);
3001
- }
3002
- pendingSpanPromises.delete(traceId);
3003
- const traceState = activeTraceStates.get(traceId);
3004
- const completionPromise = self.sendTraceCompletion({
3005
- traceFunctionKey,
3006
- traceId,
3007
- 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,
3008
3077
  endedAt,
3009
- sessionId: traceState?.sessionId,
3010
- metadata: traceState?.metadata,
3011
- contexts: traceState?.contexts ?? [],
3012
- testRunId: traceState?.testRunId,
3013
- inputSourceTraceId: traceState?.inputSourceTraceId,
3014
- dbSnapshotRef: traceState?.dbSnapshotRef,
3015
- // Built AFTER the wrapped fn finished, so `accessed` reflects
3016
- // whether customer code obtained the branch URL during this
3017
- // item. Omitted entirely when no lease was attached, so the
3018
- // server can distinguish "no branch" from "branch ignored".
3019
- ...replayCtx?.dbBranchLease && {
3020
- dbSnapshotUsage: {
3021
- neonBranchId: replayCtx.dbBranchLease.neonBranchId,
3022
- snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
3023
- sourceTraceId: replayCtx.sourceBitfabTraceId,
3024
- accessed: replayCtx.dbSnapshotAccessed === true
3025
- }
3078
+ ...replayCtx?.testRunId && { testRunId: replayCtx.testRunId },
3079
+ ...replayCtx?.inputSourceSpanId && {
3080
+ inputSourceSpanId: replayCtx.inputSourceSpanId
3026
3081
  }
3027
3082
  });
3028
- activeTraceStates.delete(traceId);
3029
- if (persistenceCollector) {
3030
- await completionPromise;
3031
- }
3032
- } else {
3033
- const pending = pendingSpanPromises.get(traceId);
3034
- if (pending) {
3083
+ if (isRootSpan) {
3084
+ const pending = pendingSpanPromises.get(traceId) ?? [];
3035
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
+ }
3036
3134
  } else {
3037
- 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
+ }
3038
3141
  }
3142
+ } catch {
3143
+ } finally {
3144
+ resolvePersistence?.();
3039
3145
  }
3040
- } catch {
3041
- } finally {
3042
- resolvePersistence?.();
3043
- }
3044
- };
3045
- const replayCtxForMock = getReplayContext();
3046
- if (replayCtxForMock?.mockTree && !isRootSpan) {
3047
- const counters = replayCtxForMock.callCounters;
3048
- const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
3049
- const callIndex = counters.get(counterKey) ?? 0;
3050
- counters.set(counterKey, callIndex + 1);
3051
- const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
3052
- if (shouldMock) {
3053
- const mockKey = `${counterKey}:${callIndex}`;
3054
- const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
3055
- if (mockSpan) {
3056
- let output = mockSpan.output;
3057
- if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
3058
- output = deserializeValue({
3059
- json: mockSpan.output,
3060
- meta: mockSpan.outputMeta
3061
- });
3062
- }
3063
- void sendSpan({ result: output });
3064
- if (fnReturnsPromise) {
3065
- 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;
3066
3170
  }
3067
- return output;
3068
3171
  }
3069
3172
  }
3070
- }
3071
- const recordSpan = (result) => {
3072
- if (options.finalize) {
3073
- const replayCtx = getReplayContext();
3074
- const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
3075
- let resolvePersistence;
3076
- if (persistenceCollector) {
3077
- persistenceCollector.push(
3078
- new Promise((resolve) => {
3079
- resolvePersistence = resolve;
3080
- })
3081
- );
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 });
3082
3201
  }
3083
- void Promise.resolve().then(() => options.finalize(result)).then(
3084
- (output) => sendSpan(
3085
- { result: output },
3086
- { skipPersistenceRegistration: true }
3087
- )
3088
- ).catch(
3089
- (error) => sendSpan(
3090
- {
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({
3091
3211
  result: void 0,
3092
- error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
3093
- },
3094
- { skipPersistenceRegistration: true }
3095
- )
3096
- ).finally(() => resolvePersistence?.());
3097
- } else {
3098
- void sendSpan({ result });
3099
- }
3100
- };
3101
- const executeWithContext = () => {
3102
- const result = fn(...args);
3103
- if (result instanceof Promise) {
3104
- return result.then((resolvedResult) => {
3105
- recordSpan(resolvedResult);
3106
- return resolvedResult;
3107
- }).catch((error) => {
3108
- void sendSpan({
3109
- result: void 0,
3110
- error: error instanceof Error ? error.message : String(error)
3212
+ error: error instanceof Error ? error.message : String(error)
3213
+ });
3214
+ throw error;
3111
3215
  });
3112
- throw error;
3113
- });
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);
3114
3227
  }
3115
- if (isAsyncGenerator(result)) {
3116
- return wrapAsyncGenerator(result, newStack, sendSpan);
3228
+ if (getReplayContext()) {
3229
+ throw setupError;
3117
3230
  }
3118
- recordSpan(result);
3119
- return result;
3120
- };
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
+ }
3121
3237
  return runWithSpanStack(newStack, executeWithContext);
3122
3238
  };
3123
3239
  Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
@@ -3334,7 +3450,7 @@ var Bitfab = class {
3334
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.`
3335
3451
  );
3336
3452
  }
3337
- const { replay: doReplay } = await import("./replay-V6RPJYXZ.js");
3453
+ const { replay: doReplay } = await import("./replay-CQIU2ITL.js");
3338
3454
  return doReplay(
3339
3455
  this.httpClient,
3340
3456
  this.serviceUrl,
@@ -3464,4 +3580,4 @@ export {
3464
3580
  BitfabFunction,
3465
3581
  finalizers
3466
3582
  };
3467
- //# sourceMappingURL=chunk-PP5K6RIU.js.map
3583
+ //# sourceMappingURL=chunk-S3PN26RH.js.map