@bitfab/sdk 0.36.2 → 0.36.4

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.
@@ -16,7 +16,7 @@ import {
16
16
  toJsonSafe,
17
17
  toJsonSafeReport,
18
18
  warnOnce
19
- } from "./chunk-4IY5SZYR.js";
19
+ } from "./chunk-6AXY24UL.js";
20
20
 
21
21
  // src/processorPayload.ts
22
22
  var SERIALIZATION_DEGRADED_STEP = "serialization_degraded";
@@ -3337,7 +3337,7 @@ var Bitfab = class {
3337
3337
  `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.`
3338
3338
  );
3339
3339
  }
3340
- const { replay: doReplay } = await import("./replay-UGABWDPP.js");
3340
+ const { replay: doReplay } = await import("./replay-EBDSNUIO.js");
3341
3341
  return doReplay(
3342
3342
  this.httpClient,
3343
3343
  this.serviceUrl,
@@ -3554,4 +3554,4 @@ export {
3554
3554
  BitfabFunction,
3555
3555
  finalizers
3556
3556
  };
3557
- //# sourceMappingURL=chunk-XZKYSTWH.js.map
3557
+ //# sourceMappingURL=chunk-5NT3VGCB.js.map
@@ -278,7 +278,7 @@ function encodeRequestBody(body) {
278
278
  }
279
279
 
280
280
  // src/version.generated.ts
281
- var __version__ = "0.36.2";
281
+ var __version__ = "0.36.4";
282
282
 
283
283
  // src/constants.ts
284
284
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
@@ -1932,11 +1932,78 @@ function reportReplayProgress(progress) {
1932
1932
  return;
1933
1933
  }
1934
1934
  try {
1935
- stderr.write(`${BITFAB_PROGRESS_PREFIX}${JSON.stringify(progress)}
1936
- `);
1935
+ stderr.write(
1936
+ `${BITFAB_PROGRESS_PREFIX}${JSON.stringify(progress, replayJsonReplacer)}
1937
+ `
1938
+ );
1937
1939
  } catch {
1938
1940
  }
1939
1941
  }
1942
+ var ReplayError = class extends BitfabError {
1943
+ constructor(message, items, testRunId, testRunUrl, cause) {
1944
+ super(message, testRunUrl);
1945
+ this.items = items;
1946
+ this.testRunId = testRunId;
1947
+ this.testRunUrl = testRunUrl;
1948
+ this.cause = cause;
1949
+ this.name = "ReplayError";
1950
+ }
1951
+ };
1952
+ var DbBranchReplayError = class extends BitfabError {
1953
+ constructor(code, message, originalTraceId, cause) {
1954
+ super(message);
1955
+ this.code = code;
1956
+ this.originalTraceId = originalTraceId;
1957
+ this.cause = cause;
1958
+ this.name = "DbBranchReplayError";
1959
+ }
1960
+ };
1961
+ function errorMessage(error) {
1962
+ return error instanceof Error ? error.message : String(error);
1963
+ }
1964
+ function replayItemErrorMessage(error) {
1965
+ if (error instanceof DbBranchReplayError) {
1966
+ 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.`;
1967
+ }
1968
+ return errorMessage(error);
1969
+ }
1970
+ function replayJsonReplacer(_key, value) {
1971
+ if (value instanceof Error) {
1972
+ const serialized = {
1973
+ name: value.name,
1974
+ message: value.message,
1975
+ stack: value.stack
1976
+ };
1977
+ if (value instanceof DbBranchReplayError) {
1978
+ serialized.code = value.code;
1979
+ serialized.originalTraceId = value.originalTraceId;
1980
+ if (value.cause !== void 0) {
1981
+ serialized.cause = value.cause;
1982
+ }
1983
+ }
1984
+ return serialized;
1985
+ }
1986
+ return value;
1987
+ }
1988
+ function serializeReplayResult(result) {
1989
+ return JSON.stringify(result, replayJsonReplacer, 2);
1990
+ }
1991
+ async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
1992
+ try {
1993
+ return await operation();
1994
+ } catch (cause) {
1995
+ if (cause instanceof ReplayError) {
1996
+ throw cause;
1997
+ }
1998
+ throw new ReplayError(
1999
+ errorMessage(cause),
2000
+ items,
2001
+ testRunId,
2002
+ testRunUrl,
2003
+ cause
2004
+ );
2005
+ }
2006
+ }
1940
2007
  function deserializeInputs(spanData) {
1941
2008
  const inputMeta = spanData.input_meta;
1942
2009
  const rawInput = spanData.input;
@@ -1994,22 +2061,36 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
1994
2061
  let originalOutput;
1995
2062
  let result;
1996
2063
  let error = null;
2064
+ let traceError = null;
2065
+ let replayError = null;
1997
2066
  const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
1998
2067
  const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
1999
2068
  try {
2000
2069
  if (includeDbBranchLease && !lease && !leaseError) {
2001
- const resolved = await httpClient.resolveDbBranchLease(
2002
- testRunId,
2003
- originalTraceId,
2004
- dbBranchSettings
2005
- );
2070
+ let resolved;
2071
+ try {
2072
+ resolved = await httpClient.resolveDbBranchLease(
2073
+ testRunId,
2074
+ originalTraceId,
2075
+ dbBranchSettings
2076
+ );
2077
+ } catch (cause) {
2078
+ throw new DbBranchReplayError(
2079
+ "lease_request_failed",
2080
+ `Bitfab could not request the database branch: ${errorMessage(cause)}`,
2081
+ originalTraceId,
2082
+ cause
2083
+ );
2084
+ }
2006
2085
  lease = resolved.lease ?? void 0;
2007
2086
  leaseError = resolved.leaseError ?? void 0;
2008
2087
  dbSnapshotRef = resolved.dbSnapshotRef ?? dbSnapshotRef;
2009
2088
  }
2010
2089
  if (leaseError) {
2011
- throw new BitfabError(
2012
- `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.`
2090
+ throw new DbBranchReplayError(
2091
+ leaseError.code,
2092
+ leaseError.message,
2093
+ originalTraceId
2013
2094
  );
2014
2095
  }
2015
2096
  const span = await httpClient.getExternalSpan(originalSpanId, {
@@ -2066,25 +2147,31 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2066
2147
  }
2067
2148
  return pending;
2068
2149
  } : void 0;
2069
- const maybePromise = runWithReplayContext(
2070
- {
2071
- testRunId,
2072
- traceId: replayedTraceId,
2073
- inputSourceSpanId: span.id,
2074
- inputSourceTraceId: span.externalTraceId,
2075
- sourceBitfabTraceId: originalTraceId,
2076
- mockTree,
2077
- callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
2078
- mockStrategy,
2079
- mockOverrides: hasOverrides ? resolvedOverrides : void 0,
2080
- fetchSpanOutput,
2081
- dbBranchLease: lease
2082
- },
2083
- () => fn(...inputs)
2084
- );
2085
- result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
2150
+ try {
2151
+ const maybePromise = runWithReplayContext(
2152
+ {
2153
+ testRunId,
2154
+ traceId: replayedTraceId,
2155
+ inputSourceSpanId: span.id,
2156
+ inputSourceTraceId: span.externalTraceId,
2157
+ sourceBitfabTraceId: originalTraceId,
2158
+ mockTree,
2159
+ callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
2160
+ mockStrategy,
2161
+ mockOverrides: hasOverrides ? resolvedOverrides : void 0,
2162
+ fetchSpanOutput,
2163
+ dbBranchLease: lease
2164
+ },
2165
+ () => fn(...inputs)
2166
+ );
2167
+ result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
2168
+ } catch (e) {
2169
+ traceError = e;
2170
+ error = errorMessage(e);
2171
+ }
2086
2172
  } catch (e) {
2087
- error = e instanceof Error ? e.message : String(e);
2173
+ replayError = e;
2174
+ error = replayItemErrorMessage(e);
2088
2175
  } finally {
2089
2176
  if (lease) {
2090
2177
  try {
@@ -2113,6 +2200,8 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2113
2200
  result,
2114
2201
  originalOutput,
2115
2202
  error,
2203
+ traceError,
2204
+ replayError,
2116
2205
  durationMs: serverItem.durationMs ?? null,
2117
2206
  // Filled in by replay() from the complete-replay response once the
2118
2207
  // replay traces are persisted and their spans aggregated server-side.
@@ -2237,6 +2326,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2237
2326
  );
2238
2327
  const mockStrategy = options?.mock ?? "marked";
2239
2328
  const maxConcurrency = options?.maxConcurrency ?? 10;
2329
+ const fullTestRunUrl = `${serviceUrl}${testRunUrl}`;
2240
2330
  const resolvedOverrides = [
2241
2331
  ...normalizeMockOverrides(options?.mockOverride),
2242
2332
  ...registeredOverrides
@@ -2293,6 +2383,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2293
2383
  result: item.result,
2294
2384
  originalOutput: item.originalOutput,
2295
2385
  error: item.error,
2386
+ traceError: item.traceError,
2387
+ replayError: item.replayError,
2296
2388
  durationMs: item.durationMs,
2297
2389
  tokens: item.tokens,
2298
2390
  model: item.model,
@@ -2303,8 +2395,18 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2303
2395
  }
2304
2396
  } : void 0
2305
2397
  );
2306
- await waitForReplayPersistence(httpClient, testRunId, replayedTraceIds);
2307
- const completeResult = await httpClient.completeReplay(testRunId);
2398
+ await preserveReplayFailure(
2399
+ () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),
2400
+ resultItems,
2401
+ testRunId,
2402
+ fullTestRunUrl
2403
+ );
2404
+ const completeResult = await preserveReplayFailure(
2405
+ () => httpClient.completeReplay(testRunId),
2406
+ resultItems,
2407
+ testRunId,
2408
+ fullTestRunUrl
2409
+ );
2308
2410
  const serverTraceIds = completeResult.traceIds;
2309
2411
  const replayTokens = completeResult.tokens;
2310
2412
  if (serverTraceIds !== void 0) {
@@ -2327,9 +2429,16 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2327
2429
  }
2328
2430
  if (completedCount > 0 && missing.length === completedCount) {
2329
2431
  const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
2330
- throw new BitfabError(
2432
+ const cause = new BitfabError(
2331
2433
  `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.`
2332
2434
  );
2435
+ throw new ReplayError(
2436
+ cause.message,
2437
+ resultItems,
2438
+ testRunId,
2439
+ fullTestRunUrl,
2440
+ cause
2441
+ );
2333
2442
  }
2334
2443
  if (missing.length > 0) {
2335
2444
  try {
@@ -2343,7 +2452,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2343
2452
  const result = {
2344
2453
  items: resultItems,
2345
2454
  testRunId,
2346
- testRunUrl: `${serviceUrl}${testRunUrl}`
2455
+ testRunUrl: fullTestRunUrl
2347
2456
  };
2348
2457
  await writeReplayResultFile(result);
2349
2458
  try {
@@ -2371,7 +2480,7 @@ async function writeReplayResultFile(result) {
2371
2480
  import("fs/promises")
2372
2481
  ]);
2373
2482
  await mkdir(dirname(resultPath), { recursive: true });
2374
- await writeFile(resultPath, `${JSON.stringify(result, null, 2)}
2483
+ await writeFile(resultPath, `${serializeReplayResult(result)}
2375
2484
  `);
2376
2485
  } catch (err) {
2377
2486
  try {
@@ -2407,6 +2516,9 @@ export {
2407
2516
  resolveMockValue,
2408
2517
  BITFAB_PROGRESS_PREFIX,
2409
2518
  reportReplayProgress,
2519
+ ReplayError,
2520
+ DbBranchReplayError,
2521
+ serializeReplayResult,
2410
2522
  replay
2411
2523
  };
2412
- //# sourceMappingURL=chunk-4IY5SZYR.js.map
2524
+ //# sourceMappingURL=chunk-6AXY24UL.js.map