@bitfab/sdk 0.29.0 → 0.29.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.
package/dist/index.d.cts CHANGED
@@ -785,6 +785,21 @@ interface ReplayOptions {
785
785
  }
786
786
  /** Running totals reported to {@link ReplayOptions.onProgress} as replay proceeds. */
787
787
  interface ReplayProgress {
788
+ /**
789
+ * Event kind. Omitted (or `"item"`) for the per-trace settle events streamed
790
+ * during the run. `"complete"` marks the single terminal event emitted once
791
+ * the run has settled and been enriched server-side; it carries the full
792
+ * {@link ReplayProgress.result} and has no `item`. The Bitfab plugin reads
793
+ * that terminal event to build the run's final result without parsing stdout.
794
+ */
795
+ type?: "item" | "complete";
796
+ /**
797
+ * The full {@link ReplayResult}, present only on the terminal `"complete"`
798
+ * event. Lets the plugin ingest the enriched result (server-aggregated tokens,
799
+ * server trace ids) over the same channel as progress, so a dependency logging
800
+ * to stdout can never block it.
801
+ */
802
+ result?: ReplayResult<unknown>;
788
803
  /** Test run ID created for this replay. */
789
804
  testRunId?: string;
790
805
  /** Items that have finished so far, whether they succeeded or errored. */
@@ -796,18 +811,25 @@ interface ReplayProgress {
796
811
  /** Of the completed items, how many threw (their `item.error` is set). */
797
812
  errored: number;
798
813
  /**
799
- * The single item that just settled to produce this event. `traceId` is the
800
- * source (historical) trace that was replayed (so a UI can identify or link
801
- * it); `error` is its replay error, or null when it ran ok; `durationMs` is how
802
- * long this one trace took to replay. Lets a progress UI show per-trace
803
- * pass/fail and timing as the run streams, without waiting for the full
804
- * {@link ReplayResult}.
814
+ * The single item that just settled to produce this event. `traceId` is null
815
+ * at this stage (the server replay id isn't known until the run completes);
816
+ * `originalTraceId` is the original (historical) trace that was replayed (so
817
+ * a UI can identify or link it); `error` is its replay error, or null when it
818
+ * ran ok; `durationMs` is how long this one trace took to replay. Lets a
819
+ * progress UI show per-trace pass/fail and timing as the run streams, without
820
+ * waiting for the full {@link ReplayResult}.
805
821
  */
806
822
  item?: {
807
- /** Source (historical) trace ID being replayed. */
808
- traceId: string | null;
809
- /** Local SDK replay trace ID, before the server maps it to a row ID. */
810
- replayTraceId?: string | null;
823
+ /** Trace ID of the new replay trace (null during the run; the server id arrives at completion). */
824
+ traceId?: string | null;
825
+ /** Bitfab trace ID of the original (historical) trace being replayed. */
826
+ originalTraceId: string | null;
827
+ /** External span ID the recorded inputs were read from (the original root span). */
828
+ originalSpanId?: string | null;
829
+ /** @deprecated alias for `originalTraceId`. */
830
+ sourceTraceId: string | null;
831
+ /** @deprecated alias for `originalSpanId`. */
832
+ sourceSpanId?: string | null;
811
833
  /** Deserialized inputs from the original trace. */
812
834
  input?: unknown[];
813
835
  /** The result returned by the replayed function, or undefined on error. */
@@ -847,9 +869,13 @@ declare const BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
847
869
  declare function reportReplayProgress(progress: ReplayProgress): void;
848
870
  /** Per-trace context passed to {@link ReplayOptions.adaptInputs}. */
849
871
  interface AdaptContext {
850
- /** Bitfab trace ID of the historical trace being replayed. */
851
- traceId: string;
872
+ /** Bitfab trace ID of the original (historical) trace being replayed. */
873
+ originalTraceId: string;
852
874
  /** External span ID the recorded inputs were read from. */
875
+ originalSpanId: string;
876
+ /** @deprecated alias for {@link AdaptContext.originalTraceId}. */
877
+ sourceTraceId: string;
878
+ /** @deprecated alias for {@link AdaptContext.originalSpanId}. */
853
879
  sourceSpanId: string;
854
880
  }
855
881
  /**
@@ -865,8 +891,23 @@ interface AdaptContext {
865
891
  */
866
892
  type AdaptInputsFn = (inputs: unknown[], ctx: AdaptContext) => unknown[];
867
893
  interface ReplayItem<T> {
868
- /** Trace ID of the new trace created during replay. */
894
+ /**
895
+ * Server trace ID of the new replay trace this item produced. Written in by
896
+ * `replay()` from the complete-replay response once the server has minted the
897
+ * trace row; the client-side id used to correlate spans during the run is
898
+ * never surfaced here. Null until completion, on older servers that omit the
899
+ * mapping, or if the item produced no trace. Not the verdict-persistence key:
900
+ * that is the original-trace lineage (`originalTraceId` + `testRunId`).
901
+ */
869
902
  traceId: string | null;
903
+ /** Bitfab trace ID of the original (historical) trace being replayed. */
904
+ originalTraceId: string;
905
+ /** External span ID the recorded inputs were read from (the original root span). */
906
+ originalSpanId: string;
907
+ /** @deprecated alias for {@link ReplayItem.originalTraceId}. */
908
+ sourceTraceId: string;
909
+ /** @deprecated alias for {@link ReplayItem.originalSpanId}. */
910
+ sourceSpanId: string;
870
911
  /** Deserialized inputs from the original trace. */
871
912
  input: unknown[];
872
913
  /** The result returned by the function during replay, or undefined on error. */
@@ -1758,7 +1799,7 @@ declare class BitfabFunction {
1758
1799
  /**
1759
1800
  * SDK version from package.json (injected at build time)
1760
1801
  */
1761
- declare const __version__ = "0.29.0";
1802
+ declare const __version__ = "0.29.1";
1762
1803
 
1763
1804
  /**
1764
1805
  * Constants for the Bitfab SDK.
package/dist/index.d.ts CHANGED
@@ -785,6 +785,21 @@ interface ReplayOptions {
785
785
  }
786
786
  /** Running totals reported to {@link ReplayOptions.onProgress} as replay proceeds. */
787
787
  interface ReplayProgress {
788
+ /**
789
+ * Event kind. Omitted (or `"item"`) for the per-trace settle events streamed
790
+ * during the run. `"complete"` marks the single terminal event emitted once
791
+ * the run has settled and been enriched server-side; it carries the full
792
+ * {@link ReplayProgress.result} and has no `item`. The Bitfab plugin reads
793
+ * that terminal event to build the run's final result without parsing stdout.
794
+ */
795
+ type?: "item" | "complete";
796
+ /**
797
+ * The full {@link ReplayResult}, present only on the terminal `"complete"`
798
+ * event. Lets the plugin ingest the enriched result (server-aggregated tokens,
799
+ * server trace ids) over the same channel as progress, so a dependency logging
800
+ * to stdout can never block it.
801
+ */
802
+ result?: ReplayResult<unknown>;
788
803
  /** Test run ID created for this replay. */
789
804
  testRunId?: string;
790
805
  /** Items that have finished so far, whether they succeeded or errored. */
@@ -796,18 +811,25 @@ interface ReplayProgress {
796
811
  /** Of the completed items, how many threw (their `item.error` is set). */
797
812
  errored: number;
798
813
  /**
799
- * The single item that just settled to produce this event. `traceId` is the
800
- * source (historical) trace that was replayed (so a UI can identify or link
801
- * it); `error` is its replay error, or null when it ran ok; `durationMs` is how
802
- * long this one trace took to replay. Lets a progress UI show per-trace
803
- * pass/fail and timing as the run streams, without waiting for the full
804
- * {@link ReplayResult}.
814
+ * The single item that just settled to produce this event. `traceId` is null
815
+ * at this stage (the server replay id isn't known until the run completes);
816
+ * `originalTraceId` is the original (historical) trace that was replayed (so
817
+ * a UI can identify or link it); `error` is its replay error, or null when it
818
+ * ran ok; `durationMs` is how long this one trace took to replay. Lets a
819
+ * progress UI show per-trace pass/fail and timing as the run streams, without
820
+ * waiting for the full {@link ReplayResult}.
805
821
  */
806
822
  item?: {
807
- /** Source (historical) trace ID being replayed. */
808
- traceId: string | null;
809
- /** Local SDK replay trace ID, before the server maps it to a row ID. */
810
- replayTraceId?: string | null;
823
+ /** Trace ID of the new replay trace (null during the run; the server id arrives at completion). */
824
+ traceId?: string | null;
825
+ /** Bitfab trace ID of the original (historical) trace being replayed. */
826
+ originalTraceId: string | null;
827
+ /** External span ID the recorded inputs were read from (the original root span). */
828
+ originalSpanId?: string | null;
829
+ /** @deprecated alias for `originalTraceId`. */
830
+ sourceTraceId: string | null;
831
+ /** @deprecated alias for `originalSpanId`. */
832
+ sourceSpanId?: string | null;
811
833
  /** Deserialized inputs from the original trace. */
812
834
  input?: unknown[];
813
835
  /** The result returned by the replayed function, or undefined on error. */
@@ -847,9 +869,13 @@ declare const BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
847
869
  declare function reportReplayProgress(progress: ReplayProgress): void;
848
870
  /** Per-trace context passed to {@link ReplayOptions.adaptInputs}. */
849
871
  interface AdaptContext {
850
- /** Bitfab trace ID of the historical trace being replayed. */
851
- traceId: string;
872
+ /** Bitfab trace ID of the original (historical) trace being replayed. */
873
+ originalTraceId: string;
852
874
  /** External span ID the recorded inputs were read from. */
875
+ originalSpanId: string;
876
+ /** @deprecated alias for {@link AdaptContext.originalTraceId}. */
877
+ sourceTraceId: string;
878
+ /** @deprecated alias for {@link AdaptContext.originalSpanId}. */
853
879
  sourceSpanId: string;
854
880
  }
855
881
  /**
@@ -865,8 +891,23 @@ interface AdaptContext {
865
891
  */
866
892
  type AdaptInputsFn = (inputs: unknown[], ctx: AdaptContext) => unknown[];
867
893
  interface ReplayItem<T> {
868
- /** Trace ID of the new trace created during replay. */
894
+ /**
895
+ * Server trace ID of the new replay trace this item produced. Written in by
896
+ * `replay()` from the complete-replay response once the server has minted the
897
+ * trace row; the client-side id used to correlate spans during the run is
898
+ * never surfaced here. Null until completion, on older servers that omit the
899
+ * mapping, or if the item produced no trace. Not the verdict-persistence key:
900
+ * that is the original-trace lineage (`originalTraceId` + `testRunId`).
901
+ */
869
902
  traceId: string | null;
903
+ /** Bitfab trace ID of the original (historical) trace being replayed. */
904
+ originalTraceId: string;
905
+ /** External span ID the recorded inputs were read from (the original root span). */
906
+ originalSpanId: string;
907
+ /** @deprecated alias for {@link ReplayItem.originalTraceId}. */
908
+ sourceTraceId: string;
909
+ /** @deprecated alias for {@link ReplayItem.originalSpanId}. */
910
+ sourceSpanId: string;
870
911
  /** Deserialized inputs from the original trace. */
871
912
  input: unknown[];
872
913
  /** The result returned by the function during replay, or undefined on error. */
@@ -1758,7 +1799,7 @@ declare class BitfabFunction {
1758
1799
  /**
1759
1800
  * SDK version from package.json (injected at build time)
1760
1801
  */
1761
- declare const __version__ = "0.29.0";
1802
+ declare const __version__ = "0.29.1";
1762
1803
 
1763
1804
  /**
1764
1805
  * Constants for the Bitfab SDK.
package/dist/index.js CHANGED
@@ -14,12 +14,12 @@ import {
14
14
  flushTraces,
15
15
  getCurrentSpan,
16
16
  getCurrentTrace
17
- } from "./chunk-2M5AWVVQ.js";
17
+ } from "./chunk-RTPEBWVO.js";
18
18
  import {
19
19
  BITFAB_PROGRESS_PREFIX,
20
20
  BitfabError,
21
21
  reportReplayProgress
22
- } from "./chunk-V3XORTWI.js";
22
+ } from "./chunk-YZU6WFG2.js";
23
23
  export {
24
24
  BITFAB_PROGRESS_PREFIX,
25
25
  Bitfab,
package/dist/node.cjs CHANGED
@@ -394,23 +394,27 @@ function buildMockTree(rootNode) {
394
394
  }
395
395
  return { spans };
396
396
  }
397
- async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, environment, adaptInputs) {
397
+ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, environment, adaptInputs) {
398
398
  const lease = environment ? serverItem.dbBranchLease : void 0;
399
399
  let inputs = [];
400
400
  let originalOutput;
401
401
  let result;
402
402
  let error = null;
403
- const replayedTraceId = randomUuid();
404
403
  const pendingPersistence = [];
404
+ const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
405
+ const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
405
406
  try {
406
- const span = await httpClient.getExternalSpan(serverItem.externalSpanId);
407
+ const span = await httpClient.getExternalSpan(originalSpanId);
407
408
  const spanData = span.rawData?.span_data ?? {};
408
409
  inputs = deserializeInputs(spanData);
409
410
  originalOutput = deserializeOutput(spanData);
410
411
  if (adaptInputs) {
411
412
  inputs = adaptInputs(inputs, {
412
- traceId: serverItem.traceId,
413
- sourceSpanId: serverItem.externalSpanId
413
+ originalTraceId,
414
+ originalSpanId,
415
+ // Deprecated aliases for originalTraceId/originalSpanId.
416
+ sourceTraceId: originalTraceId,
417
+ sourceSpanId: originalSpanId
414
418
  });
415
419
  }
416
420
  const hasOverrides = resolvedOverrides.length > 0;
@@ -419,15 +423,14 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
419
423
  let mockTree;
420
424
  if (needTree) {
421
425
  try {
422
- const treeResponse = await httpClient.getSpanTree(
423
- serverItem.externalSpanId,
424
- { includeOutputs }
425
- );
426
+ const treeResponse = await httpClient.getSpanTree(originalSpanId, {
427
+ includeOutputs
428
+ });
426
429
  if (treeResponse.root) {
427
430
  mockTree = buildMockTree(treeResponse.root);
428
431
  } else if (mockStrategy === "all" || hasOverrides) {
429
432
  throw new BitfabError(
430
- `Replay mock strategy "${mockStrategy}"${hasOverrides ? " with overrides" : ""} requires a span tree root for source span ${serverItem.externalSpanId}.`
433
+ `Replay mock strategy "${mockStrategy}"${hasOverrides ? " with overrides" : ""} requires a span tree root for original span ${originalSpanId}.`
431
434
  );
432
435
  } else {
433
436
  mockTree = void 0;
@@ -458,7 +461,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
458
461
  traceId: replayedTraceId,
459
462
  inputSourceSpanId: span.id,
460
463
  inputSourceTraceId: span.externalTraceId,
461
- sourceBitfabTraceId: serverItem.traceId,
464
+ sourceBitfabTraceId: originalTraceId,
462
465
  mockTree,
463
466
  callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
464
467
  mockStrategy,
@@ -488,7 +491,15 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
488
491
  }
489
492
  }
490
493
  return {
491
- traceId: replayedTraceId,
494
+ // Written in by replay() from the complete-replay response once the server
495
+ // has minted this replay trace's row. Null until then: the client-side
496
+ // correlation id (replayedTraceId) is never surfaced as the item's traceId.
497
+ traceId: null,
498
+ originalTraceId,
499
+ originalSpanId,
500
+ // Deprecated aliases for originalTraceId/originalSpanId.
501
+ sourceTraceId: originalTraceId,
502
+ sourceSpanId: originalSpanId,
492
503
  input: inputs,
493
504
  result,
494
505
  originalOutput,
@@ -564,14 +575,16 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
564
575
  ...normalizeMockOverrides(options?.mockOverride),
565
576
  ...registeredOverrides
566
577
  ];
578
+ const replayedTraceIds = serverItems.map(() => randomUuid());
567
579
  const tasks = serverItems.map(
568
- (serverItem) => () => processItem(
580
+ (serverItem, index) => () => processItem(
569
581
  httpClient,
570
582
  serverItem,
571
583
  fn,
572
584
  testRunId,
573
585
  mockStrategy,
574
586
  resolvedOverrides,
587
+ replayedTraceIds[index],
575
588
  options?.environment,
576
589
  options?.adaptInputs
577
590
  )
@@ -598,11 +611,17 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
598
611
  succeeded,
599
612
  errored,
600
613
  item: {
601
- // Source (historical) trace id, so a UI can identify the trace
602
- // that just settled. The item's own traceId is the new replay
603
- // trace and is assigned later (below), so use the server item.
604
- traceId: serverItems[index]?.traceId ?? null,
605
- replayTraceId: item.traceId,
614
+ // The server replay trace id isn't known until completeReplay
615
+ // runs (below), so it can't be reported mid-run and we never
616
+ // emit the client-side placeholder. originalTraceId (the
617
+ // historical trace) is known now and is what a UI keys on to
618
+ // identify what just settled.
619
+ traceId: null,
620
+ originalTraceId: item.originalTraceId ?? null,
621
+ originalSpanId: item.originalSpanId ?? null,
622
+ // Deprecated aliases for originalTraceId/originalSpanId.
623
+ sourceTraceId: item.originalTraceId ?? null,
624
+ sourceSpanId: item.originalSpanId ?? null,
606
625
  input: item.input,
607
626
  result: item.result,
608
627
  originalOutput: item.originalOutput,
@@ -620,56 +639,58 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
620
639
  const completeResult = await httpClient.completeReplay(testRunId);
621
640
  const serverTraceIds = completeResult.traceIds;
622
641
  const replayTokens = completeResult.tokens;
623
- if (serverTraceIds === void 0) {
624
- try {
625
- console.warn(
626
- "Bitfab: server did not return replay trace IDs; item.traceId will be null (server upgrade required for verdict persistence)"
627
- );
628
- } catch {
629
- }
630
- for (const item of resultItems) {
631
- item.traceId = null;
632
- }
633
- } else {
642
+ if (serverTraceIds !== void 0) {
634
643
  const missing = [];
635
644
  let completedCount = 0;
636
- for (const item of resultItems) {
637
- if (item.traceId) {
638
- const mapped = serverTraceIds[item.traceId];
639
- if (item.error === null) {
640
- completedCount += 1;
641
- if (mapped === void 0) {
642
- missing.push(item.traceId);
643
- }
644
- }
645
- if (mapped !== void 0) {
646
- item.tokens = replayTokens?.[mapped] ?? null;
645
+ for (let index = 0; index < resultItems.length; index += 1) {
646
+ const item = resultItems[index];
647
+ const localId = replayedTraceIds[index];
648
+ const mapped = localId ? serverTraceIds[localId] : void 0;
649
+ item.traceId = mapped ?? null;
650
+ if (item.error === null) {
651
+ completedCount += 1;
652
+ if (mapped === void 0) {
653
+ missing.push(localId ?? item.originalTraceId);
647
654
  }
648
- item.traceId = mapped ?? null;
655
+ }
656
+ if (mapped !== void 0) {
657
+ item.tokens = replayTokens?.[mapped] ?? null;
649
658
  }
650
659
  }
651
- if (missing.length > 0) {
660
+ if (completedCount > 0 && missing.length === completedCount) {
652
661
  const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
653
- if (missing.length === completedCount) {
654
- throw new BitfabError(
655
- `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.`
656
- );
657
- }
662
+ throw new BitfabError(
663
+ `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.`
664
+ );
665
+ }
666
+ if (missing.length > 0) {
658
667
  try {
659
668
  console.error(
660
- `Bitfab: server has no persisted trace for ${missing.length} of ${completedCount} completed replay item(s) (testRunId ${testRunId}).${serverCount} Their traceId is null and verdicts cannot be persisted for them. Missing: ${missing.join(", ")}`
669
+ `Bitfab: server has no persisted trace for ${missing.length} of ${completedCount} completed replay item(s) (testRunId ${testRunId}). Their replay token usage is unavailable and they cannot be labeled.`
661
670
  );
662
671
  } catch {
663
672
  }
664
673
  }
665
674
  }
666
- const replayResult = {
675
+ const result = {
667
676
  items: resultItems,
668
677
  testRunId,
669
678
  testRunUrl: `${serviceUrl}${testRunUrl}`
670
679
  };
671
- await writeReplayResultFile(replayResult);
672
- return replayResult;
680
+ await writeReplayResultFile(result);
681
+ try {
682
+ options?.onProgress?.({
683
+ type: "complete",
684
+ testRunId,
685
+ completed: total,
686
+ total,
687
+ succeeded,
688
+ errored,
689
+ result
690
+ });
691
+ } catch {
692
+ }
693
+ return result;
673
694
  }
674
695
  async function writeReplayResultFile(result) {
675
696
  const resultPath = typeof process !== "undefined" ? process.env?.BITFAB_REPLAY_RESULT_PATH : void 0;
@@ -739,7 +760,7 @@ registerAsyncLocalStorageClass(
739
760
  );
740
761
 
741
762
  // src/version.generated.ts
742
- var __version__ = "0.29.0";
763
+ var __version__ = "0.29.1";
743
764
 
744
765
  // src/constants.ts
745
766
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
@@ -4169,7 +4190,7 @@ var Bitfab = class {
4169
4190
  dbSnapshotUsage: {
4170
4191
  neonBranchId: replayCtx.dbBranchLease.neonBranchId,
4171
4192
  snapshotTimestamp: replayCtx.dbBranchLease.snapshotTimestamp,
4172
- sourceTraceId: replayCtx.sourceBitfabTraceId,
4193
+ originalTraceId: replayCtx.sourceBitfabTraceId,
4173
4194
  accessed: replayCtx.dbSnapshotAccessed === true
4174
4195
  }
4175
4196
  }
@@ -4473,8 +4494,11 @@ var Bitfab = class {
4473
4494
  ...params.dbSnapshotUsage.snapshotTimestamp && {
4474
4495
  snapshot_timestamp: params.dbSnapshotUsage.snapshotTimestamp
4475
4496
  },
4476
- ...params.dbSnapshotUsage.sourceTraceId && {
4477
- source_trace_id: params.dbSnapshotUsage.sourceTraceId
4497
+ ...params.dbSnapshotUsage.originalTraceId && {
4498
+ original_trace_id: params.dbSnapshotUsage.originalTraceId,
4499
+ // Deprecated wire alias, kept so this SDK still reports usage
4500
+ // against servers that predate the rename.
4501
+ source_trace_id: params.dbSnapshotUsage.originalTraceId
4478
4502
  },
4479
4503
  accessed: params.dbSnapshotUsage.accessed
4480
4504
  };