bitfab 0.36.1 → 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.d.cts CHANGED
@@ -457,8 +457,11 @@ declare class HttpClient {
457
457
  /**
458
458
  * Fetch an external span by ID.
459
459
  * Blocking GET request.
460
+ * The replay view limits rawData to input/output serialization fields.
460
461
  */
461
- getExternalSpan(spanId: string): Promise<ExternalSpanResponse>;
462
+ getExternalSpan(spanId: string, options?: {
463
+ view?: "full" | "replay";
464
+ }): Promise<ExternalSpanResponse>;
462
465
  /**
463
466
  * Fetch the span tree for a root span.
464
467
  * Blocking GET request.
@@ -466,9 +469,11 @@ declare class HttpClient {
466
469
  * Pass `includeOutputs: false` for a payload-free tree (structure +
467
470
  * `externalSpanId` only), so recorded outputs are fetched lazily per mocked
468
471
  * span instead of all up front. Omit it (default eager) for `mock: "all"`.
472
+ * Pass `includeRootOutput: false` when the root was already fetched.
469
473
  */
470
474
  getSpanTree(externalSpanId: string, options?: {
471
475
  includeOutputs?: boolean;
476
+ includeRootOutput?: boolean;
472
477
  }): Promise<SpanTreeResponse>;
473
478
  /**
474
479
  * Read which of a replay run's traces the server has fully persisted.
@@ -1237,9 +1242,9 @@ interface ReplayProgress {
1237
1242
  completed: number;
1238
1243
  /** Total number of items in this replay run. */
1239
1244
  total: number;
1240
- /** Of the completed items, how many ran the function without throwing. */
1245
+ /** Of the completed items, how many finished without a code or replay error. */
1241
1246
  succeeded: number;
1242
- /** Of the completed items, how many threw (their `item.error` is set). */
1247
+ /** Of the completed items, how many have `item.error` set. */
1243
1248
  errored: number;
1244
1249
  /**
1245
1250
  * The single item that just settled to produce this event. `traceId` is null
@@ -1267,7 +1272,12 @@ interface ReplayProgress {
1267
1272
  result?: unknown;
1268
1273
  /** The original output from the historical trace. */
1269
1274
  originalOutput?: unknown;
1275
+ /** Backward-compatible message for either error kind. */
1270
1276
  error: string | null;
1277
+ /** The actual value thrown by the replayed customer function. */
1278
+ traceError: unknown | null;
1279
+ /** The actual value thrown while Bitfab prepared this replay item. */
1280
+ replayError: unknown | null;
1271
1281
  durationMs: number | null;
1272
1282
  tokens?: TokenUsage | null;
1273
1283
  model?: string | null;
@@ -1345,8 +1355,18 @@ interface ReplayItem<T> {
1345
1355
  result: T | undefined;
1346
1356
  /** The original output from the historical trace. */
1347
1357
  originalOutput: unknown;
1348
- /** Error message if the function threw, or null on success. */
1358
+ /**
1359
+ * Backward-compatible message for either error kind. Prefer `traceError` and
1360
+ * `replayError` when callers need the original exception and its source.
1361
+ */
1349
1362
  error: string | null;
1363
+ /** The actual value thrown by the replayed customer function. */
1364
+ traceError: unknown | null;
1365
+ /**
1366
+ * The actual value thrown by replay setup before the customer function ran,
1367
+ * such as database warmup, input hydration, or mock preparation.
1368
+ */
1369
+ replayError: unknown | null;
1350
1370
  /** Original trace duration in milliseconds, or null if timestamps are missing. */
1351
1371
  durationMs: number | null;
1352
1372
  /**
@@ -1376,6 +1396,37 @@ interface ReplayResult<T> {
1376
1396
  /** Full URL to view the test run in the dashboard. */
1377
1397
  testRunUrl: string;
1378
1398
  }
1399
+ /**
1400
+ * Whole-run replay failure that preserves every item collected before the run
1401
+ * failed. The original whole-run exception is available as `cause`.
1402
+ */
1403
+ declare class ReplayError<T = unknown> extends BitfabError {
1404
+ readonly items: ReplayItem<T>[];
1405
+ readonly testRunId: string;
1406
+ readonly testRunUrl: string;
1407
+ readonly cause: unknown;
1408
+ constructor(message: string, items: ReplayItem<T>[], testRunId: string, testRunUrl: string, cause: unknown);
1409
+ }
1410
+ /**
1411
+ * A database branch requested for one replay item could not be resolved.
1412
+ *
1413
+ * The server's resolver code and message are retained as structured fields so
1414
+ * callers can handle failures such as `branch_create_failed` and
1415
+ * `snapshot_from_replaced_origin` without parsing the compatible `item.error`
1416
+ * string.
1417
+ */
1418
+ declare class DbBranchReplayError extends BitfabError {
1419
+ readonly code: string;
1420
+ readonly originalTraceId: string;
1421
+ readonly cause?: unknown | undefined;
1422
+ constructor(code: string, message: string, originalTraceId: string, cause?: unknown | undefined);
1423
+ }
1424
+ /**
1425
+ * Serialize a replay result as JSON while retaining structured trace and replay
1426
+ * errors. Use this for direct-run stdout instead of raw `JSON.stringify`, which
1427
+ * drops the useful fields on JavaScript `Error` objects.
1428
+ */
1429
+ declare function serializeReplayResult<T>(result: ReplayResult<T>): string;
1379
1430
 
1380
1431
  /**
1381
1432
  * The database branch a single replay item runs against.
@@ -2348,7 +2399,7 @@ declare class BitfabFunction {
2348
2399
  /**
2349
2400
  * SDK version from package.json (injected at build time)
2350
2401
  */
2351
- declare const __version__ = "0.36.1";
2402
+ declare const __version__ = "0.36.3";
2352
2403
 
2353
2404
  /**
2354
2405
  * Constants for the Bitfab SDK.
@@ -2416,4 +2467,4 @@ declare const finalizers: {
2416
2467
  readableStream: typeof readableStream;
2417
2468
  };
2418
2469
 
2419
- export { type ActiveSpanContext, type AdaptContext, type AdaptInputsFn, type AllowedEnvVars, BITFAB_PROGRESS_PREFIX, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler as BitfabLangChainCallbackHandler, BitfabLangGraphCallbackHandler, type BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, type CaptureWhen, type CapturedSpan, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type DbBranchOptions, type DbSnapshotConfig, type DbSnapshotProvider, type DbSnapshotRef, type DetachedTrace, HttpClient, type MockOverride, type MockOverrideCtx, type MockStrategy, type MockValue, type NodeMatcher, type ProviderDefinition, ReplayBranch, type ReplayItem, type ReplayOptions, type ReplayProgress, type ReplayResult, SUPPORTED_PROVIDERS, type SpanLookup, type SpanNodeMeta, type SpanOccurrence, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type VercelCallParams, type VercelGenerateResult, type VercelStreamResult, type WrapBAMLOptions, type WrappedBamlFn, __version__, finalizers, flushTraces, getCurrentReplayBranch, getCurrentSpan, getCurrentTrace, reportReplayProgress };
2470
+ export { type ActiveSpanContext, type AdaptContext, type AdaptInputsFn, type AllowedEnvVars, BITFAB_PROGRESS_PREFIX, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler as BitfabLangChainCallbackHandler, BitfabLangGraphCallbackHandler, type BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, type CaptureWhen, type CapturedSpan, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type DbBranchOptions, DbBranchReplayError, type DbSnapshotConfig, type DbSnapshotProvider, type DbSnapshotRef, type DetachedTrace, HttpClient, type MockOverride, type MockOverrideCtx, type MockStrategy, type MockValue, type NodeMatcher, type ProviderDefinition, ReplayBranch, ReplayError, type ReplayItem, type ReplayOptions, type ReplayProgress, type ReplayResult, SUPPORTED_PROVIDERS, type SpanLookup, type SpanNodeMeta, type SpanOccurrence, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type VercelCallParams, type VercelGenerateResult, type VercelStreamResult, type WrapBAMLOptions, type WrappedBamlFn, __version__, finalizers, flushTraces, getCurrentReplayBranch, getCurrentSpan, getCurrentTrace, reportReplayProgress, serializeReplayResult };
package/dist/index.d.ts CHANGED
@@ -457,8 +457,11 @@ declare class HttpClient {
457
457
  /**
458
458
  * Fetch an external span by ID.
459
459
  * Blocking GET request.
460
+ * The replay view limits rawData to input/output serialization fields.
460
461
  */
461
- getExternalSpan(spanId: string): Promise<ExternalSpanResponse>;
462
+ getExternalSpan(spanId: string, options?: {
463
+ view?: "full" | "replay";
464
+ }): Promise<ExternalSpanResponse>;
462
465
  /**
463
466
  * Fetch the span tree for a root span.
464
467
  * Blocking GET request.
@@ -466,9 +469,11 @@ declare class HttpClient {
466
469
  * Pass `includeOutputs: false` for a payload-free tree (structure +
467
470
  * `externalSpanId` only), so recorded outputs are fetched lazily per mocked
468
471
  * span instead of all up front. Omit it (default eager) for `mock: "all"`.
472
+ * Pass `includeRootOutput: false` when the root was already fetched.
469
473
  */
470
474
  getSpanTree(externalSpanId: string, options?: {
471
475
  includeOutputs?: boolean;
476
+ includeRootOutput?: boolean;
472
477
  }): Promise<SpanTreeResponse>;
473
478
  /**
474
479
  * Read which of a replay run's traces the server has fully persisted.
@@ -1237,9 +1242,9 @@ interface ReplayProgress {
1237
1242
  completed: number;
1238
1243
  /** Total number of items in this replay run. */
1239
1244
  total: number;
1240
- /** Of the completed items, how many ran the function without throwing. */
1245
+ /** Of the completed items, how many finished without a code or replay error. */
1241
1246
  succeeded: number;
1242
- /** Of the completed items, how many threw (their `item.error` is set). */
1247
+ /** Of the completed items, how many have `item.error` set. */
1243
1248
  errored: number;
1244
1249
  /**
1245
1250
  * The single item that just settled to produce this event. `traceId` is null
@@ -1267,7 +1272,12 @@ interface ReplayProgress {
1267
1272
  result?: unknown;
1268
1273
  /** The original output from the historical trace. */
1269
1274
  originalOutput?: unknown;
1275
+ /** Backward-compatible message for either error kind. */
1270
1276
  error: string | null;
1277
+ /** The actual value thrown by the replayed customer function. */
1278
+ traceError: unknown | null;
1279
+ /** The actual value thrown while Bitfab prepared this replay item. */
1280
+ replayError: unknown | null;
1271
1281
  durationMs: number | null;
1272
1282
  tokens?: TokenUsage | null;
1273
1283
  model?: string | null;
@@ -1345,8 +1355,18 @@ interface ReplayItem<T> {
1345
1355
  result: T | undefined;
1346
1356
  /** The original output from the historical trace. */
1347
1357
  originalOutput: unknown;
1348
- /** Error message if the function threw, or null on success. */
1358
+ /**
1359
+ * Backward-compatible message for either error kind. Prefer `traceError` and
1360
+ * `replayError` when callers need the original exception and its source.
1361
+ */
1349
1362
  error: string | null;
1363
+ /** The actual value thrown by the replayed customer function. */
1364
+ traceError: unknown | null;
1365
+ /**
1366
+ * The actual value thrown by replay setup before the customer function ran,
1367
+ * such as database warmup, input hydration, or mock preparation.
1368
+ */
1369
+ replayError: unknown | null;
1350
1370
  /** Original trace duration in milliseconds, or null if timestamps are missing. */
1351
1371
  durationMs: number | null;
1352
1372
  /**
@@ -1376,6 +1396,37 @@ interface ReplayResult<T> {
1376
1396
  /** Full URL to view the test run in the dashboard. */
1377
1397
  testRunUrl: string;
1378
1398
  }
1399
+ /**
1400
+ * Whole-run replay failure that preserves every item collected before the run
1401
+ * failed. The original whole-run exception is available as `cause`.
1402
+ */
1403
+ declare class ReplayError<T = unknown> extends BitfabError {
1404
+ readonly items: ReplayItem<T>[];
1405
+ readonly testRunId: string;
1406
+ readonly testRunUrl: string;
1407
+ readonly cause: unknown;
1408
+ constructor(message: string, items: ReplayItem<T>[], testRunId: string, testRunUrl: string, cause: unknown);
1409
+ }
1410
+ /**
1411
+ * A database branch requested for one replay item could not be resolved.
1412
+ *
1413
+ * The server's resolver code and message are retained as structured fields so
1414
+ * callers can handle failures such as `branch_create_failed` and
1415
+ * `snapshot_from_replaced_origin` without parsing the compatible `item.error`
1416
+ * string.
1417
+ */
1418
+ declare class DbBranchReplayError extends BitfabError {
1419
+ readonly code: string;
1420
+ readonly originalTraceId: string;
1421
+ readonly cause?: unknown | undefined;
1422
+ constructor(code: string, message: string, originalTraceId: string, cause?: unknown | undefined);
1423
+ }
1424
+ /**
1425
+ * Serialize a replay result as JSON while retaining structured trace and replay
1426
+ * errors. Use this for direct-run stdout instead of raw `JSON.stringify`, which
1427
+ * drops the useful fields on JavaScript `Error` objects.
1428
+ */
1429
+ declare function serializeReplayResult<T>(result: ReplayResult<T>): string;
1379
1430
 
1380
1431
  /**
1381
1432
  * The database branch a single replay item runs against.
@@ -2348,7 +2399,7 @@ declare class BitfabFunction {
2348
2399
  /**
2349
2400
  * SDK version from package.json (injected at build time)
2350
2401
  */
2351
- declare const __version__ = "0.36.1";
2402
+ declare const __version__ = "0.36.3";
2352
2403
 
2353
2404
  /**
2354
2405
  * Constants for the Bitfab SDK.
@@ -2416,4 +2467,4 @@ declare const finalizers: {
2416
2467
  readableStream: typeof readableStream;
2417
2468
  };
2418
2469
 
2419
- export { type ActiveSpanContext, type AdaptContext, type AdaptInputsFn, type AllowedEnvVars, BITFAB_PROGRESS_PREFIX, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler as BitfabLangChainCallbackHandler, BitfabLangGraphCallbackHandler, type BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, type CaptureWhen, type CapturedSpan, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type DbBranchOptions, type DbSnapshotConfig, type DbSnapshotProvider, type DbSnapshotRef, type DetachedTrace, HttpClient, type MockOverride, type MockOverrideCtx, type MockStrategy, type MockValue, type NodeMatcher, type ProviderDefinition, ReplayBranch, type ReplayItem, type ReplayOptions, type ReplayProgress, type ReplayResult, SUPPORTED_PROVIDERS, type SpanLookup, type SpanNodeMeta, type SpanOccurrence, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type VercelCallParams, type VercelGenerateResult, type VercelStreamResult, type WrapBAMLOptions, type WrappedBamlFn, __version__, finalizers, flushTraces, getCurrentReplayBranch, getCurrentSpan, getCurrentTrace, reportReplayProgress };
2470
+ export { type ActiveSpanContext, type AdaptContext, type AdaptInputsFn, type AllowedEnvVars, BITFAB_PROGRESS_PREFIX, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler as BitfabLangChainCallbackHandler, BitfabLangGraphCallbackHandler, type BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, type CaptureWhen, type CapturedSpan, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type DbBranchOptions, DbBranchReplayError, type DbSnapshotConfig, type DbSnapshotProvider, type DbSnapshotRef, type DetachedTrace, HttpClient, type MockOverride, type MockOverrideCtx, type MockStrategy, type MockValue, type NodeMatcher, type ProviderDefinition, ReplayBranch, ReplayError, type ReplayItem, type ReplayOptions, type ReplayProgress, type ReplayResult, SUPPORTED_PROVIDERS, type SpanLookup, type SpanNodeMeta, type SpanOccurrence, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type VercelCallParams, type VercelGenerateResult, type VercelStreamResult, type WrapBAMLOptions, type WrappedBamlFn, __version__, finalizers, flushTraces, getCurrentReplayBranch, getCurrentSpan, getCurrentTrace, reportReplayProgress, serializeReplayResult };
package/dist/index.js CHANGED
@@ -20,16 +20,19 @@ import {
20
20
  getCurrentReplayBranch,
21
21
  getCurrentSpan,
22
22
  getCurrentTrace
23
- } from "./chunk-HD4DRDRE.js";
23
+ } from "./chunk-4CTDZMG5.js";
24
24
  import {
25
25
  BITFAB_PROGRESS_PREFIX,
26
26
  BitfabError,
27
27
  DEFAULT_SERVICE_URL,
28
+ DbBranchReplayError,
28
29
  HttpClient,
30
+ ReplayError,
29
31
  __version__,
30
32
  flushTraces,
31
- reportReplayProgress
32
- } from "./chunk-6ZBZBR5K.js";
33
+ reportReplayProgress,
34
+ serializeReplayResult
35
+ } from "./chunk-M2GISXGV.js";
33
36
  export {
34
37
  BITFAB_PROGRESS_PREFIX,
35
38
  Bitfab,
@@ -42,7 +45,9 @@ export {
42
45
  BitfabOpenAITracingProcessor,
43
46
  BitfabVercelAiHandler,
44
47
  DEFAULT_SERVICE_URL,
48
+ DbBranchReplayError,
45
49
  HttpClient,
50
+ ReplayError,
46
51
  SUPPORTED_PROVIDERS,
47
52
  __version__,
48
53
  finalizers,
@@ -50,6 +55,7 @@ export {
50
55
  getCurrentReplayBranch,
51
56
  getCurrentSpan,
52
57
  getCurrentTrace,
53
- reportReplayProgress
58
+ reportReplayProgress,
59
+ serializeReplayResult
54
60
  };
55
61
  //# sourceMappingURL=index.js.map
package/dist/node.cjs CHANGED
@@ -97,7 +97,7 @@ var __version__;
97
97
  var init_version_generated = __esm({
98
98
  "src/version.generated.ts"() {
99
99
  "use strict";
100
- __version__ = "0.36.1";
100
+ __version__ = "0.36.3";
101
101
  }
102
102
  });
103
103
 
@@ -1537,9 +1537,11 @@ var init_http = __esm({
1537
1537
  /**
1538
1538
  * Fetch an external span by ID.
1539
1539
  * Blocking GET request.
1540
+ * The replay view limits rawData to input/output serialization fields.
1540
1541
  */
1541
- async getExternalSpan(spanId) {
1542
- const url = `${this.serviceUrl}/api/sdk/externalSpans/${spanId}`;
1542
+ async getExternalSpan(spanId, options) {
1543
+ const query = options?.view === "replay" ? "?view=replay" : "";
1544
+ const url = `${this.serviceUrl}/api/sdk/externalSpans/${spanId}${query}`;
1543
1545
  const controller = new AbortController();
1544
1546
  const timeoutId = setTimeout(() => controller.abort(), 3e4);
1545
1547
  try {
@@ -1577,9 +1579,18 @@ var init_http = __esm({
1577
1579
  * Pass `includeOutputs: false` for a payload-free tree (structure +
1578
1580
  * `externalSpanId` only), so recorded outputs are fetched lazily per mocked
1579
1581
  * span instead of all up front. Omit it (default eager) for `mock: "all"`.
1582
+ * Pass `includeRootOutput: false` when the root was already fetched.
1580
1583
  */
1581
1584
  async getSpanTree(externalSpanId, options) {
1582
- const query = options?.includeOutputs === false ? "?includeOutputs=false" : "";
1585
+ const searchParams = new URLSearchParams();
1586
+ if (options?.includeOutputs === false) {
1587
+ searchParams.set("includeOutputs", "false");
1588
+ }
1589
+ if (options?.includeRootOutput === false) {
1590
+ searchParams.set("includeRootOutput", "false");
1591
+ }
1592
+ const encodedQuery = searchParams.toString();
1593
+ const query = encodedQuery ? `?${encodedQuery}` : "";
1583
1594
  const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}${query}`;
1584
1595
  const controller = new AbortController();
1585
1596
  const timeoutId = setTimeout(() => controller.abort(), 3e4);
@@ -2058,8 +2069,11 @@ var init_codeChange = __esm({
2058
2069
  var replay_exports = {};
2059
2070
  __export(replay_exports, {
2060
2071
  BITFAB_PROGRESS_PREFIX: () => BITFAB_PROGRESS_PREFIX,
2072
+ DbBranchReplayError: () => DbBranchReplayError,
2073
+ ReplayError: () => ReplayError,
2061
2074
  replay: () => replay,
2062
- reportReplayProgress: () => reportReplayProgress
2075
+ reportReplayProgress: () => reportReplayProgress,
2076
+ serializeReplayResult: () => serializeReplayResult
2063
2077
  });
2064
2078
  function dbBranchEnabled(dbBranch) {
2065
2079
  return dbBranch !== void 0 && dbBranch !== false;
@@ -2082,11 +2096,59 @@ function reportReplayProgress(progress) {
2082
2096
  return;
2083
2097
  }
2084
2098
  try {
2085
- stderr.write(`${BITFAB_PROGRESS_PREFIX}${JSON.stringify(progress)}
2086
- `);
2099
+ stderr.write(
2100
+ `${BITFAB_PROGRESS_PREFIX}${JSON.stringify(progress, replayJsonReplacer)}
2101
+ `
2102
+ );
2087
2103
  } catch {
2088
2104
  }
2089
2105
  }
2106
+ function errorMessage(error) {
2107
+ return error instanceof Error ? error.message : String(error);
2108
+ }
2109
+ function replayItemErrorMessage(error) {
2110
+ if (error instanceof DbBranchReplayError) {
2111
+ 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.`;
2112
+ }
2113
+ return errorMessage(error);
2114
+ }
2115
+ function replayJsonReplacer(_key, value) {
2116
+ if (value instanceof Error) {
2117
+ const serialized = {
2118
+ name: value.name,
2119
+ message: value.message,
2120
+ stack: value.stack
2121
+ };
2122
+ if (value instanceof DbBranchReplayError) {
2123
+ serialized.code = value.code;
2124
+ serialized.originalTraceId = value.originalTraceId;
2125
+ if (value.cause !== void 0) {
2126
+ serialized.cause = value.cause;
2127
+ }
2128
+ }
2129
+ return serialized;
2130
+ }
2131
+ return value;
2132
+ }
2133
+ function serializeReplayResult(result) {
2134
+ return JSON.stringify(result, replayJsonReplacer, 2);
2135
+ }
2136
+ async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
2137
+ try {
2138
+ return await operation();
2139
+ } catch (cause) {
2140
+ if (cause instanceof ReplayError) {
2141
+ throw cause;
2142
+ }
2143
+ throw new ReplayError(
2144
+ errorMessage(cause),
2145
+ items,
2146
+ testRunId,
2147
+ testRunUrl,
2148
+ cause
2149
+ );
2150
+ }
2151
+ }
2090
2152
  function deserializeInputs(spanData) {
2091
2153
  const inputMeta = spanData.input_meta;
2092
2154
  const rawInput = spanData.input;
@@ -2144,25 +2206,41 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2144
2206
  let originalOutput;
2145
2207
  let result;
2146
2208
  let error = null;
2209
+ let traceError = null;
2210
+ let replayError = null;
2147
2211
  const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
2148
2212
  const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
2149
2213
  try {
2150
2214
  if (includeDbBranchLease && !lease && !leaseError) {
2151
- const resolved = await httpClient.resolveDbBranchLease(
2152
- testRunId,
2153
- originalTraceId,
2154
- dbBranchSettings
2155
- );
2215
+ let resolved;
2216
+ try {
2217
+ resolved = await httpClient.resolveDbBranchLease(
2218
+ testRunId,
2219
+ originalTraceId,
2220
+ dbBranchSettings
2221
+ );
2222
+ } catch (cause) {
2223
+ throw new DbBranchReplayError(
2224
+ "lease_request_failed",
2225
+ `Bitfab could not request the database branch: ${errorMessage(cause)}`,
2226
+ originalTraceId,
2227
+ cause
2228
+ );
2229
+ }
2156
2230
  lease = resolved.lease ?? void 0;
2157
2231
  leaseError = resolved.leaseError ?? void 0;
2158
2232
  dbSnapshotRef = resolved.dbSnapshotRef ?? dbSnapshotRef;
2159
2233
  }
2160
2234
  if (leaseError) {
2161
- throw new BitfabError(
2162
- `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.`
2235
+ throw new DbBranchReplayError(
2236
+ leaseError.code,
2237
+ leaseError.message,
2238
+ originalTraceId
2163
2239
  );
2164
2240
  }
2165
- const span = await httpClient.getExternalSpan(originalSpanId);
2241
+ const span = await httpClient.getExternalSpan(originalSpanId, {
2242
+ view: "replay"
2243
+ });
2166
2244
  const spanData = span.rawData?.span_data ?? {};
2167
2245
  inputs = deserializeInputs(spanData);
2168
2246
  originalOutput = deserializeOutput(spanData);
@@ -2182,7 +2260,8 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2182
2260
  if (needTree) {
2183
2261
  try {
2184
2262
  const treeResponse = await httpClient.getSpanTree(originalSpanId, {
2185
- includeOutputs
2263
+ includeOutputs,
2264
+ includeRootOutput: false
2186
2265
  });
2187
2266
  if (treeResponse.root) {
2188
2267
  mockTree = buildMockTree(treeResponse.root);
@@ -2204,7 +2283,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2204
2283
  const fetchSpanOutput = mockTree && !includeOutputs ? (externalSpanId) => {
2205
2284
  let pending = outputCache.get(externalSpanId);
2206
2285
  if (!pending) {
2207
- pending = httpClient.getExternalSpan(externalSpanId).then(
2286
+ pending = httpClient.getExternalSpan(externalSpanId, { view: "replay" }).then(
2208
2287
  (s) => deserializeOutput(
2209
2288
  s.rawData?.span_data ?? {}
2210
2289
  )
@@ -2213,25 +2292,31 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2213
2292
  }
2214
2293
  return pending;
2215
2294
  } : void 0;
2216
- const maybePromise = runWithReplayContext(
2217
- {
2218
- testRunId,
2219
- traceId: replayedTraceId,
2220
- inputSourceSpanId: span.id,
2221
- inputSourceTraceId: span.externalTraceId,
2222
- sourceBitfabTraceId: originalTraceId,
2223
- mockTree,
2224
- callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
2225
- mockStrategy,
2226
- mockOverrides: hasOverrides ? resolvedOverrides : void 0,
2227
- fetchSpanOutput,
2228
- dbBranchLease: lease
2229
- },
2230
- () => fn(...inputs)
2231
- );
2232
- result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
2295
+ try {
2296
+ const maybePromise = runWithReplayContext(
2297
+ {
2298
+ testRunId,
2299
+ traceId: replayedTraceId,
2300
+ inputSourceSpanId: span.id,
2301
+ inputSourceTraceId: span.externalTraceId,
2302
+ sourceBitfabTraceId: originalTraceId,
2303
+ mockTree,
2304
+ callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
2305
+ mockStrategy,
2306
+ mockOverrides: hasOverrides ? resolvedOverrides : void 0,
2307
+ fetchSpanOutput,
2308
+ dbBranchLease: lease
2309
+ },
2310
+ () => fn(...inputs)
2311
+ );
2312
+ result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
2313
+ } catch (e) {
2314
+ traceError = e;
2315
+ error = errorMessage(e);
2316
+ }
2233
2317
  } catch (e) {
2234
- error = e instanceof Error ? e.message : String(e);
2318
+ replayError = e;
2319
+ error = replayItemErrorMessage(e);
2235
2320
  } finally {
2236
2321
  if (lease) {
2237
2322
  try {
@@ -2260,6 +2345,8 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2260
2345
  result,
2261
2346
  originalOutput,
2262
2347
  error,
2348
+ traceError,
2349
+ replayError,
2263
2350
  durationMs: serverItem.durationMs ?? null,
2264
2351
  // Filled in by replay() from the complete-replay response once the
2265
2352
  // replay traces are persisted and their spans aggregated server-side.
@@ -2384,6 +2471,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2384
2471
  );
2385
2472
  const mockStrategy = options?.mock ?? "marked";
2386
2473
  const maxConcurrency = options?.maxConcurrency ?? 10;
2474
+ const fullTestRunUrl = `${serviceUrl}${testRunUrl}`;
2387
2475
  const resolvedOverrides = [
2388
2476
  ...normalizeMockOverrides(options?.mockOverride),
2389
2477
  ...registeredOverrides
@@ -2440,6 +2528,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2440
2528
  result: item.result,
2441
2529
  originalOutput: item.originalOutput,
2442
2530
  error: item.error,
2531
+ traceError: item.traceError,
2532
+ replayError: item.replayError,
2443
2533
  durationMs: item.durationMs,
2444
2534
  tokens: item.tokens,
2445
2535
  model: item.model,
@@ -2450,8 +2540,18 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2450
2540
  }
2451
2541
  } : void 0
2452
2542
  );
2453
- await waitForReplayPersistence(httpClient, testRunId, replayedTraceIds);
2454
- const completeResult = await httpClient.completeReplay(testRunId);
2543
+ await preserveReplayFailure(
2544
+ () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),
2545
+ resultItems,
2546
+ testRunId,
2547
+ fullTestRunUrl
2548
+ );
2549
+ const completeResult = await preserveReplayFailure(
2550
+ () => httpClient.completeReplay(testRunId),
2551
+ resultItems,
2552
+ testRunId,
2553
+ fullTestRunUrl
2554
+ );
2455
2555
  const serverTraceIds = completeResult.traceIds;
2456
2556
  const replayTokens = completeResult.tokens;
2457
2557
  if (serverTraceIds !== void 0) {
@@ -2474,9 +2574,16 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2474
2574
  }
2475
2575
  if (completedCount > 0 && missing.length === completedCount) {
2476
2576
  const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
2477
- throw new BitfabError(
2577
+ const cause = new BitfabError(
2478
2578
  `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.`
2479
2579
  );
2580
+ throw new ReplayError(
2581
+ cause.message,
2582
+ resultItems,
2583
+ testRunId,
2584
+ fullTestRunUrl,
2585
+ cause
2586
+ );
2480
2587
  }
2481
2588
  if (missing.length > 0) {
2482
2589
  try {
@@ -2490,7 +2597,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2490
2597
  const result = {
2491
2598
  items: resultItems,
2492
2599
  testRunId,
2493
- testRunUrl: `${serviceUrl}${testRunUrl}`
2600
+ testRunUrl: fullTestRunUrl
2494
2601
  };
2495
2602
  await writeReplayResultFile(result);
2496
2603
  try {
@@ -2518,7 +2625,7 @@ async function writeReplayResultFile(result) {
2518
2625
  import("fs/promises")
2519
2626
  ]);
2520
2627
  await mkdir(dirname(resultPath), { recursive: true });
2521
- await writeFile(resultPath, `${JSON.stringify(result, null, 2)}
2628
+ await writeFile(resultPath, `${serializeReplayResult(result)}
2522
2629
  `);
2523
2630
  } catch (err) {
2524
2631
  try {
@@ -2529,7 +2636,7 @@ async function writeReplayResultFile(result) {
2529
2636
  }
2530
2637
  }
2531
2638
  }
2532
- var REPLAY_PERSISTENCE_TIMEOUT_MS, BITFAB_PROGRESS_PREFIX;
2639
+ var REPLAY_PERSISTENCE_TIMEOUT_MS, BITFAB_PROGRESS_PREFIX, ReplayError, DbBranchReplayError;
2533
2640
  var init_replay = __esm({
2534
2641
  "src/replay.ts"() {
2535
2642
  "use strict";
@@ -2544,6 +2651,25 @@ var init_replay = __esm({
2544
2651
  init_unrefTimer();
2545
2652
  REPLAY_PERSISTENCE_TIMEOUT_MS = 3e4;
2546
2653
  BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
2654
+ ReplayError = class extends BitfabError {
2655
+ constructor(message, items, testRunId, testRunUrl, cause) {
2656
+ super(message, testRunUrl);
2657
+ this.items = items;
2658
+ this.testRunId = testRunId;
2659
+ this.testRunUrl = testRunUrl;
2660
+ this.cause = cause;
2661
+ this.name = "ReplayError";
2662
+ }
2663
+ };
2664
+ DbBranchReplayError = class extends BitfabError {
2665
+ constructor(code, message, originalTraceId, cause) {
2666
+ super(message);
2667
+ this.code = code;
2668
+ this.originalTraceId = originalTraceId;
2669
+ this.cause = cause;
2670
+ this.name = "DbBranchReplayError";
2671
+ }
2672
+ };
2547
2673
  }
2548
2674
  });
2549
2675
 
@@ -2561,7 +2687,9 @@ __export(node_exports, {
2561
2687
  BitfabOpenAITracingProcessor: () => BitfabOpenAITracingProcessor,
2562
2688
  BitfabVercelAiHandler: () => BitfabVercelAiHandler,
2563
2689
  DEFAULT_SERVICE_URL: () => DEFAULT_SERVICE_URL,
2690
+ DbBranchReplayError: () => DbBranchReplayError,
2564
2691
  HttpClient: () => HttpClient,
2692
+ ReplayError: () => ReplayError,
2565
2693
  SUPPORTED_PROVIDERS: () => SUPPORTED_PROVIDERS,
2566
2694
  __version__: () => __version__,
2567
2695
  finalizers: () => finalizers,
@@ -2569,7 +2697,8 @@ __export(node_exports, {
2569
2697
  getCurrentReplayBranch: () => getCurrentReplayBranch,
2570
2698
  getCurrentSpan: () => getCurrentSpan,
2571
2699
  getCurrentTrace: () => getCurrentTrace,
2572
- reportReplayProgress: () => reportReplayProgress
2700
+ reportReplayProgress: () => reportReplayProgress,
2701
+ serializeReplayResult: () => serializeReplayResult
2573
2702
  });
2574
2703
  module.exports = __toCommonJS(node_exports);
2575
2704
 
@@ -6161,7 +6290,9 @@ assertAsyncStorageRegistered();
6161
6290
  BitfabOpenAITracingProcessor,
6162
6291
  BitfabVercelAiHandler,
6163
6292
  DEFAULT_SERVICE_URL,
6293
+ DbBranchReplayError,
6164
6294
  HttpClient,
6295
+ ReplayError,
6165
6296
  SUPPORTED_PROVIDERS,
6166
6297
  __version__,
6167
6298
  finalizers,
@@ -6169,6 +6300,7 @@ assertAsyncStorageRegistered();
6169
6300
  getCurrentReplayBranch,
6170
6301
  getCurrentSpan,
6171
6302
  getCurrentTrace,
6172
- reportReplayProgress
6303
+ reportReplayProgress,
6304
+ serializeReplayResult
6173
6305
  });
6174
6306
  //# sourceMappingURL=node.cjs.map