bitfab 0.52.3 → 0.52.5

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.
Files changed (35) hide show
  1. package/README.md +2 -0
  2. package/dist/{chunk-JPFJ5YSK.js → chunk-3D7FNJ5R.js} +14 -2
  3. package/dist/{chunk-JPFJ5YSK.js.map → chunk-3D7FNJ5R.js.map} +1 -1
  4. package/dist/chunk-3QXNM62Y.js +747 -0
  5. package/dist/chunk-3QXNM62Y.js.map +1 -0
  6. package/dist/{chunk-NZGYJWHY.js → chunk-6DRFQJ75.js} +111 -13
  7. package/dist/chunk-6DRFQJ75.js.map +1 -0
  8. package/dist/{chunk-XRSOWDOP.js → chunk-CAE7HE4C.js} +15 -2
  9. package/dist/{chunk-XRSOWDOP.js.map → chunk-CAE7HE4C.js.map} +1 -1
  10. package/dist/{chunk-EAET3MSX.js → chunk-EMRHO6YW.js} +291 -21
  11. package/dist/chunk-EMRHO6YW.js.map +1 -0
  12. package/dist/{http-HSNLZMNZ.js → http-Q6UEKZRF.js} +2 -2
  13. package/dist/{http-W66ZE5TL.js → http-UQT6QVBC.js} +2 -2
  14. package/dist/index.cjs +444 -57
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +44 -2
  17. package/dist/index.d.ts +44 -2
  18. package/dist/index.js +5 -3
  19. package/dist/node.cjs +444 -57
  20. package/dist/node.cjs.map +1 -1
  21. package/dist/node.d.cts +1 -1
  22. package/dist/node.d.ts +1 -1
  23. package/dist/node.js +5 -3
  24. package/dist/node.js.map +1 -1
  25. package/dist/{replay-QMA2K5M4.js → replay-UVSZFZW7.js} +5 -3
  26. package/dist/replayCli.js +18 -687
  27. package/dist/replayCli.js.map +1 -1
  28. package/dist/seedCli.js +30 -0
  29. package/dist/seedCli.js.map +1 -0
  30. package/package.json +3 -2
  31. package/dist/chunk-EAET3MSX.js.map +0 -1
  32. package/dist/chunk-NZGYJWHY.js.map +0 -1
  33. /package/dist/{http-HSNLZMNZ.js.map → http-Q6UEKZRF.js.map} +0 -0
  34. /package/dist/{http-W66ZE5TL.js.map → http-UQT6QVBC.js.map} +0 -0
  35. /package/dist/{replay-QMA2K5M4.js.map → replay-UVSZFZW7.js.map} +0 -0
package/dist/node.cjs CHANGED
@@ -97,7 +97,7 @@ var __version__, __packageName__;
97
97
  var init_version_generated = __esm({
98
98
  "src/version.generated.ts"() {
99
99
  "use strict";
100
- __version__ = "0.52.3";
100
+ __version__ = "0.52.5";
101
101
  __packageName__ = "bitfab";
102
102
  }
103
103
  });
@@ -1137,7 +1137,7 @@ var init_otel = __esm({
1137
1137
  if (this.onDelivered === void 0) {
1138
1138
  return;
1139
1139
  }
1140
- const refs = spans.map((span) => span.ref).filter((ref) => ref !== void 0);
1140
+ const refs = spans.map((span) => span.ref).filter((ref2) => ref2 !== void 0);
1141
1141
  if (refs.length === 0) {
1142
1142
  return;
1143
1143
  }
@@ -1394,9 +1394,9 @@ function parseRetryAfterMs(header) {
1394
1394
  }
1395
1395
  return Math.max(0, at - Date.now());
1396
1396
  }
1397
- function carrierMeta(operation, payload, ref) {
1397
+ function carrierMeta(operation, payload, ref2) {
1398
1398
  return {
1399
- ref,
1399
+ ref: ref2,
1400
1400
  name: carrierName(operation, payload),
1401
1401
  startTime: payloadTimestamp(payload, "started_at"),
1402
1402
  endTime: payloadTimestamp(payload, "ended_at"),
@@ -1641,22 +1641,22 @@ var init_http = __esm({
1641
1641
  return reports;
1642
1642
  }
1643
1643
  /** Build a carrier's meta and record what it adds to its trace's expected set. */
1644
- recordedMeta(operation, payload, ref) {
1645
- this.recordSubmittedCarrier(ref);
1646
- return carrierMeta(operation, payload, ref);
1644
+ recordedMeta(operation, payload, ref2) {
1645
+ this.recordSubmittedCarrier(ref2);
1646
+ return carrierMeta(operation, payload, ref2);
1647
1647
  }
1648
- recordSubmittedCarrier(ref) {
1649
- if (ref === void 0) {
1648
+ recordSubmittedCarrier(ref2) {
1649
+ if (ref2 === void 0) {
1650
1650
  return;
1651
1651
  }
1652
- const delivery = this.traceDeliveries.get(ref.traceId);
1652
+ const delivery = this.traceDeliveries.get(ref2.traceId);
1653
1653
  if (delivery === void 0) {
1654
1654
  return;
1655
1655
  }
1656
- if (ref.spanId === void 0) {
1656
+ if (ref2.spanId === void 0) {
1657
1657
  delivery.closed = true;
1658
1658
  } else {
1659
- delivery.submittedSpanIds.add(ref.spanId);
1659
+ delivery.submittedSpanIds.add(ref2.spanId);
1660
1660
  }
1661
1661
  }
1662
1662
  /**
@@ -1682,15 +1682,15 @@ var init_http = __esm({
1682
1682
  }
1683
1683
  }
1684
1684
  recordDeliveredCarriers(refs) {
1685
- for (const ref of refs) {
1686
- const delivery = this.traceDeliveries.get(ref.traceId);
1685
+ for (const ref2 of refs) {
1686
+ const delivery = this.traceDeliveries.get(ref2.traceId);
1687
1687
  if (delivery === void 0) {
1688
1688
  continue;
1689
1689
  }
1690
- if (ref.spanId === void 0) {
1690
+ if (ref2.spanId === void 0) {
1691
1691
  delivery.closingAcked = true;
1692
1692
  } else {
1693
- delivery.ackedSpanIds.add(ref.spanId);
1693
+ delivery.ackedSpanIds.add(ref2.spanId);
1694
1694
  }
1695
1695
  }
1696
1696
  }
@@ -1720,9 +1720,9 @@ var init_http = __esm({
1720
1720
  */
1721
1721
  async waitForPendingRequests(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
1722
1722
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1723
- const settled = await this.settleDeferredWork(timeoutMs);
1723
+ const settled2 = await this.settleDeferredWork(timeoutMs);
1724
1724
  const flushed = await this.traceTransport?.flush(Math.max(0, deadline - Date.now())) ?? true;
1725
- return settled && flushed;
1725
+ return settled2 && flushed;
1726
1726
  }
1727
1727
  /**
1728
1728
  * Flush and permanently close this client's tracing transport. Idempotent:
@@ -1735,14 +1735,14 @@ var init_http = __esm({
1735
1735
  }
1736
1736
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1737
1737
  this.closing = (async () => {
1738
- const settled = await this.settleDeferredWork(
1738
+ const settled2 = await this.settleDeferredWork(
1739
1739
  Math.max(0, deadline - Date.now())
1740
1740
  );
1741
1741
  this.closed = true;
1742
1742
  const transport = this.traceTransport;
1743
1743
  this.traceTransport = void 0;
1744
1744
  const shutdownOk = await transport?.shutdown(Math.max(0, deadline - Date.now())) ?? true;
1745
- return settled && shutdownOk;
1745
+ return settled2 && shutdownOk;
1746
1746
  })();
1747
1747
  return this.closing;
1748
1748
  }
@@ -2131,6 +2131,18 @@ var init_http = __esm({
2131
2131
  { timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS }
2132
2132
  );
2133
2133
  }
2134
+ async getReseedSource(traceId) {
2135
+ return this.get(
2136
+ `/api/sdk/traces/${encodeURIComponent(traceId)}/reseedSource`
2137
+ );
2138
+ }
2139
+ async reseedTrace(traceId, runTraceId) {
2140
+ return this.request(
2141
+ `/api/sdk/traces/${encodeURIComponent(traceId)}/reseed`,
2142
+ { runTraceId },
2143
+ { timeout: 3e4 }
2144
+ );
2145
+ }
2134
2146
  /**
2135
2147
  * Ask the server to materialize a per-trace DB branch lease from a
2136
2148
  * captured `dbSnapshotRef`. Blocking - the resolver creates a Neon
@@ -2341,6 +2353,112 @@ var init_randomUuid = __esm({
2341
2353
  }
2342
2354
  });
2343
2355
 
2356
+ // src/gitCommand.ts
2357
+ function isUnrefable(value) {
2358
+ return typeof value === "object" && value !== null && "unref" in value && typeof value.unref === "function";
2359
+ }
2360
+ function unrefStream(stream) {
2361
+ if (isUnrefable(stream)) {
2362
+ stream.unref();
2363
+ }
2364
+ }
2365
+ function terminate(child) {
2366
+ if (child.pid && process.platform !== "win32") {
2367
+ try {
2368
+ process.kill(-child.pid, "SIGTERM");
2369
+ return;
2370
+ } catch {
2371
+ child.kill("SIGTERM");
2372
+ return;
2373
+ }
2374
+ }
2375
+ child.kill("SIGTERM");
2376
+ }
2377
+ async function gitRunner(options) {
2378
+ let spawn;
2379
+ try {
2380
+ ;
2381
+ ({ spawn } = await import("child_process"));
2382
+ } catch {
2383
+ return null;
2384
+ }
2385
+ return (dir, args) => new Promise((resolve) => {
2386
+ let child;
2387
+ try {
2388
+ child = spawn("git", args, {
2389
+ cwd: dir,
2390
+ detached: process.platform !== "win32",
2391
+ stdio: ["ignore", "pipe", "pipe"]
2392
+ });
2393
+ } catch {
2394
+ resolve(null);
2395
+ return;
2396
+ }
2397
+ const chunks = [];
2398
+ let size = 0;
2399
+ let overflow = false;
2400
+ let exitCode = null;
2401
+ let done = false;
2402
+ const timers = [];
2403
+ const schedule = (callback, ms) => {
2404
+ const timer = setTimeout(callback, ms);
2405
+ if (!options.keepProcessAlive) {
2406
+ timer.unref();
2407
+ }
2408
+ timers.push(timer);
2409
+ };
2410
+ const finish = () => {
2411
+ if (done) {
2412
+ return;
2413
+ }
2414
+ done = true;
2415
+ for (const timer of timers) {
2416
+ clearTimeout(timer);
2417
+ }
2418
+ child.stdout?.destroy();
2419
+ child.stderr?.destroy();
2420
+ resolve(
2421
+ exitCode === 0 && !overflow ? Buffer.concat(chunks).toString("utf8") : null
2422
+ );
2423
+ };
2424
+ child.stdout?.on("data", (chunk) => {
2425
+ size += chunk.length;
2426
+ if (size > options.maxBuffer) {
2427
+ overflow = true;
2428
+ terminate(child);
2429
+ return;
2430
+ }
2431
+ chunks.push(chunk);
2432
+ });
2433
+ child.stderr?.resume();
2434
+ child.on("error", () => {
2435
+ exitCode = null;
2436
+ finish();
2437
+ });
2438
+ child.on("exit", (code) => {
2439
+ exitCode = code;
2440
+ schedule(finish, EXIT_GRACE_MS);
2441
+ });
2442
+ child.on("close", (code) => {
2443
+ exitCode = code ?? exitCode;
2444
+ finish();
2445
+ });
2446
+ schedule(() => terminate(child), options.timeoutMs);
2447
+ if (!options.keepProcessAlive) {
2448
+ child.unref();
2449
+ unrefStream(child.stdout);
2450
+ unrefStream(child.stderr);
2451
+ }
2452
+ });
2453
+ }
2454
+ var EXIT_GRACE_MS;
2455
+ var init_gitCommand = __esm({
2456
+ "src/gitCommand.ts"() {
2457
+ "use strict";
2458
+ EXIT_GRACE_MS = 500;
2459
+ }
2460
+ });
2461
+
2344
2462
  // src/mockOverride.ts
2345
2463
  function resolveMockValue(value, ctx) {
2346
2464
  return typeof value === "function" ? value(ctx) : value;
@@ -2397,37 +2515,33 @@ async function readCodeChangeFile() {
2397
2515
  }
2398
2516
  }
2399
2517
  async function captureCodeChangeFromGit(cwd, label) {
2400
- let execFile;
2401
2518
  let readFile;
2402
2519
  try {
2403
2520
  ;
2404
- ({ execFile } = await import("child_process"));
2405
2521
  ({ readFile } = await import("fs/promises"));
2406
2522
  } catch {
2407
2523
  return null;
2408
2524
  }
2409
- const git = (dir, args) => new Promise((resolve) => {
2410
- execFile(
2411
- "git",
2412
- args,
2413
- // 30s timeout so a hung git (e.g. a network-touching ref op) can't
2414
- // block the whole replay indefinitely.
2415
- { cwd: dir, maxBuffer: 64 * 1024 * 1024, timeout: 3e4 },
2416
- (err, stdout) => resolve(err ? null : stdout)
2417
- );
2525
+ const git = await gitRunner({
2526
+ timeoutMs: 3e4,
2527
+ maxBuffer: 64 * 1024 * 1024,
2528
+ keepProcessAlive: true
2418
2529
  });
2530
+ if (!git) {
2531
+ return null;
2532
+ }
2419
2533
  try {
2420
2534
  const root = (await git(cwd, ["rev-parse", "--show-toplevel"]))?.trim();
2421
2535
  if (!root) {
2422
2536
  return null;
2423
2537
  }
2424
- const resolved = await resolveBase(git, root);
2425
- if (!resolved) {
2538
+ const resolved2 = await resolveBase(git, root);
2539
+ if (!resolved2) {
2426
2540
  return null;
2427
2541
  }
2428
- const { base, fromTrunk } = resolved;
2429
- const blobBytes = async (ref, path) => {
2430
- const out = await git(root, ["cat-file", "-s", `${ref}:${path}`]);
2542
+ const { base, fromTrunk } = resolved2;
2543
+ const blobBytes = async (ref2, path) => {
2544
+ const out = await git(root, ["cat-file", "-s", `${ref2}:${path}`]);
2431
2545
  const n = out ? Number.parseInt(out.trim(), 10) : Number.NaN;
2432
2546
  return Number.isFinite(n) ? n : 0;
2433
2547
  };
@@ -2519,8 +2633,8 @@ async function resolveBase(git, root) {
2519
2633
  }
2520
2634
  return await refExists(git, root, "HEAD") ? { base: "HEAD", fromTrunk: false } : null;
2521
2635
  }
2522
- async function refExists(git, root, ref) {
2523
- return await git(root, ["rev-parse", "--verify", `${ref}^{object}`]) !== null;
2636
+ async function refExists(git, root, ref2) {
2637
+ return await git(root, ["rev-parse", "--verify", `${ref2}^{object}`]) !== null;
2524
2638
  }
2525
2639
  async function readWorkingFile(readFile, root, path) {
2526
2640
  try {
@@ -2555,6 +2669,7 @@ var MAX_FILES, MAX_FILE_BYTES, MAX_TOTAL_BYTES, TRUNK_CANDIDATES, NUL;
2555
2669
  var init_codeChange = __esm({
2556
2670
  "src/codeChange.ts"() {
2557
2671
  "use strict";
2672
+ init_gitCommand();
2558
2673
  MAX_FILES = 60;
2559
2674
  MAX_FILE_BYTES = 5e5;
2560
2675
  MAX_TOTAL_BYTES = 2e6;
@@ -2575,6 +2690,7 @@ __export(replay_exports, {
2575
2690
  BITFAB_PROGRESS_PREFIX: () => BITFAB_PROGRESS_PREFIX,
2576
2691
  DbBranchReplayError: () => DbBranchReplayError,
2577
2692
  ReplayError: () => ReplayError,
2693
+ deserializeInputs: () => deserializeInputs,
2578
2694
  replay: () => replay,
2579
2695
  reportReplayProgress: () => reportReplayProgress,
2580
2696
  serializeReplayResult: () => serializeReplayResult,
@@ -2758,9 +2874,9 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2758
2874
  const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
2759
2875
  try {
2760
2876
  if (includeDbBranchLease && !lease && !leaseError) {
2761
- let resolved;
2877
+ let resolved2;
2762
2878
  try {
2763
- resolved = await httpClient.resolveDbBranchLease(
2879
+ resolved2 = await httpClient.resolveDbBranchLease(
2764
2880
  testRunId,
2765
2881
  originalTraceId,
2766
2882
  dbBranchSettings,
@@ -2774,10 +2890,10 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2774
2890
  cause
2775
2891
  );
2776
2892
  }
2777
- lease = resolved.lease ?? void 0;
2778
- leaseError = resolved.leaseError ?? void 0;
2779
- dbSnapshotRef = resolved.dbSnapshotRef ?? dbSnapshotRef;
2780
- dbBranchTimings = resolved.timings ?? dbBranchTimings;
2893
+ lease = resolved2.lease ?? void 0;
2894
+ leaseError = resolved2.leaseError ?? void 0;
2895
+ dbSnapshotRef = resolved2.dbSnapshotRef ?? dbSnapshotRef;
2896
+ dbBranchTimings = resolved2.timings ?? dbBranchTimings;
2781
2897
  }
2782
2898
  if (leaseError) {
2783
2899
  throw new DbBranchReplayError(
@@ -3425,6 +3541,7 @@ __export(node_exports, {
3425
3541
  getCurrentSpan: () => getCurrentSpan,
3426
3542
  getCurrentTrace: () => getCurrentTrace,
3427
3543
  reportReplayProgress: () => reportReplayProgress,
3544
+ reseedFromRegistry: () => reseedFromRegistry,
3428
3545
  seedFromRegistry: () => seedFromRegistry,
3429
3546
  serializeReplayResult: () => serializeReplayResult
3430
3547
  });
@@ -4559,24 +4676,239 @@ function mixedTracingError(enteredApi, entered, enclosing, traceFunctionKey) {
4559
4676
  `opt-in and opt-out tracing can't be mixed in one call stack: ${enteredApi} (${entered})${subject} was entered inside a ${SURFACE_API[enclosing]} call (${enclosing}). ${MIXED_SURFACE_REMEDY[entered]}`
4560
4677
  );
4561
4678
  }
4562
- function assertSurfacesCompatible(requested, resolved, parentSurface, traceFunctionKey) {
4679
+ function assertSurfacesCompatible(requested, resolved2, parentSurface, traceFunctionKey) {
4563
4680
  if (requested === "inherit" || requested === "neutral") {
4564
4681
  return;
4565
4682
  }
4566
- if (resolved === void 0 || parentSurface === void 0) {
4683
+ if (resolved2 === void 0 || parentSurface === void 0) {
4567
4684
  return;
4568
4685
  }
4569
- if (parentSurface === resolved) {
4686
+ if (parentSurface === resolved2) {
4570
4687
  return;
4571
4688
  }
4572
4689
  throw mixedTracingError(
4573
- SURFACE_API[resolved],
4574
- resolved,
4690
+ SURFACE_API[resolved2],
4691
+ resolved2,
4575
4692
  parentSurface,
4576
4693
  traceFunctionKey
4577
4694
  );
4578
4695
  }
4579
4696
 
4697
+ // src/commitRef.ts
4698
+ init_gitCommand();
4699
+ init_readEnv();
4700
+ var EXPLICIT_SHA_ENV = "BITFAB_COMMIT_SHA";
4701
+ var DISABLE_ENV = "BITFAB_DISABLE_COMMIT_REF";
4702
+ var GIT_TIMEOUT_MS = 2e3;
4703
+ var GIT_MAX_BUFFER = 1024 * 1024;
4704
+ var VERCEL_PROVIDER_HOSTS = {
4705
+ github: "github.com",
4706
+ gitlab: "gitlab.com",
4707
+ bitbucket: "bitbucket.org"
4708
+ };
4709
+ function read(env, name) {
4710
+ if (name === null) {
4711
+ return null;
4712
+ }
4713
+ const value = env(name)?.trim();
4714
+ return value ? value : null;
4715
+ }
4716
+ function remoteFrom(...names) {
4717
+ return (env) => {
4718
+ const parts = names.map((name) => read(env, name));
4719
+ if (parts.some((part) => part === null)) {
4720
+ return null;
4721
+ }
4722
+ return normalizeRemote(parts.join("/"));
4723
+ };
4724
+ }
4725
+ function vercelRemote(env) {
4726
+ const provider = read(env, "VERCEL_GIT_PROVIDER")?.toLowerCase() ?? "";
4727
+ const host = VERCEL_PROVIDER_HOSTS[provider];
4728
+ const owner = read(env, "VERCEL_GIT_REPO_OWNER");
4729
+ const slug = read(env, "VERCEL_GIT_REPO_SLUG");
4730
+ if (!host || !owner || !slug) {
4731
+ return null;
4732
+ }
4733
+ return `${host}/${owner}/${slug}`;
4734
+ }
4735
+ function noRemote() {
4736
+ return null;
4737
+ }
4738
+ var PLATFORM_ENVS = [
4739
+ ["VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF", vercelRemote],
4740
+ [
4741
+ "GITHUB_SHA",
4742
+ "GITHUB_REF_NAME",
4743
+ remoteFrom("GITHUB_SERVER_URL", "GITHUB_REPOSITORY")
4744
+ ],
4745
+ ["RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH", noRemote],
4746
+ ["RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH", noRemote],
4747
+ ["SOURCE_VERSION", null, noRemote],
4748
+ ["CF_PAGES_COMMIT_SHA", "CF_PAGES_BRANCH", noRemote],
4749
+ ["CI_COMMIT_SHA", "CI_COMMIT_REF_NAME", remoteFrom("CI_REPOSITORY_URL")],
4750
+ [
4751
+ "BUILD_SOURCEVERSION",
4752
+ "BUILD_SOURCEBRANCHNAME",
4753
+ remoteFrom("BUILD_REPOSITORY_URI")
4754
+ ],
4755
+ ["CIRCLE_SHA1", "CIRCLE_BRANCH", remoteFrom("CIRCLE_REPOSITORY_URL")]
4756
+ ];
4757
+ var COMMIT_REF_ENV_NAMES = [
4758
+ EXPLICIT_SHA_ENV,
4759
+ DISABLE_ENV,
4760
+ ...PLATFORM_ENVS.flatMap(
4761
+ ([sha, branch]) => branch === null ? [sha] : [sha, branch]
4762
+ ),
4763
+ "GITHUB_SERVER_URL",
4764
+ "GITHUB_REPOSITORY",
4765
+ "CI_REPOSITORY_URL",
4766
+ "BUILD_REPOSITORY_URI",
4767
+ "CIRCLE_REPOSITORY_URL",
4768
+ "VERCEL_GIT_PROVIDER",
4769
+ "VERCEL_GIT_REPO_OWNER",
4770
+ "VERCEL_GIT_REPO_SLUG"
4771
+ ];
4772
+ function normalizeRemote(raw) {
4773
+ const value = raw?.trim();
4774
+ if (!value) {
4775
+ return null;
4776
+ }
4777
+ let host = "";
4778
+ let path = value;
4779
+ if (value.includes("://")) {
4780
+ try {
4781
+ const parsed = new URL(value);
4782
+ host = parsed.hostname;
4783
+ path = parsed.pathname;
4784
+ } catch {
4785
+ return null;
4786
+ }
4787
+ } else {
4788
+ const separator = value.indexOf(":");
4789
+ if (separator !== -1) {
4790
+ const head = value.slice(0, separator);
4791
+ host = head.slice(head.lastIndexOf("@") + 1);
4792
+ path = value.slice(separator + 1);
4793
+ }
4794
+ }
4795
+ path = path.replace(/^\/+|\/+$/g, "");
4796
+ if (path.endsWith(".git")) {
4797
+ path = path.slice(0, -".git".length).replace(/\/+$/, "");
4798
+ }
4799
+ if (!host) {
4800
+ return path || null;
4801
+ }
4802
+ return path ? `${host}/${path}` : host;
4803
+ }
4804
+ function resolveCommitRefFromEnv(env) {
4805
+ const explicit = read(env, EXPLICIT_SHA_ENV);
4806
+ const platform = PLATFORM_ENVS.find(([sha]) => read(env, sha) !== null);
4807
+ if (!platform) {
4808
+ if (explicit === null) {
4809
+ return null;
4810
+ }
4811
+ return {
4812
+ sha: explicit,
4813
+ branch: null,
4814
+ dirty: null,
4815
+ remote: null,
4816
+ rootSha: null
4817
+ };
4818
+ }
4819
+ const [shaEnv, branchEnv, readRemote] = platform;
4820
+ return {
4821
+ sha: explicit ?? read(env, shaEnv),
4822
+ branch: read(env, branchEnv),
4823
+ dirty: null,
4824
+ remote: readRemote(env),
4825
+ rootSha: null
4826
+ };
4827
+ }
4828
+ async function resolveCommitRefFromGit(cwd) {
4829
+ const git = await gitRunner({
4830
+ timeoutMs: GIT_TIMEOUT_MS,
4831
+ maxBuffer: GIT_MAX_BUFFER,
4832
+ keepProcessAlive: false
4833
+ });
4834
+ if (!git) {
4835
+ return null;
4836
+ }
4837
+ const run = async (args) => (await git(cwd, args))?.trim() ?? null;
4838
+ const sha = await run(["rev-parse", "HEAD"]);
4839
+ if (!sha) {
4840
+ return null;
4841
+ }
4842
+ const [branch, status, remote, roots] = await Promise.all([
4843
+ run(["symbolic-ref", "--short", "-q", "HEAD"]),
4844
+ run(["status", "--porcelain"]),
4845
+ run(["remote", "get-url", "origin"]),
4846
+ run(["rev-list", "--max-parents=0", "HEAD"])
4847
+ ]);
4848
+ return {
4849
+ sha,
4850
+ branch: branch || null,
4851
+ dirty: status === null ? null : status.length > 0,
4852
+ remote: normalizeRemote(remote),
4853
+ rootSha: roots ? roots.split(/\s+/).sort()[0] ?? null : null
4854
+ };
4855
+ }
4856
+ function commitRefWire(ref2) {
4857
+ return {
4858
+ sha: ref2.sha,
4859
+ branch: ref2.branch,
4860
+ dirty: ref2.dirty,
4861
+ remote: ref2.remote,
4862
+ root_sha: ref2.rootSha
4863
+ };
4864
+ }
4865
+ var resolved = false;
4866
+ var ref = null;
4867
+ var gitStarted = false;
4868
+ var settled = Promise.resolve();
4869
+ function startCommitRefResolution() {
4870
+ if (resolved || gitStarted) {
4871
+ return;
4872
+ }
4873
+ if (read(readEnv, DISABLE_ENV) !== null) {
4874
+ resolved = true;
4875
+ return;
4876
+ }
4877
+ const fromEnv = resolveCommitRefFromEnv(readEnv);
4878
+ if (fromEnv) {
4879
+ ref = fromEnv;
4880
+ resolved = true;
4881
+ return;
4882
+ }
4883
+ gitStarted = true;
4884
+ if (typeof process === "undefined") {
4885
+ resolved = true;
4886
+ return;
4887
+ }
4888
+ let cwd;
4889
+ try {
4890
+ cwd = process.cwd?.() ?? ".";
4891
+ } catch {
4892
+ resolved = true;
4893
+ return;
4894
+ }
4895
+ settled = resolveCommitRefFromGit(cwd).then(
4896
+ (fromGit) => {
4897
+ if (!resolved) {
4898
+ ref = fromGit;
4899
+ resolved = true;
4900
+ }
4901
+ },
4902
+ () => {
4903
+ resolved = true;
4904
+ }
4905
+ );
4906
+ }
4907
+ function currentCommitRef() {
4908
+ startCommitRefResolution();
4909
+ return ref;
4910
+ }
4911
+
4580
4912
  // src/client.ts
4581
4913
  init_constants();
4582
4914
 
@@ -5069,11 +5401,11 @@ var BitfabLangGraphCallbackHandler = class {
5069
5401
  this.invocations.set(invocation.rootRunId, invocation);
5070
5402
  }
5071
5403
  if (!willHide) {
5072
- let resolved = parentSpan;
5073
- while (resolved?.hidden === true) {
5074
- resolved = resolved.parentId ? this.runToSpan.get(resolved.parentId) : void 0;
5404
+ let resolved2 = parentSpan;
5405
+ while (resolved2?.hidden === true) {
5406
+ resolved2 = resolved2.parentId ? this.runToSpan.get(resolved2.parentId) : void 0;
5075
5407
  }
5076
- effectiveParentId = resolved ? resolved.spanId : invocation.activeContext?.spanId ?? null;
5408
+ effectiveParentId = resolved2 ? resolved2.spanId : invocation.activeContext?.spanId ?? null;
5077
5409
  } else {
5078
5410
  effectiveParentId = parentRunId ?? null;
5079
5411
  }
@@ -5741,6 +6073,7 @@ var BitfabOpenAIAgentHandler = class {
5741
6073
 
5742
6074
  // src/client.ts
5743
6075
  init_randomUuid();
6076
+ init_replay();
5744
6077
 
5745
6078
  // src/replayBranch.ts
5746
6079
  var _url, _context;
@@ -6572,6 +6905,7 @@ var Bitfab = class {
6572
6905
  validateDbSnapshotConfig(config.dbSnapshot);
6573
6906
  }
6574
6907
  this.dbSnapshot = config.dbSnapshot;
6908
+ startCommitRefResolution();
6575
6909
  this.httpClient = new HttpClient({
6576
6910
  apiKey: () => this.resolveApiKey(),
6577
6911
  serviceUrl: this.serviceUrl,
@@ -7665,15 +7999,15 @@ var Bitfab = class {
7665
7999
  );
7666
8000
  }
7667
8001
  return runWithSpanStack(newStack, async () => {
7668
- const resolved = await resolution;
7669
- if (resolved.matched) {
8002
+ const resolved2 = await resolution;
8003
+ if (resolved2.matched) {
7670
8004
  void sendSpan({
7671
- result: resolved.output,
8005
+ result: resolved2.output,
7672
8006
  mocked: true,
7673
8007
  mockTarget: "output",
7674
8008
  mockSource: "override"
7675
8009
  });
7676
- return resolved.output;
8010
+ return resolved2.output;
7677
8011
  }
7678
8012
  if (shouldMockWithBaseStrategy && !mockSpan) {
7679
8013
  throw new BitfabError(
@@ -7909,6 +8243,10 @@ var Bitfab = class {
7909
8243
  if (params.dbSnapshotRef) {
7910
8244
  rawTrace.db_snapshot_ref = params.dbSnapshotRef;
7911
8245
  }
8246
+ const commitRef = currentCommitRef();
8247
+ if (commitRef) {
8248
+ rawTrace.commit_ref = commitRefWire(commitRef);
8249
+ }
7912
8250
  if (params.ingestionType) {
7913
8251
  rawTrace.ingestion_type = params.ingestionType;
7914
8252
  }
@@ -8168,6 +8506,36 @@ var Bitfab = class {
8168
8506
  }
8169
8507
  return traceId;
8170
8508
  }
8509
+ async reseedTrace(traceFunctionKey, fn, options) {
8510
+ const source = await this.httpClient.getReseedSource(options.traceId);
8511
+ if (source.traceFunctionKey !== traceFunctionKey) {
8512
+ throw new BitfabError(
8513
+ `Trace ${options.traceId} belongs to '${source.traceFunctionKey}', not '${traceFunctionKey}'.`
8514
+ );
8515
+ }
8516
+ const args = deserializeInputs({
8517
+ input: source.input,
8518
+ input_meta: source.inputMeta
8519
+ });
8520
+ const runTraceId = await this.seedTraceByRunning(
8521
+ traceFunctionKey,
8522
+ fn,
8523
+ {
8524
+ args,
8525
+ metadata: source.metadata,
8526
+ ...source.sessionId === null ? {} : { sessionId: source.sessionId },
8527
+ ...source.name === null ? {} : { name: source.name }
8528
+ }
8529
+ );
8530
+ const adopted = await this.httpClient.reseedTrace(
8531
+ options.traceId,
8532
+ runTraceId
8533
+ );
8534
+ return {
8535
+ traceId: adopted.traceId,
8536
+ previousRunTraceId: adopted.previousRunTraceId
8537
+ };
8538
+ }
8171
8539
  async replay(traceFunctionKey, fn, options) {
8172
8540
  const wrappedKey = fn._bitfabTraceFunctionKey;
8173
8541
  let replayFn = fn;
@@ -8429,6 +8797,24 @@ init_replay();
8429
8797
  function defineReplayRegistry(registry) {
8430
8798
  return registry;
8431
8799
  }
8800
+ async function reseedFromRegistry(registry, pipeline, traceIds) {
8801
+ const registration = registry[pipeline];
8802
+ if (registration === void 0) {
8803
+ throw new BitfabError(
8804
+ `Unknown pipeline '${pipeline}'. Registered: ${Object.keys(registry).join(", ")}`
8805
+ );
8806
+ }
8807
+ const traceFunctionKey = resolveTraceFunctionKey(registration);
8808
+ const reseeded = [];
8809
+ for (const traceId of traceIds) {
8810
+ reseeded.push(
8811
+ await registration.client.reseedTrace(traceFunctionKey, registration.fn, {
8812
+ traceId
8813
+ })
8814
+ );
8815
+ }
8816
+ return { pipeline, traceFunctionKey, reseeded };
8817
+ }
8432
8818
  async function seedFromRegistry(registry, pipeline, cases, options = {}) {
8433
8819
  const registration = registry[pipeline];
8434
8820
  if (registration === void 0) {
@@ -8509,6 +8895,7 @@ assertAsyncStorageRegistered();
8509
8895
  getCurrentSpan,
8510
8896
  getCurrentTrace,
8511
8897
  reportReplayProgress,
8898
+ reseedFromRegistry,
8512
8899
  seedFromRegistry,
8513
8900
  serializeReplayResult
8514
8901
  });