bitfab 0.36.9 → 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.
package/dist/index.d.cts CHANGED
@@ -353,6 +353,12 @@ interface DeliveryReport {
353
353
  closed: boolean;
354
354
  /** Every carrier submitted under this trace came back accepted. */
355
355
  delivered: boolean;
356
+ /**
357
+ * The server's assigned `traces.id`, read back from the OTLP ingest response.
358
+ * Absent when talking to a server that predates this field, or before any
359
+ * carrier for the trace has been acked.
360
+ */
361
+ serverTraceId?: string;
356
362
  }
357
363
  declare class HttpClient {
358
364
  private readonly apiKey;
@@ -390,6 +396,12 @@ declare class HttpClient {
390
396
  * that was never tracked, so ordinary tracing costs no bookkeeping at all.
391
397
  */
392
398
  trackTraceDeliveries(traceIds: string[]): void;
399
+ /**
400
+ * The server's assigned `traces.id` for a tracked trace if it has already
401
+ * been read back off an ingest response, without stopping tracking. Lets a
402
+ * replay surface the id mid-run for items whose spans already landed.
403
+ */
404
+ peekServerTraceId(traceId: string): string | undefined;
393
405
  /** Whether any tracked trace has had its closing carrier submitted. */
394
406
  hasClosedDeliveries(traceIds: string[]): boolean;
395
407
  /**
@@ -410,6 +422,12 @@ declare class HttpClient {
410
422
  * delivered ref is proof its row exists: the same fact the replay status
411
423
  * endpoint would report, already in hand.
412
424
  */
425
+ /**
426
+ * Record the server's assigned `traces.id` for each tracked source trace,
427
+ * read back from the OTLP ingest response. Keyed by source trace id, the same
428
+ * key the delivery ledger uses. Untracked ids are ignored.
429
+ */
430
+ private recordServerTraceIds;
413
431
  private recordDeliveredCarriers;
414
432
  /**
415
433
  * Track deferred span work so this client's own lifecycle waits for it, and
@@ -2557,7 +2575,7 @@ declare class BitfabFunction {
2557
2575
  /**
2558
2576
  * SDK version from package.json (injected at build time)
2559
2577
  */
2560
- declare const __version__ = "0.36.9";
2578
+ declare const __version__ = "0.36.10";
2561
2579
 
2562
2580
  /**
2563
2581
  * Constants for the Bitfab SDK.
package/dist/index.d.ts CHANGED
@@ -353,6 +353,12 @@ interface DeliveryReport {
353
353
  closed: boolean;
354
354
  /** Every carrier submitted under this trace came back accepted. */
355
355
  delivered: boolean;
356
+ /**
357
+ * The server's assigned `traces.id`, read back from the OTLP ingest response.
358
+ * Absent when talking to a server that predates this field, or before any
359
+ * carrier for the trace has been acked.
360
+ */
361
+ serverTraceId?: string;
356
362
  }
357
363
  declare class HttpClient {
358
364
  private readonly apiKey;
@@ -390,6 +396,12 @@ declare class HttpClient {
390
396
  * that was never tracked, so ordinary tracing costs no bookkeeping at all.
391
397
  */
392
398
  trackTraceDeliveries(traceIds: string[]): void;
399
+ /**
400
+ * The server's assigned `traces.id` for a tracked trace if it has already
401
+ * been read back off an ingest response, without stopping tracking. Lets a
402
+ * replay surface the id mid-run for items whose spans already landed.
403
+ */
404
+ peekServerTraceId(traceId: string): string | undefined;
393
405
  /** Whether any tracked trace has had its closing carrier submitted. */
394
406
  hasClosedDeliveries(traceIds: string[]): boolean;
395
407
  /**
@@ -410,6 +422,12 @@ declare class HttpClient {
410
422
  * delivered ref is proof its row exists: the same fact the replay status
411
423
  * endpoint would report, already in hand.
412
424
  */
425
+ /**
426
+ * Record the server's assigned `traces.id` for each tracked source trace,
427
+ * read back from the OTLP ingest response. Keyed by source trace id, the same
428
+ * key the delivery ledger uses. Untracked ids are ignored.
429
+ */
430
+ private recordServerTraceIds;
413
431
  private recordDeliveredCarriers;
414
432
  /**
415
433
  * Track deferred span work so this client's own lifecycle waits for it, and
@@ -2557,7 +2575,7 @@ declare class BitfabFunction {
2557
2575
  /**
2558
2576
  * SDK version from package.json (injected at build time)
2559
2577
  */
2560
- declare const __version__ = "0.36.9";
2578
+ declare const __version__ = "0.36.10";
2561
2579
 
2562
2580
  /**
2563
2581
  * Constants for the Bitfab SDK.
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  getCurrentReplayBranch,
21
21
  getCurrentSpan,
22
22
  getCurrentTrace
23
- } from "./chunk-TJAFKZ4X.js";
23
+ } from "./chunk-RSM2TLKG.js";
24
24
  import {
25
25
  BITFAB_PROGRESS_PREFIX,
26
26
  BitfabError,
@@ -32,7 +32,7 @@ import {
32
32
  flushTraces,
33
33
  reportReplayProgress,
34
34
  serializeReplayResult
35
- } from "./chunk-MBKY42QC.js";
35
+ } from "./chunk-AAMRJGZJ.js";
36
36
  export {
37
37
  BITFAB_PROGRESS_PREFIX,
38
38
  Bitfab,
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.9";
100
+ __version__ = "0.36.10";
101
101
  }
102
102
  });
103
103
 
@@ -1451,6 +1451,10 @@ var init_http = __esm({
1451
1451
  ...error instanceof BitfabError && error.retryAfterMs !== void 0 ? { retryAfterMs: error.retryAfterMs } : {}
1452
1452
  });
1453
1453
  }
1454
+ const serverTraceIds = asPayloadRecord(response?.traceIds);
1455
+ if (serverTraceIds !== void 0) {
1456
+ this.recordServerTraceIds(serverTraceIds);
1457
+ }
1454
1458
  const partialSuccess = asPayloadRecord(response?.partialSuccess);
1455
1459
  const rejected = partialSuccess?.rejectedSpans;
1456
1460
  if (rejected !== void 0 && rejected !== "0" && rejected !== 0) {
@@ -1475,6 +1479,14 @@ var init_http = __esm({
1475
1479
  }
1476
1480
  }
1477
1481
  }
1482
+ /**
1483
+ * The server's assigned `traces.id` for a tracked trace if it has already
1484
+ * been read back off an ingest response, without stopping tracking. Lets a
1485
+ * replay surface the id mid-run for items whose spans already landed.
1486
+ */
1487
+ peekServerTraceId(traceId) {
1488
+ return this.traceDeliveries.get(traceId)?.serverTraceId;
1489
+ }
1478
1490
  /** Whether any tracked trace has had its closing carrier submitted. */
1479
1491
  hasClosedDeliveries(traceIds) {
1480
1492
  return traceIds.some((traceId) => this.traceDeliveries.get(traceId)?.closed);
@@ -1501,7 +1513,8 @@ var init_http = __esm({
1501
1513
  closed: delivery.closed,
1502
1514
  delivered: delivery.closingAcked && [...delivery.submittedSpanIds].every(
1503
1515
  (spanId) => delivery.ackedSpanIds.has(spanId)
1504
- )
1516
+ ),
1517
+ serverTraceId: delivery.serverTraceId
1505
1518
  };
1506
1519
  }
1507
1520
  return reports;
@@ -1530,6 +1543,23 @@ var init_http = __esm({
1530
1543
  * delivered ref is proof its row exists: the same fact the replay status
1531
1544
  * endpoint would report, already in hand.
1532
1545
  */
1546
+ /**
1547
+ * Record the server's assigned `traces.id` for each tracked source trace,
1548
+ * read back from the OTLP ingest response. Keyed by source trace id, the same
1549
+ * key the delivery ledger uses. Untracked ids are ignored.
1550
+ */
1551
+ recordServerTraceIds(map) {
1552
+ for (const [sourceTraceId, serverTraceId] of Object.entries(map)) {
1553
+ if (typeof serverTraceId !== "string") {
1554
+ continue;
1555
+ }
1556
+ const delivery = this.traceDeliveries.get(sourceTraceId);
1557
+ if (delivery === void 0) {
1558
+ continue;
1559
+ }
1560
+ delivery.serverTraceId = serverTraceId;
1561
+ }
1562
+ }
1533
1563
  recordDeliveredCarriers(refs) {
1534
1564
  for (const ref of refs) {
1535
1565
  const delivery = this.traceDeliveries.get(ref.traceId);
@@ -2667,13 +2697,17 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
2667
2697
  }
2668
2698
  if (!httpClient.hasClosedDeliveries(replayedTraceIds)) {
2669
2699
  httpClient.takeTraceDeliveries(replayedTraceIds);
2670
- return;
2700
+ return {};
2671
2701
  }
2672
2702
  const flushed = await flushTraces(REPLAY_PERSISTENCE_TIMEOUT_MS);
2673
2703
  const deliveries = httpClient.takeTraceDeliveries(replayedTraceIds);
2674
2704
  const expectedSpanCounts = {};
2705
+ const readBackTraceIds = {};
2675
2706
  let allDelivered = true;
2676
2707
  for (const [traceId, delivery] of Object.entries(deliveries)) {
2708
+ if (delivery.serverTraceId !== void 0) {
2709
+ readBackTraceIds[traceId] = delivery.serverTraceId;
2710
+ }
2677
2711
  if (!delivery.closed) {
2678
2712
  continue;
2679
2713
  }
@@ -2681,7 +2715,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
2681
2715
  allDelivered = allDelivered && delivery.delivered;
2682
2716
  }
2683
2717
  if (allDelivered) {
2684
- return;
2718
+ return readBackTraceIds;
2685
2719
  }
2686
2720
  const deadline = Date.now() + REPLAY_PERSISTENCE_TIMEOUT_MS;
2687
2721
  let missing = Object.keys(expectedSpanCounts).length;
@@ -2695,7 +2729,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
2695
2729
  (traceId) => ready[traceId] === void 0
2696
2730
  ).length;
2697
2731
  if (missing === 0) {
2698
- return;
2732
+ return readBackTraceIds;
2699
2733
  }
2700
2734
  if (Date.now() >= deadline) {
2701
2735
  break;
@@ -2723,7 +2757,7 @@ async function mapWithConcurrency2(tasks, maxConcurrency, onSettled, onStarted)
2723
2757
  onStarted?.(index);
2724
2758
  const result = await tasks[index]();
2725
2759
  results[index] = result;
2726
- onSettled?.(result, index);
2760
+ await onSettled?.(result, index);
2727
2761
  }
2728
2762
  }
2729
2763
  const workers = Array.from(
@@ -2813,10 +2847,36 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2813
2847
  let started = 0;
2814
2848
  let succeeded = 0;
2815
2849
  let errored = 0;
2850
+ let flushInFlight = null;
2851
+ let flushPending = false;
2852
+ const flushFinishedItemTrace = () => {
2853
+ flushPending = true;
2854
+ if (!flushInFlight) {
2855
+ flushInFlight = (async () => {
2856
+ try {
2857
+ while (flushPending) {
2858
+ flushPending = false;
2859
+ await httpClient.settleDeferredWork(REPLAY_PERSISTENCE_TIMEOUT_MS);
2860
+ await flushTraces(REPLAY_PERSISTENCE_TIMEOUT_MS);
2861
+ }
2862
+ } finally {
2863
+ flushInFlight = null;
2864
+ }
2865
+ })();
2866
+ }
2867
+ return flushInFlight;
2868
+ };
2816
2869
  const resultItems = await mapWithConcurrency2(
2817
2870
  tasks,
2818
2871
  maxConcurrency,
2819
- (item) => {
2872
+ async (item, index) => {
2873
+ let serverTraceId = null;
2874
+ try {
2875
+ await flushFinishedItemTrace();
2876
+ serverTraceId = httpClient.peekServerTraceId(replayedTraceIds[index]) ?? null;
2877
+ } catch {
2878
+ }
2879
+ item.traceId = serverTraceId;
2820
2880
  completed += 1;
2821
2881
  if (item.error === null) {
2822
2882
  succeeded += 1;
@@ -2831,11 +2891,12 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2831
2891
  succeeded,
2832
2892
  errored,
2833
2893
  item: {
2834
- // The server replay trace id isn't known until completeReplay runs
2835
- // (below), so it can't be reported mid-run and we never emit the
2836
- // client-side placeholder. originalTraceId (the historical trace)
2837
- // is known now and is what a UI keys on to identify what settled.
2838
- traceId: null,
2894
+ // The server's assigned traces.id, read back off the ingest
2895
+ // response and surfaced as this item finishes. Null only if the
2896
+ // per-item flush could not confirm delivery in time; the end-of-run
2897
+ // barrier then fills the returned ReplayItem. The client-side
2898
+ // placeholder is never surfaced.
2899
+ traceId: serverTraceId,
2839
2900
  originalTraceId: item.originalTraceId ?? null,
2840
2901
  originalSpanId: item.originalSpanId ?? null,
2841
2902
  // Deprecated aliases for originalTraceId/originalSpanId.
@@ -2881,12 +2942,19 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2881
2942
  }
2882
2943
  } : void 0
2883
2944
  );
2884
- await preserveReplayFailure(
2945
+ const deliveredTraceIds = await preserveReplayFailure(
2885
2946
  () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),
2886
2947
  resultItems,
2887
2948
  testRunId,
2888
2949
  fullTestRunUrl
2889
2950
  );
2951
+ for (let index = 0; index < resultItems.length; index += 1) {
2952
+ const localId = replayedTraceIds[index];
2953
+ const readBack = localId ? deliveredTraceIds[localId] : void 0;
2954
+ if (readBack !== void 0) {
2955
+ resultItems[index].traceId = readBack;
2956
+ }
2957
+ }
2890
2958
  const completeResult = await preserveReplayFailure(
2891
2959
  () => httpClient.completeReplay(testRunId),
2892
2960
  resultItems,
@@ -2902,7 +2970,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2902
2970
  const item = resultItems[index];
2903
2971
  const localId = replayedTraceIds[index];
2904
2972
  const mapped = localId ? serverTraceIds[localId] : void 0;
2905
- item.traceId = mapped ?? null;
2973
+ item.traceId = item.traceId ?? mapped ?? null;
2906
2974
  if (item.error === null) {
2907
2975
  completedCount += 1;
2908
2976
  if (mapped === void 0) {