bitfab 0.36.2 → 0.36.3

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/index.cjs CHANGED
@@ -51,7 +51,7 @@ var __version__;
51
51
  var init_version_generated = __esm({
52
52
  "src/version.generated.ts"() {
53
53
  "use strict";
54
- __version__ = "0.36.2";
54
+ __version__ = "0.36.3";
55
55
  }
56
56
  });
57
57
 
@@ -2062,8 +2062,11 @@ var init_codeChange = __esm({
2062
2062
  var replay_exports = {};
2063
2063
  __export(replay_exports, {
2064
2064
  BITFAB_PROGRESS_PREFIX: () => BITFAB_PROGRESS_PREFIX,
2065
+ DbBranchReplayError: () => DbBranchReplayError,
2066
+ ReplayError: () => ReplayError,
2065
2067
  replay: () => replay,
2066
- reportReplayProgress: () => reportReplayProgress
2068
+ reportReplayProgress: () => reportReplayProgress,
2069
+ serializeReplayResult: () => serializeReplayResult
2067
2070
  });
2068
2071
  function dbBranchEnabled(dbBranch) {
2069
2072
  return dbBranch !== void 0 && dbBranch !== false;
@@ -2086,11 +2089,59 @@ function reportReplayProgress(progress) {
2086
2089
  return;
2087
2090
  }
2088
2091
  try {
2089
- stderr.write(`${BITFAB_PROGRESS_PREFIX}${JSON.stringify(progress)}
2090
- `);
2092
+ stderr.write(
2093
+ `${BITFAB_PROGRESS_PREFIX}${JSON.stringify(progress, replayJsonReplacer)}
2094
+ `
2095
+ );
2091
2096
  } catch {
2092
2097
  }
2093
2098
  }
2099
+ function errorMessage(error) {
2100
+ return error instanceof Error ? error.message : String(error);
2101
+ }
2102
+ function replayItemErrorMessage(error) {
2103
+ if (error instanceof DbBranchReplayError) {
2104
+ return `Replay requested a database branch for trace ${error.originalTraceId} but it could not be resolved (${error.code}): ${error.message}. The function was not run, because replaying it against the live database would produce a result that looks valid but did not use the historical data you asked for.`;
2105
+ }
2106
+ return errorMessage(error);
2107
+ }
2108
+ function replayJsonReplacer(_key, value) {
2109
+ if (value instanceof Error) {
2110
+ const serialized = {
2111
+ name: value.name,
2112
+ message: value.message,
2113
+ stack: value.stack
2114
+ };
2115
+ if (value instanceof DbBranchReplayError) {
2116
+ serialized.code = value.code;
2117
+ serialized.originalTraceId = value.originalTraceId;
2118
+ if (value.cause !== void 0) {
2119
+ serialized.cause = value.cause;
2120
+ }
2121
+ }
2122
+ return serialized;
2123
+ }
2124
+ return value;
2125
+ }
2126
+ function serializeReplayResult(result) {
2127
+ return JSON.stringify(result, replayJsonReplacer, 2);
2128
+ }
2129
+ async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
2130
+ try {
2131
+ return await operation();
2132
+ } catch (cause) {
2133
+ if (cause instanceof ReplayError) {
2134
+ throw cause;
2135
+ }
2136
+ throw new ReplayError(
2137
+ errorMessage(cause),
2138
+ items,
2139
+ testRunId,
2140
+ testRunUrl,
2141
+ cause
2142
+ );
2143
+ }
2144
+ }
2094
2145
  function deserializeInputs(spanData) {
2095
2146
  const inputMeta = spanData.input_meta;
2096
2147
  const rawInput = spanData.input;
@@ -2148,22 +2199,36 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2148
2199
  let originalOutput;
2149
2200
  let result;
2150
2201
  let error = null;
2202
+ let traceError = null;
2203
+ let replayError = null;
2151
2204
  const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
2152
2205
  const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
2153
2206
  try {
2154
2207
  if (includeDbBranchLease && !lease && !leaseError) {
2155
- const resolved = await httpClient.resolveDbBranchLease(
2156
- testRunId,
2157
- originalTraceId,
2158
- dbBranchSettings
2159
- );
2208
+ let resolved;
2209
+ try {
2210
+ resolved = await httpClient.resolveDbBranchLease(
2211
+ testRunId,
2212
+ originalTraceId,
2213
+ dbBranchSettings
2214
+ );
2215
+ } catch (cause) {
2216
+ throw new DbBranchReplayError(
2217
+ "lease_request_failed",
2218
+ `Bitfab could not request the database branch: ${errorMessage(cause)}`,
2219
+ originalTraceId,
2220
+ cause
2221
+ );
2222
+ }
2160
2223
  lease = resolved.lease ?? void 0;
2161
2224
  leaseError = resolved.leaseError ?? void 0;
2162
2225
  dbSnapshotRef = resolved.dbSnapshotRef ?? dbSnapshotRef;
2163
2226
  }
2164
2227
  if (leaseError) {
2165
- throw new BitfabError(
2166
- `Replay requested a database branch for trace ${originalTraceId} but it could not be resolved (${leaseError.code}): ${leaseError.message}. The function was not run, because replaying it against the live database would produce a result that looks valid but did not use the historical data you asked for.`
2228
+ throw new DbBranchReplayError(
2229
+ leaseError.code,
2230
+ leaseError.message,
2231
+ originalTraceId
2167
2232
  );
2168
2233
  }
2169
2234
  const span = await httpClient.getExternalSpan(originalSpanId, {
@@ -2220,25 +2285,31 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2220
2285
  }
2221
2286
  return pending;
2222
2287
  } : void 0;
2223
- const maybePromise = runWithReplayContext(
2224
- {
2225
- testRunId,
2226
- traceId: replayedTraceId,
2227
- inputSourceSpanId: span.id,
2228
- inputSourceTraceId: span.externalTraceId,
2229
- sourceBitfabTraceId: originalTraceId,
2230
- mockTree,
2231
- callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
2232
- mockStrategy,
2233
- mockOverrides: hasOverrides ? resolvedOverrides : void 0,
2234
- fetchSpanOutput,
2235
- dbBranchLease: lease
2236
- },
2237
- () => fn(...inputs)
2238
- );
2239
- result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
2288
+ try {
2289
+ const maybePromise = runWithReplayContext(
2290
+ {
2291
+ testRunId,
2292
+ traceId: replayedTraceId,
2293
+ inputSourceSpanId: span.id,
2294
+ inputSourceTraceId: span.externalTraceId,
2295
+ sourceBitfabTraceId: originalTraceId,
2296
+ mockTree,
2297
+ callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
2298
+ mockStrategy,
2299
+ mockOverrides: hasOverrides ? resolvedOverrides : void 0,
2300
+ fetchSpanOutput,
2301
+ dbBranchLease: lease
2302
+ },
2303
+ () => fn(...inputs)
2304
+ );
2305
+ result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
2306
+ } catch (e) {
2307
+ traceError = e;
2308
+ error = errorMessage(e);
2309
+ }
2240
2310
  } catch (e) {
2241
- error = e instanceof Error ? e.message : String(e);
2311
+ replayError = e;
2312
+ error = replayItemErrorMessage(e);
2242
2313
  } finally {
2243
2314
  if (lease) {
2244
2315
  try {
@@ -2267,6 +2338,8 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2267
2338
  result,
2268
2339
  originalOutput,
2269
2340
  error,
2341
+ traceError,
2342
+ replayError,
2270
2343
  durationMs: serverItem.durationMs ?? null,
2271
2344
  // Filled in by replay() from the complete-replay response once the
2272
2345
  // replay traces are persisted and their spans aggregated server-side.
@@ -2391,6 +2464,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2391
2464
  );
2392
2465
  const mockStrategy = options?.mock ?? "marked";
2393
2466
  const maxConcurrency = options?.maxConcurrency ?? 10;
2467
+ const fullTestRunUrl = `${serviceUrl}${testRunUrl}`;
2394
2468
  const resolvedOverrides = [
2395
2469
  ...normalizeMockOverrides(options?.mockOverride),
2396
2470
  ...registeredOverrides
@@ -2447,6 +2521,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2447
2521
  result: item.result,
2448
2522
  originalOutput: item.originalOutput,
2449
2523
  error: item.error,
2524
+ traceError: item.traceError,
2525
+ replayError: item.replayError,
2450
2526
  durationMs: item.durationMs,
2451
2527
  tokens: item.tokens,
2452
2528
  model: item.model,
@@ -2457,8 +2533,18 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2457
2533
  }
2458
2534
  } : void 0
2459
2535
  );
2460
- await waitForReplayPersistence(httpClient, testRunId, replayedTraceIds);
2461
- const completeResult = await httpClient.completeReplay(testRunId);
2536
+ await preserveReplayFailure(
2537
+ () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),
2538
+ resultItems,
2539
+ testRunId,
2540
+ fullTestRunUrl
2541
+ );
2542
+ const completeResult = await preserveReplayFailure(
2543
+ () => httpClient.completeReplay(testRunId),
2544
+ resultItems,
2545
+ testRunId,
2546
+ fullTestRunUrl
2547
+ );
2462
2548
  const serverTraceIds = completeResult.traceIds;
2463
2549
  const replayTokens = completeResult.tokens;
2464
2550
  if (serverTraceIds !== void 0) {
@@ -2481,9 +2567,16 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2481
2567
  }
2482
2568
  if (completedCount > 0 && missing.length === completedCount) {
2483
2569
  const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
2484
- throw new BitfabError(
2570
+ const cause = new BitfabError(
2485
2571
  `Replay completed but the server has no persisted trace for any of the ${completedCount} completed item(s) (testRunId ${testRunId}).${serverCount} Trace uploads were awaited, so either the uploads failed (check for "Bitfab: Failed to create" errors above) or the replayed function is not wrapped with withSpan.`
2486
2572
  );
2573
+ throw new ReplayError(
2574
+ cause.message,
2575
+ resultItems,
2576
+ testRunId,
2577
+ fullTestRunUrl,
2578
+ cause
2579
+ );
2487
2580
  }
2488
2581
  if (missing.length > 0) {
2489
2582
  try {
@@ -2497,7 +2590,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2497
2590
  const result = {
2498
2591
  items: resultItems,
2499
2592
  testRunId,
2500
- testRunUrl: `${serviceUrl}${testRunUrl}`
2593
+ testRunUrl: fullTestRunUrl
2501
2594
  };
2502
2595
  await writeReplayResultFile(result);
2503
2596
  try {
@@ -2525,7 +2618,7 @@ async function writeReplayResultFile(result) {
2525
2618
  import("fs/promises")
2526
2619
  ]);
2527
2620
  await mkdir(dirname(resultPath), { recursive: true });
2528
- await writeFile(resultPath, `${JSON.stringify(result, null, 2)}
2621
+ await writeFile(resultPath, `${serializeReplayResult(result)}
2529
2622
  `);
2530
2623
  } catch (err) {
2531
2624
  try {
@@ -2536,7 +2629,7 @@ async function writeReplayResultFile(result) {
2536
2629
  }
2537
2630
  }
2538
2631
  }
2539
- var REPLAY_PERSISTENCE_TIMEOUT_MS, BITFAB_PROGRESS_PREFIX;
2632
+ var REPLAY_PERSISTENCE_TIMEOUT_MS, BITFAB_PROGRESS_PREFIX, ReplayError, DbBranchReplayError;
2540
2633
  var init_replay = __esm({
2541
2634
  "src/replay.ts"() {
2542
2635
  "use strict";
@@ -2551,6 +2644,25 @@ var init_replay = __esm({
2551
2644
  init_unrefTimer();
2552
2645
  REPLAY_PERSISTENCE_TIMEOUT_MS = 3e4;
2553
2646
  BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
2647
+ ReplayError = class extends BitfabError {
2648
+ constructor(message, items, testRunId, testRunUrl, cause) {
2649
+ super(message, testRunUrl);
2650
+ this.items = items;
2651
+ this.testRunId = testRunId;
2652
+ this.testRunUrl = testRunUrl;
2653
+ this.cause = cause;
2654
+ this.name = "ReplayError";
2655
+ }
2656
+ };
2657
+ DbBranchReplayError = class extends BitfabError {
2658
+ constructor(code, message, originalTraceId, cause) {
2659
+ super(message);
2660
+ this.code = code;
2661
+ this.originalTraceId = originalTraceId;
2662
+ this.cause = cause;
2663
+ this.name = "DbBranchReplayError";
2664
+ }
2665
+ };
2554
2666
  }
2555
2667
  });
2556
2668
 
@@ -2568,7 +2680,9 @@ __export(index_exports, {
2568
2680
  BitfabOpenAITracingProcessor: () => BitfabOpenAITracingProcessor,
2569
2681
  BitfabVercelAiHandler: () => BitfabVercelAiHandler,
2570
2682
  DEFAULT_SERVICE_URL: () => DEFAULT_SERVICE_URL,
2683
+ DbBranchReplayError: () => DbBranchReplayError,
2571
2684
  HttpClient: () => HttpClient,
2685
+ ReplayError: () => ReplayError,
2572
2686
  SUPPORTED_PROVIDERS: () => SUPPORTED_PROVIDERS,
2573
2687
  __version__: () => __version__,
2574
2688
  finalizers: () => finalizers,
@@ -2576,7 +2690,8 @@ __export(index_exports, {
2576
2690
  getCurrentReplayBranch: () => getCurrentReplayBranch,
2577
2691
  getCurrentSpan: () => getCurrentSpan,
2578
2692
  getCurrentTrace: () => getCurrentTrace,
2579
- reportReplayProgress: () => reportReplayProgress
2693
+ reportReplayProgress: () => reportReplayProgress,
2694
+ serializeReplayResult: () => serializeReplayResult
2580
2695
  });
2581
2696
  module.exports = __toCommonJS(index_exports);
2582
2697
 
@@ -6157,7 +6272,9 @@ init_replay();
6157
6272
  BitfabOpenAITracingProcessor,
6158
6273
  BitfabVercelAiHandler,
6159
6274
  DEFAULT_SERVICE_URL,
6275
+ DbBranchReplayError,
6160
6276
  HttpClient,
6277
+ ReplayError,
6161
6278
  SUPPORTED_PROVIDERS,
6162
6279
  __version__,
6163
6280
  finalizers,
@@ -6165,6 +6282,7 @@ init_replay();
6165
6282
  getCurrentReplayBranch,
6166
6283
  getCurrentSpan,
6167
6284
  getCurrentTrace,
6168
- reportReplayProgress
6285
+ reportReplayProgress,
6286
+ serializeReplayResult
6169
6287
  });
6170
6288
  //# sourceMappingURL=index.cjs.map