@bitfab/sdk 0.36.8 → 0.36.10

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-QUX6N7ON.js";
19
+ } from "./chunk-AAMRJGZJ.js";
20
20
 
21
21
  // src/processorPayload.ts
22
22
  var SERIALIZATION_DEGRADED_STEP = "serialization_degraded";
@@ -3371,7 +3371,7 @@ var Bitfab = class {
3371
3371
  `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.`
3372
3372
  );
3373
3373
  }
3374
- const { replay: doReplay } = await import("./replay-7PUB6ZOG.js");
3374
+ const { replay: doReplay } = await import("./replay-WKWBBIGE.js");
3375
3375
  return doReplay(
3376
3376
  this.httpClient,
3377
3377
  this.serviceUrl,
@@ -3609,4 +3609,4 @@ export {
3609
3609
  BitfabFunction,
3610
3610
  finalizers
3611
3611
  };
3612
- //# sourceMappingURL=chunk-B4HK3BPE.js.map
3612
+ //# sourceMappingURL=chunk-RSM2TLKG.js.map
package/dist/index.cjs CHANGED
@@ -42,7 +42,7 @@ var __version__;
42
42
  var init_version_generated = __esm({
43
43
  "src/version.generated.ts"() {
44
44
  "use strict";
45
- __version__ = "0.36.8";
45
+ __version__ = "0.36.10";
46
46
  }
47
47
  });
48
48
 
@@ -1435,6 +1435,10 @@ var init_http = __esm({
1435
1435
  ...error instanceof BitfabError && error.retryAfterMs !== void 0 ? { retryAfterMs: error.retryAfterMs } : {}
1436
1436
  });
1437
1437
  }
1438
+ const serverTraceIds = asPayloadRecord(response?.traceIds);
1439
+ if (serverTraceIds !== void 0) {
1440
+ this.recordServerTraceIds(serverTraceIds);
1441
+ }
1438
1442
  const partialSuccess = asPayloadRecord(response?.partialSuccess);
1439
1443
  const rejected = partialSuccess?.rejectedSpans;
1440
1444
  if (rejected !== void 0 && rejected !== "0" && rejected !== 0) {
@@ -1459,6 +1463,14 @@ var init_http = __esm({
1459
1463
  }
1460
1464
  }
1461
1465
  }
1466
+ /**
1467
+ * The server's assigned `traces.id` for a tracked trace if it has already
1468
+ * been read back off an ingest response, without stopping tracking. Lets a
1469
+ * replay surface the id mid-run for items whose spans already landed.
1470
+ */
1471
+ peekServerTraceId(traceId) {
1472
+ return this.traceDeliveries.get(traceId)?.serverTraceId;
1473
+ }
1462
1474
  /** Whether any tracked trace has had its closing carrier submitted. */
1463
1475
  hasClosedDeliveries(traceIds) {
1464
1476
  return traceIds.some((traceId) => this.traceDeliveries.get(traceId)?.closed);
@@ -1485,7 +1497,8 @@ var init_http = __esm({
1485
1497
  closed: delivery.closed,
1486
1498
  delivered: delivery.closingAcked && [...delivery.submittedSpanIds].every(
1487
1499
  (spanId) => delivery.ackedSpanIds.has(spanId)
1488
- )
1500
+ ),
1501
+ serverTraceId: delivery.serverTraceId
1489
1502
  };
1490
1503
  }
1491
1504
  return reports;
@@ -1514,6 +1527,23 @@ var init_http = __esm({
1514
1527
  * delivered ref is proof its row exists: the same fact the replay status
1515
1528
  * endpoint would report, already in hand.
1516
1529
  */
1530
+ /**
1531
+ * Record the server's assigned `traces.id` for each tracked source trace,
1532
+ * read back from the OTLP ingest response. Keyed by source trace id, the same
1533
+ * key the delivery ledger uses. Untracked ids are ignored.
1534
+ */
1535
+ recordServerTraceIds(map) {
1536
+ for (const [sourceTraceId, serverTraceId] of Object.entries(map)) {
1537
+ if (typeof serverTraceId !== "string") {
1538
+ continue;
1539
+ }
1540
+ const delivery = this.traceDeliveries.get(sourceTraceId);
1541
+ if (delivery === void 0) {
1542
+ continue;
1543
+ }
1544
+ delivery.serverTraceId = serverTraceId;
1545
+ }
1546
+ }
1517
1547
  recordDeliveredCarriers(refs) {
1518
1548
  for (const ref of refs) {
1519
1549
  const delivery = this.traceDeliveries.get(ref.traceId);
@@ -2355,6 +2385,7 @@ __export(replay_exports, {
2355
2385
  replay: () => replay,
2356
2386
  reportReplayProgress: () => reportReplayProgress,
2357
2387
  serializeReplayResult: () => serializeReplayResult,
2388
+ sleepForReplayPersistence: () => sleepForReplayPersistence,
2358
2389
  waitForReplayPersistence: () => waitForReplayPersistence
2359
2390
  });
2360
2391
  function dbBranchEnabled(dbBranch) {
@@ -2650,13 +2681,17 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
2650
2681
  }
2651
2682
  if (!httpClient.hasClosedDeliveries(replayedTraceIds)) {
2652
2683
  httpClient.takeTraceDeliveries(replayedTraceIds);
2653
- return;
2684
+ return {};
2654
2685
  }
2655
2686
  const flushed = await flushTraces(REPLAY_PERSISTENCE_TIMEOUT_MS);
2656
2687
  const deliveries = httpClient.takeTraceDeliveries(replayedTraceIds);
2657
2688
  const expectedSpanCounts = {};
2689
+ const readBackTraceIds = {};
2658
2690
  let allDelivered = true;
2659
2691
  for (const [traceId, delivery] of Object.entries(deliveries)) {
2692
+ if (delivery.serverTraceId !== void 0) {
2693
+ readBackTraceIds[traceId] = delivery.serverTraceId;
2694
+ }
2660
2695
  if (!delivery.closed) {
2661
2696
  continue;
2662
2697
  }
@@ -2664,7 +2699,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
2664
2699
  allDelivered = allDelivered && delivery.delivered;
2665
2700
  }
2666
2701
  if (allDelivered) {
2667
- return;
2702
+ return readBackTraceIds;
2668
2703
  }
2669
2704
  const deadline = Date.now() + REPLAY_PERSISTENCE_TIMEOUT_MS;
2670
2705
  let missing = Object.keys(expectedSpanCounts).length;
@@ -2678,22 +2713,23 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
2678
2713
  (traceId) => ready[traceId] === void 0
2679
2714
  ).length;
2680
2715
  if (missing === 0) {
2681
- return;
2716
+ return readBackTraceIds;
2682
2717
  }
2683
2718
  if (Date.now() >= deadline) {
2684
2719
  break;
2685
2720
  }
2686
- await sleep(Math.min(100, Math.max(0, deadline - Date.now())));
2721
+ await sleepForReplayPersistence(
2722
+ Math.min(100, Math.max(0, deadline - Date.now()))
2723
+ );
2687
2724
  }
2688
2725
  const cause = flushed ? "" : " Delivery was also not confirmed before the flush deadline, so the spans likely never reached the server.";
2689
2726
  throw new BitfabError(
2690
2727
  `Replay traces were not fully persisted before the delivery deadline (testRunId ${testRunId}, missing ${missing} of ${Object.keys(expectedSpanCounts).length} trace(s)).${cause}`
2691
2728
  );
2692
2729
  }
2693
- function sleep(ms) {
2730
+ function sleepForReplayPersistence(ms) {
2694
2731
  return new Promise((resolve) => {
2695
- const timer = setTimeout(resolve, ms);
2696
- unrefTimer(timer);
2732
+ setTimeout(resolve, ms);
2697
2733
  });
2698
2734
  }
2699
2735
  async function mapWithConcurrency2(tasks, maxConcurrency, onSettled, onStarted) {
@@ -2705,7 +2741,7 @@ async function mapWithConcurrency2(tasks, maxConcurrency, onSettled, onStarted)
2705
2741
  onStarted?.(index);
2706
2742
  const result = await tasks[index]();
2707
2743
  results[index] = result;
2708
- onSettled?.(result, index);
2744
+ await onSettled?.(result, index);
2709
2745
  }
2710
2746
  }
2711
2747
  const workers = Array.from(
@@ -2795,10 +2831,36 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2795
2831
  let started = 0;
2796
2832
  let succeeded = 0;
2797
2833
  let errored = 0;
2834
+ let flushInFlight = null;
2835
+ let flushPending = false;
2836
+ const flushFinishedItemTrace = () => {
2837
+ flushPending = true;
2838
+ if (!flushInFlight) {
2839
+ flushInFlight = (async () => {
2840
+ try {
2841
+ while (flushPending) {
2842
+ flushPending = false;
2843
+ await httpClient.settleDeferredWork(REPLAY_PERSISTENCE_TIMEOUT_MS);
2844
+ await flushTraces(REPLAY_PERSISTENCE_TIMEOUT_MS);
2845
+ }
2846
+ } finally {
2847
+ flushInFlight = null;
2848
+ }
2849
+ })();
2850
+ }
2851
+ return flushInFlight;
2852
+ };
2798
2853
  const resultItems = await mapWithConcurrency2(
2799
2854
  tasks,
2800
2855
  maxConcurrency,
2801
- (item) => {
2856
+ async (item, index) => {
2857
+ let serverTraceId = null;
2858
+ try {
2859
+ await flushFinishedItemTrace();
2860
+ serverTraceId = httpClient.peekServerTraceId(replayedTraceIds[index]) ?? null;
2861
+ } catch {
2862
+ }
2863
+ item.traceId = serverTraceId;
2802
2864
  completed += 1;
2803
2865
  if (item.error === null) {
2804
2866
  succeeded += 1;
@@ -2813,11 +2875,12 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2813
2875
  succeeded,
2814
2876
  errored,
2815
2877
  item: {
2816
- // The server replay trace id isn't known until completeReplay runs
2817
- // (below), so it can't be reported mid-run and we never emit the
2818
- // client-side placeholder. originalTraceId (the historical trace)
2819
- // is known now and is what a UI keys on to identify what settled.
2820
- traceId: null,
2878
+ // The server's assigned traces.id, read back off the ingest
2879
+ // response and surfaced as this item finishes. Null only if the
2880
+ // per-item flush could not confirm delivery in time; the end-of-run
2881
+ // barrier then fills the returned ReplayItem. The client-side
2882
+ // placeholder is never surfaced.
2883
+ traceId: serverTraceId,
2821
2884
  originalTraceId: item.originalTraceId ?? null,
2822
2885
  originalSpanId: item.originalSpanId ?? null,
2823
2886
  // Deprecated aliases for originalTraceId/originalSpanId.
@@ -2863,12 +2926,19 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2863
2926
  }
2864
2927
  } : void 0
2865
2928
  );
2866
- await preserveReplayFailure(
2929
+ const deliveredTraceIds = await preserveReplayFailure(
2867
2930
  () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),
2868
2931
  resultItems,
2869
2932
  testRunId,
2870
2933
  fullTestRunUrl
2871
2934
  );
2935
+ for (let index = 0; index < resultItems.length; index += 1) {
2936
+ const localId = replayedTraceIds[index];
2937
+ const readBack = localId ? deliveredTraceIds[localId] : void 0;
2938
+ if (readBack !== void 0) {
2939
+ resultItems[index].traceId = readBack;
2940
+ }
2941
+ }
2872
2942
  const completeResult = await preserveReplayFailure(
2873
2943
  () => httpClient.completeReplay(testRunId),
2874
2944
  resultItems,
@@ -2884,7 +2954,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2884
2954
  const item = resultItems[index];
2885
2955
  const localId = replayedTraceIds[index];
2886
2956
  const mapped = localId ? serverTraceIds[localId] : void 0;
2887
- item.traceId = mapped ?? null;
2957
+ item.traceId = item.traceId ?? mapped ?? null;
2888
2958
  if (item.error === null) {
2889
2959
  completedCount += 1;
2890
2960
  if (mapped === void 0) {
@@ -2972,7 +3042,6 @@ var init_replay = __esm({
2972
3042
  init_randomUuid();
2973
3043
  init_replayContext();
2974
3044
  init_serialize();
2975
- init_unrefTimer();
2976
3045
  REPLAY_PERSISTENCE_TIMEOUT_MS = 3e4;
2977
3046
  BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
2978
3047
  ReplayError = class extends BitfabError {