@bitfab/sdk 0.65.0 → 0.65.3

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 (93) hide show
  1. package/cloudReplay.py +207 -30
  2. package/dist/autoTrace.cjs +28 -0
  3. package/dist/autoTrace.cjs.map +1 -1
  4. package/dist/autoTrace.d.cts +2 -1
  5. package/dist/autoTrace.d.ts +2 -1
  6. package/dist/autoTrace.js +3 -1
  7. package/dist/{chunk-3W6RLPGK.js → chunk-BRMGNMDO.js} +4 -4
  8. package/dist/chunk-BRMGNMDO.js.map +1 -0
  9. package/dist/{chunk-WW2RMMTA.js → chunk-CLP2L5PU.js} +2 -2
  10. package/dist/{chunk-WW2RMMTA.js.map → chunk-CLP2L5PU.js.map} +1 -1
  11. package/dist/{chunk-JIGT4NC5.js → chunk-HLH7TN2A.js} +2 -2
  12. package/dist/{chunk-TNIJXUGC.js → chunk-LCV2JYXL.js} +2 -2
  13. package/dist/{chunk-TNIJXUGC.js.map → chunk-LCV2JYXL.js.map} +1 -1
  14. package/dist/{chunk-SUXEIG4U.js → chunk-NRE5CJPR.js} +29 -13
  15. package/dist/chunk-NRE5CJPR.js.map +1 -0
  16. package/dist/{chunk-MN3E2RUU.js → chunk-PTCLRG4E.js} +3 -3
  17. package/dist/{chunk-YWXQM6J3.js → chunk-QRNHRY7F.js} +28 -1
  18. package/dist/chunk-QRNHRY7F.js.map +1 -0
  19. package/dist/{chunk-IBWW3SP4.js → chunk-YVJAOGHE.js} +2 -2
  20. package/dist/{chunk-JXIW4FOF.js → chunk-Z26K57WZ.js} +2 -2
  21. package/dist/{chunk-GSHCHCB5.js → chunk-ZG6SHKEF.js} +12 -41
  22. package/dist/chunk-ZG6SHKEF.js.map +1 -0
  23. package/dist/{http-7LTYEMJO.js → http-VN52X47M.js} +2 -2
  24. package/dist/{http-NNVOMI63.js → http-WP4YPPKC.js} +2 -2
  25. package/dist/index.cjs +37 -7
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +4 -4
  30. package/dist/node.cjs +37 -7
  31. package/dist/node.cjs.map +1 -1
  32. package/dist/node.js +4 -4
  33. package/dist/{replay-D7AN6O25.js → replay-463SEV53.js} +3 -3
  34. package/dist/{replay-L56SESG7.js → replay-OT4A4HVF.js} +3 -3
  35. package/dist/replayCli.js +3 -3
  36. package/dist/seedCli.js +3 -3
  37. package/dist/transform/babel.cjs +11 -40
  38. package/dist/transform/babel.cjs.map +1 -1
  39. package/dist/transform/babel.js +1 -1
  40. package/dist/transform/bun.cjs +11 -40
  41. package/dist/transform/bun.cjs.map +1 -1
  42. package/dist/transform/bun.js +3 -3
  43. package/dist/transform/esbuild.cjs +11 -40
  44. package/dist/transform/esbuild.cjs.map +1 -1
  45. package/dist/transform/esbuild.js +3 -3
  46. package/dist/transform/loaderRuntime.cjs +11 -40
  47. package/dist/transform/loaderRuntime.cjs.map +1 -1
  48. package/dist/transform/loaderRuntime.js +2 -2
  49. package/dist/transform/nest.cjs +11 -40
  50. package/dist/transform/nest.cjs.map +1 -1
  51. package/dist/transform/nest.js +3 -3
  52. package/dist/transform/next.cjs +11 -40
  53. package/dist/transform/next.cjs.map +1 -1
  54. package/dist/transform/next.js +3 -3
  55. package/dist/transform/nodeLoader.js +11 -40
  56. package/dist/transform/nodeLoader.js.map +1 -1
  57. package/dist/transform/rolldown.cjs +11 -40
  58. package/dist/transform/rolldown.cjs.map +1 -1
  59. package/dist/transform/rolldown.js +3 -3
  60. package/dist/transform/rollup.cjs +11 -40
  61. package/dist/transform/rollup.cjs.map +1 -1
  62. package/dist/transform/rollup.js +3 -3
  63. package/dist/transform/rsbuild.cjs +11 -40
  64. package/dist/transform/rsbuild.cjs.map +1 -1
  65. package/dist/transform/rsbuild.js +3 -3
  66. package/dist/transform/rspack.cjs +11 -40
  67. package/dist/transform/rspack.cjs.map +1 -1
  68. package/dist/transform/rspack.js +3 -3
  69. package/dist/transform/swc.cjs +11 -40
  70. package/dist/transform/swc.cjs.map +1 -1
  71. package/dist/transform/swc.js +2 -2
  72. package/dist/transform/unplugin.cjs +11 -40
  73. package/dist/transform/unplugin.cjs.map +1 -1
  74. package/dist/transform/unplugin.js +3 -3
  75. package/dist/transform/vite.cjs +11 -40
  76. package/dist/transform/vite.cjs.map +1 -1
  77. package/dist/transform/vite.js +3 -3
  78. package/dist/transform/webpack.cjs +11 -40
  79. package/dist/transform/webpack.cjs.map +1 -1
  80. package/dist/transform/webpack.js +3 -3
  81. package/package.json +11 -11
  82. package/dist/chunk-3W6RLPGK.js.map +0 -1
  83. package/dist/chunk-GSHCHCB5.js.map +0 -1
  84. package/dist/chunk-SUXEIG4U.js.map +0 -1
  85. package/dist/chunk-YWXQM6J3.js.map +0 -1
  86. /package/dist/{chunk-JIGT4NC5.js.map → chunk-HLH7TN2A.js.map} +0 -0
  87. /package/dist/{chunk-MN3E2RUU.js.map → chunk-PTCLRG4E.js.map} +0 -0
  88. /package/dist/{chunk-IBWW3SP4.js.map → chunk-YVJAOGHE.js.map} +0 -0
  89. /package/dist/{chunk-JXIW4FOF.js.map → chunk-Z26K57WZ.js.map} +0 -0
  90. /package/dist/{http-7LTYEMJO.js.map → http-VN52X47M.js.map} +0 -0
  91. /package/dist/{http-NNVOMI63.js.map → http-WP4YPPKC.js.map} +0 -0
  92. /package/dist/{replay-D7AN6O25.js.map → replay-463SEV53.js.map} +0 -0
  93. /package/dist/{replay-L56SESG7.js.map → replay-OT4A4HVF.js.map} +0 -0
package/cloudReplay.py CHANGED
@@ -116,6 +116,8 @@ PUSH_TRIGGER = re.compile(
116
116
  r"^[ \t]*\"?(?:on\"?[ \t]*:.*\bpush\b|push\"?[ \t]*:)", re.MULTILINE
117
117
  )
118
118
  DISPATCH_INPUTS = ("execution_id", "request", KEY_INPUT)
119
+ PUSH_SUBJECT = "Bitfab replay started on push"
120
+ PUSH_CONDITION = f"github.event_name != 'push' || !startsWith(github.ref_name, '{PREFIX}') || startsWith(github.event.head_commit.message, '{PUSH_SUBJECT}')"
119
121
  BRANCH_NAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9._/-]*")
120
122
  ENV_NAME = re.compile(r"[A-Za-z_][A-Za-z0-9_]*")
121
123
  RESERVED_ENV_PREFIXES = ("GITHUB_", "RUNNER_", "ACTIONS_", "BITFAB_REPLAY_")
@@ -254,7 +256,8 @@ Set up once:
254
256
  [--runs-on LABEL] [--secret-prefix PREFIX] [--check COMMAND]
255
257
  Writes .github/workflows/bitfab-replay.yml, the only file cloud replay keeps
256
258
  in the repository, and lists what is left to do. On an existing workflow it
257
- adds only the lines a newer SDK needs and lists what it changed.
259
+ adds only the lines a newer SDK needs, such as the push trigger that lets a
260
+ replay run without GitHub access, and lists what it changed.
258
261
  --cloud-update [same options] [--remove-old-key-secrets]
259
262
  The same command as --cloud-init, for bringing an existing setup up to date.
260
263
  It also lists BITFAB_REPLAY_KEY_* secrets an older SDK left in the
@@ -266,6 +269,16 @@ Set up once:
266
269
  GitHub access comes from the GitHub CLI when it is logged in, otherwise from GH_TOKEN
267
270
  or GITHUB_TOKEN, otherwise from the github.com credential git already stores. Without
268
271
  the GitHub CLI, --cloud-secrets lists the secrets to create by hand and where.
272
+
273
+ Without GitHub access, or with access that cannot read or push to the repository, a
274
+ replay still runs when git can push the snapshot branch and the workflow also starts
275
+ on a push to a bitfab-replay/ branch, as one written or updated by --cloud-init does.
276
+ This command then warns, prints where to watch the run, and returns once the branch
277
+ is pushed, with no updates and exit code 0. The run uses the workflow's BITFAB_API_KEY
278
+ secret, and --cloud-full-output and --cloud-allow-file are refused, since only GitHub
279
+ access can confirm the repository is private. Delete the snapshot branch once the run
280
+ finishes. With access, a replay starts through the GitHub API as before, and its
281
+ snapshot commit asks GitHub not to start any workflow on the push.
269
282
  """
270
283
 
271
284
 
@@ -281,6 +294,10 @@ class DispatchRejected(ValueError):
281
294
  self.record = record
282
295
 
283
296
 
297
+ class GithubUnreachable(ValueError):
298
+ pass
299
+
300
+
284
301
  class KeepRedirect(urllib.request.HTTPRedirectHandler):
285
302
  def redirect_request(self, *args, **kwargs):
286
303
  return None
@@ -939,7 +956,15 @@ def sensitive(path):
939
956
  )
940
957
 
941
958
 
942
- def snapshot(root, workflow, files, request, *, preview=False, allowed=()):
959
+ def snapshot_subject(execution_id, on_push):
960
+ if on_push:
961
+ return f"{PUSH_SUBJECT} {execution_id}"
962
+ return f"Bitfab replay {execution_id} [skip actions]"
963
+
964
+
965
+ def snapshot(
966
+ root, workflow, files, request, *, preview=False, allowed=(), on_push=False
967
+ ):
943
968
  if git(root, "ls-files", "-u"):
944
969
  raise ValueError("Resolve merge conflicts before snapshotting")
945
970
  head = git(root, "rev-parse", "HEAD")
@@ -1014,7 +1039,7 @@ def snapshot(root, workflow, files, request, *, preview=False, allowed=()):
1014
1039
  tree,
1015
1040
  "-p",
1016
1041
  head,
1017
- input=f"Bitfab replay {request['id']}\n\n{json.dumps(request)}\n",
1042
+ input=f"{snapshot_subject(request['id'], on_push)}\n\n{json.dumps(request)}\n",
1018
1043
  )
1019
1044
  return {**summary, "sha": sha}
1020
1045
 
@@ -1050,9 +1075,8 @@ def execution_lock(directory, execution_id):
1050
1075
  def find_run(record):
1051
1076
  if record.get("snapshot") is False:
1052
1077
  return find_branch_run(record)
1053
- query = urlencode(
1054
- {"event": "workflow_dispatch", "branch": record["branch"], "per_page": 100}
1055
- )
1078
+ event = "push" if record.get("startedOnPush") else "workflow_dispatch"
1079
+ query = urlencode({"event": event, "branch": record["branch"], "per_page": 100})
1056
1080
  runs = api(
1057
1081
  record["repository"], f"actions/workflows/{record['workflow']}/runs?{query}"
1058
1082
  )
@@ -1274,15 +1298,19 @@ def http_status(error):
1274
1298
 
1275
1299
 
1276
1300
  def preflight(repo, workflow, *, pushes=True, full_output=False, allows_files=False):
1277
- access = github_access()
1301
+ try:
1302
+ access = github_access()
1303
+ except ValueError as error:
1304
+ raise GithubUnreachable(str(error)) from error
1278
1305
  try:
1279
1306
  details = api(repo, "")
1280
1307
  except RuntimeError as error:
1281
- raise ValueError(
1308
+ refused = getattr(error, "http_status", None) in (401, 403, 404)
1309
+ raise (GithubUnreachable if refused else ValueError)(
1282
1310
  f"GitHub access from {access['source']} cannot read {repo}{http_status(error)}; use an account or token with write access to it"
1283
1311
  ) from error
1284
1312
  if pushes and (details or {}).get("permissions", {}).get("push") is False:
1285
- raise ValueError(
1313
+ raise GithubUnreachable(
1286
1314
  f"GitHub access from {access['source']} cannot push to {repo}; use an account or token with write access to it"
1287
1315
  )
1288
1316
  if full_output and (details or {}).get("private") is False:
@@ -1344,6 +1372,12 @@ def submit(root, repo, workflow, parsed, path):
1344
1372
  record = json.loads(path.read_text())
1345
1373
  if record["request"] != request or record["repository"].lower() != repo.lower():
1346
1374
  raise ValueError("Execution ID already belongs to a different request")
1375
+ if record.get("startedOnPush") and record["state"] != "prepared":
1376
+ try:
1377
+ github_access()
1378
+ except ValueError as error:
1379
+ record["unfollowed"] = str(error)
1380
+ return record
1347
1381
  status(record, fetch_result=False)
1348
1382
  if record["state"] == "prepared":
1349
1383
  raise ValueError(
@@ -1351,17 +1385,24 @@ def submit(root, repo, workflow, parsed, path):
1351
1385
  )
1352
1386
  refuse_rejected(record)
1353
1387
  return record
1354
- preflight(
1355
- repo,
1356
- workflow,
1357
- full_output=request.get("fullOutput") is True,
1358
- allows_files=bool(allowed),
1359
- )
1388
+ unfollowed = None
1389
+ try:
1390
+ preflight(
1391
+ repo,
1392
+ workflow,
1393
+ full_output=request.get("fullOutput") is True,
1394
+ allows_files=bool(allowed),
1395
+ )
1396
+ except GithubUnreachable as error:
1397
+ unfollowed = str(error)
1398
+ refuse_unfollowed(root, repo, workflow, request, allowed, unfollowed)
1360
1399
  if dry_run(request["args"]):
1361
1400
  progress(
1362
1401
  "--dry-run dispatches a GitHub run that checks secrets and resolves the traces without replaying; it receives the workflow's secrets"
1363
1402
  )
1364
- source = snapshot(root, workflow, files, request, allowed=allowed)
1403
+ source = snapshot(
1404
+ root, workflow, files, request, allowed=allowed, on_push=bool(unfollowed)
1405
+ )
1365
1406
  describe_snapshot(source)
1366
1407
  record = {
1367
1408
  "id": execution_id,
@@ -1370,6 +1411,7 @@ def submit(root, repo, workflow, parsed, path):
1370
1411
  "branch": PREFIX + execution_id,
1371
1412
  "request": request,
1372
1413
  "state": "prepared",
1414
+ **({"startedOnPush": True} if unfollowed else {}),
1373
1415
  **source,
1374
1416
  }
1375
1417
  save(path, record)
@@ -1389,6 +1431,9 @@ def submit(root, repo, workflow, parsed, path):
1389
1431
  ) from error
1390
1432
  record["state"] = "dispatch_unknown"
1391
1433
  save(path, record)
1434
+ if unfollowed:
1435
+ record["unfollowed"] = unfollowed
1436
+ return record
1392
1437
  text = within(root, f"{WORKFLOW_DIRECTORY}/{workflow}").read_text()
1393
1438
  declared = declared_inputs(text)
1394
1439
  key_inputs = issue_key_token(root, record, text)
@@ -1400,6 +1445,62 @@ def submit(root, repo, workflow, parsed, path):
1400
1445
  )
1401
1446
 
1402
1447
 
1448
+ def push_trigger(lines):
1449
+ for index, line in enumerate(lines):
1450
+ if re.fullmatch(r'"?on"?[ \t]*:[ \t]*(#.*)?', line):
1451
+ end = block_end(lines, index, 0)
1452
+ step = child_indent(lines, index, end, " ")
1453
+ row = next(
1454
+ (
1455
+ row
1456
+ for row in range(index + 1, end)
1457
+ if re.fullmatch(rf'{step}"?push"?[ \t]*:.*', lines[row])
1458
+ ),
1459
+ None,
1460
+ )
1461
+ if row is None:
1462
+ return None
1463
+ return lines[row : block_end(lines, row, len(step))]
1464
+ if re.match(r'"?on"?[ \t]*:', line):
1465
+ return [line] if re.search(r"\bpush\b", line) else None
1466
+ return None
1467
+
1468
+
1469
+ def starts_on_push(text):
1470
+ trigger = push_trigger(text.splitlines()) or []
1471
+ return any(PREFIX in line for line in trigger) and not any(
1472
+ "branches-ignore" in line for line in trigger
1473
+ )
1474
+
1475
+
1476
+ def refuse_unfollowed(root, repo, workflow, request, allowed, reason):
1477
+ path = f"{WORKFLOW_DIRECTORY}/{workflow}"
1478
+ if not starts_on_push(within(root, path).read_text()):
1479
+ raise ValueError(
1480
+ f"{reason}. Without it a replay can still run, with no updates here, once {path} also starts on a push to a {PREFIX} branch: run bitfab-replay --cloud-update and commit the workflow it updates"
1481
+ )
1482
+ if request.get("fullOutput") is True:
1483
+ raise ValueError(
1484
+ f"--cloud-full-output writes every trace's input and output into the Actions job log, so it needs GitHub access that confirms {repo} is private: {reason}"
1485
+ )
1486
+ if allowed:
1487
+ raise ValueError(
1488
+ f"{ALLOW_FLAG} uploads a credential-like file to a snapshot branch, so it needs GitHub access that confirms {repo} is private: {reason}"
1489
+ )
1490
+
1491
+
1492
+ def report_unfollowed(record, reason):
1493
+ progress(
1494
+ f"Warning: the replay started on GitHub, but this command gets no updates on it: {reason}"
1495
+ )
1496
+ progress(
1497
+ f"It runs with the workflow's BITFAB_API_KEY secret, since replaying as you needs GitHub access. Watch it at https://github.com/{record['repository']}/actions/workflows/{record['workflow']}; its experiment appears in Bitfab when it finishes"
1498
+ )
1499
+ progress(
1500
+ f"Once it finishes, delete its snapshot branch with git push origin --delete {record['branch']}, or with --cloud-cleanup {record['id']} from a machine with GitHub access"
1501
+ )
1502
+
1503
+
1403
1504
  def local_client(root, request):
1404
1505
  try:
1405
1506
  output = command(
@@ -1736,6 +1837,11 @@ def run_cli(argv):
1736
1837
  raise
1737
1838
  if record.get("preview"):
1738
1839
  return record, None
1840
+ reason = record.pop("unfollowed", None)
1841
+ if reason:
1842
+ report_unfollowed(record, reason)
1843
+ save(path, record)
1844
+ return record, None
1739
1845
  else:
1740
1846
  if not path.exists():
1741
1847
  raise ValueError(
@@ -2791,7 +2897,7 @@ def push_triggers(root, workflow):
2791
2897
 
2792
2898
 
2793
2899
  def workflow_document(steps, run, directory, env, *, runs_on, environment):
2794
- job = {"runs-on": runs_on}
2900
+ job = {"if": PUSH_CONDITION, "runs-on": runs_on}
2795
2901
  if environment:
2796
2902
  job["environment"] = environment
2797
2903
  job["steps"] = [
@@ -2816,7 +2922,8 @@ def workflow_document(steps, run, directory, env, *, runs_on, environment):
2816
2922
  "request": {"type": "string", "required": False},
2817
2923
  KEY_INPUT: {"type": "string", "required": False},
2818
2924
  }
2819
- }
2925
+ },
2926
+ "push": {"branches": [PREFIX + "**"]},
2820
2927
  },
2821
2928
  "permissions": {"contents": "read", "id-token": "write"},
2822
2929
  "jobs": {"replay": job},
@@ -2830,10 +2937,18 @@ def indentation(line):
2830
2937
  def block_end(lines, start, indent):
2831
2938
  end = start + 1
2832
2939
  while end < len(lines) and (
2833
- not lines[end].strip() or indentation(lines[end]) > indent
2940
+ not lines[end].strip()
2941
+ or lines[end].lstrip().startswith("#")
2942
+ or indentation(lines[end]) > indent
2834
2943
  ):
2835
2944
  end += 1
2836
- while end > start + 1 and not lines[end - 1].strip():
2945
+ while end > start + 1 and (
2946
+ not lines[end - 1].strip()
2947
+ or (
2948
+ lines[end - 1].lstrip().startswith("#")
2949
+ and indentation(lines[end - 1]) <= indent
2950
+ )
2951
+ ):
2837
2952
  end -= 1
2838
2953
  return end
2839
2954
 
@@ -2938,19 +3053,67 @@ def add_id_token_permission(lines):
2938
3053
  if not re.fullmatch(r'"?permissions"?[ \t]*:[ \t]*(#.*)?', line):
2939
3054
  continue
2940
3055
  end = block_end(lines, index, 0)
2941
- child = next(
2942
- (
2943
- lines[row][: indentation(lines[row])]
2944
- for row in range(index + 1, end)
2945
- if lines[row].strip()
2946
- ),
2947
- " ",
2948
- )
2949
- lines.insert(end, f"{child}id-token: write")
3056
+ lines.insert(end, f"{child_indent(lines, index, end, ' ')}id-token: write")
2950
3057
  return True
2951
3058
  return False
2952
3059
 
2953
3060
 
3061
+ def holds_yaml(line):
3062
+ return bool(line.strip()) and not line.lstrip().startswith("#")
3063
+
3064
+
3065
+ def child_indent(lines, index, end, fallback):
3066
+ return next(
3067
+ (
3068
+ lines[row][: indentation(lines[row])]
3069
+ for row in range(index + 1, end)
3070
+ if holds_yaml(lines[row])
3071
+ ),
3072
+ fallback,
3073
+ )
3074
+
3075
+
3076
+ def add_push_trigger(lines):
3077
+ for index, line in enumerate(lines):
3078
+ if not re.fullmatch(r'"?on"?[ \t]*:[ \t]*(#.*)?', line):
3079
+ continue
3080
+ end = block_end(lines, index, 0)
3081
+ step = child_indent(lines, index, end, " ")
3082
+ lines[end:end] = [
3083
+ f"{step}push:",
3084
+ f"{step * 2}branches:",
3085
+ f"{step * 3}- {PREFIX}**",
3086
+ ]
3087
+ return True
3088
+ return False
3089
+
3090
+
3091
+ def add_push_condition(lines):
3092
+ jobs = next(
3093
+ (
3094
+ index
3095
+ for index, line in enumerate(lines)
3096
+ if re.fullmatch(r'"?jobs"?[ \t]*:[ \t]*(#.*)?', line)
3097
+ ),
3098
+ None,
3099
+ )
3100
+ if jobs is None:
3101
+ return False
3102
+ end = block_end(lines, jobs, 0)
3103
+ job = next((row for row in range(jobs + 1, end) if holds_yaml(lines[row])), None)
3104
+ if job is None or not lines[job].rstrip().endswith(":"):
3105
+ return False
3106
+ job_end = block_end(lines, job, indentation(lines[job]))
3107
+ step = child_indent(lines, job, job_end, " " * (indentation(lines[job]) + 2))
3108
+ if any(
3109
+ re.fullmatch(rf'{step}"?if"?[ \t]*:.*', lines[row])
3110
+ for row in range(job + 1, job_end)
3111
+ ):
3112
+ return False
3113
+ lines.insert(job + 1, f"{step}if: {json.dumps(PUSH_CONDITION)}")
3114
+ return True
3115
+
3116
+
2954
3117
  def secret_scope(environment):
2955
3118
  return f"environments/{environment}/" if environment else "actions/"
2956
3119
 
@@ -3041,6 +3204,20 @@ def update_workflow(root, path, *, remove_old_key_secrets=False):
3041
3204
  by_hand.append(
3042
3205
  f"{name} (type: string, required: false) under on.workflow_dispatch.inputs"
3043
3206
  )
3207
+ if not starts_on_push(text):
3208
+ if push_trigger(lines) is None and add_push_trigger(lines):
3209
+ updated.append(
3210
+ f"Started the workflow on a push to a {PREFIX} branch too, so a replay runs without GitHub access, with no updates on it"
3211
+ )
3212
+ else:
3213
+ by_hand.append(f"{PREFIX}** in on.push.branches")
3214
+ if push_trigger(lines) is not None and PUSH_SUBJECT not in text:
3215
+ if add_push_condition(lines):
3216
+ updated.append(
3217
+ "Skipped the push run of a replay started through the GitHub API, so each replay runs once"
3218
+ )
3219
+ else:
3220
+ by_hand.append(f"if: {json.dumps(PUSH_CONDITION)} on the replay job")
3044
3221
  if not re.search(r'^"?run-name"?[ \t]*:', text, re.MULTILINE):
3045
3222
  add_run_name(lines)
3046
3223
  updated.append("Named each run after its execution")
@@ -3095,7 +3272,7 @@ def update_workflow(root, path, *, remove_old_key_secrets=False):
3095
3272
  def initialize(argv, name="--cloud-init"):
3096
3273
  parser = argparse.ArgumentParser(
3097
3274
  prog=f"bitfab-replay {name}",
3098
- description="Write .github/workflows/bitfab-replay.yml, the only file cloud replay keeps in the repository, and list what is left to do. It detects the runtime, package manager, install command, and replay command from the directory you run it in. On an existing workflow it only adds what a newer SDK needs (the execution_id and key_id inputs, the run name, and the id-token: write permission), removes the BITFAB_REPLAY_BITFAB_API_KEY line older SDKs wrote, lists what it changed, and lists the BITFAB_REPLAY_KEY_* secrets older SDKs left in the repository; edit it directly for anything else.",
3275
+ description="Write .github/workflows/bitfab-replay.yml, the only file cloud replay keeps in the repository, and list what is left to do. It detects the runtime, package manager, install command, and replay command from the directory you run it in. On an existing workflow it only adds what a newer SDK needs (the execution_id and key_id inputs, the push trigger and the condition that skips a duplicate push run, the run name, and the id-token: write permission), removes the BITFAB_REPLAY_BITFAB_API_KEY line older SDKs wrote, lists what it changed, and lists the BITFAB_REPLAY_KEY_* secrets older SDKs left in the repository; edit it directly for anything else.",
3099
3276
  )
3100
3277
  parser.add_argument(
3101
3278
  "--secret",
@@ -27,6 +27,7 @@ __export(autoTrace_exports, {
27
27
  clearAutoTraceNodeConfiguration: () => clearAutoTraceNodeConfiguration,
28
28
  currentAutoTraceEntries: () => currentAutoTraceEntries,
29
29
  invokeThroughAutoTraceEntries: () => invokeThroughAutoTraceEntries,
30
+ runAsAutoTraceRootFunction: () => runAsAutoTraceRootFunction,
30
31
  runWithAutoTraceEntry: () => runWithAutoTraceEntry,
31
32
  runWithAutoTraceNodeConfiguration: () => runWithAutoTraceNodeConfiguration,
32
33
  runWithAutoTraceRootContext: () => runWithAutoTraceRootContext
@@ -155,6 +156,29 @@ function runWithAutoTraceNodeConfiguration(nodeConfiguration, fn) {
155
156
  }
156
157
  return result;
157
158
  }
159
+ function runAsAutoTraceRootFunction(functionName, fn) {
160
+ const scope = currentAutoTraceScope();
161
+ if (!scope || scope.entries.length === 0 || functionName === "") {
162
+ return fn();
163
+ }
164
+ const rootFunction = {
165
+ name: functionName,
166
+ entering: true
167
+ };
168
+ try {
169
+ return runWithAutoTraceScope({ ...scope, rootFunction }, fn);
170
+ } finally {
171
+ rootFunction.entering = false;
172
+ }
173
+ }
174
+ function isRootFunctionEntry(scope, simpleName) {
175
+ const rootFunction = scope.rootFunction;
176
+ if (rootFunction === void 0 || !rootFunction.entering) {
177
+ return false;
178
+ }
179
+ rootFunction.entering = false;
180
+ return rootFunction.name === simpleName;
181
+ }
158
182
  var SUBTREE_TRACING_DOCS_URL = "https://docs.bitfab.ai/typescript-sdk#experimental-subtree-tracing";
159
183
  function noteTransformedModule() {
160
184
  autoTraceGlobal.__bitfab_auto_trace_transformed_v1__ = true;
@@ -264,6 +288,9 @@ function __bitfabAutoSpan(definition, inputs, fn) {
264
288
  }
265
289
  const nameParts = definition.name.split(".");
266
290
  const simpleName = nameParts[nameParts.length - 1];
291
+ if (isRootFunctionEntry(scope, simpleName)) {
292
+ return fn();
293
+ }
267
294
  const nodeConfiguration = simpleName === scope.nodeConfiguration?.functionName ? shareFinalize(scope.nodeConfiguration) : void 0;
268
295
  return invokeThroughAutoTraceEntries(
269
296
  scope.entries,
@@ -319,6 +346,7 @@ function _resetAutoTraceTransformDetection() {
319
346
  clearAutoTraceNodeConfiguration,
320
347
  currentAutoTraceEntries,
321
348
  invokeThroughAutoTraceEntries,
349
+ runAsAutoTraceRootFunction,
322
350
  runWithAutoTraceEntry,
323
351
  runWithAutoTraceNodeConfiguration,
324
352
  runWithAutoTraceRootContext
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/autoTrace.ts","../src/asyncStorage.ts","../src/captureSuppression.ts","../src/warnOnce.ts"],"sourcesContent":["import {\n type AsyncLocalStorageLike,\n createAsyncLocalStorage,\n} from \"./asyncStorage.js\"\nimport { isCaptureSuppressed } from \"./captureSuppression.js\"\nimport { warnOnce } from \"./warnOnce.js\"\n\nexport interface AutoTraceFunctionDefinition {\n id: string\n name: string\n file: string\n line: number\n column: number\n async?: boolean\n wrapper?: boolean\n}\n\nexport interface AutoTraceNodeConfiguration {\n functionName: string\n name?: string\n type?: \"llm\" | \"agent\" | \"function\" | \"guardrail\" | \"handoff\" | \"custom\"\n capture: boolean\n experimentId?: string\n /** @deprecated Use experimentId. */\n testRunId?: string\n mockOnReplay?: boolean\n replayReusable?: boolean\n // biome-ignore lint/suspicious/noExplicitAny: node finalizers receive the configured function's result, whose type is owned by the caller\n finalize?: (result: any) => unknown | Promise<unknown>\n}\n\nexport interface NestedTraceLink {\n traceFunctionKey: string\n traceId?: string\n rootSpanId?: string\n}\n\nexport interface AutoTraceEntry {\n context: AutoTraceContext\n depth: number\n spanStack: unknown\n}\n\nexport interface AutoTraceContext {\n readonly traceFunctionKey: string\n invoke<T>(\n definition: AutoTraceFunctionDefinition,\n inputs: unknown[],\n fn: () => T,\n entry: AutoTraceEntry,\n nodeConfiguration?: AutoTraceNodeConfiguration,\n ): T\n invokeNestedRoot<T>(\n name: string,\n functionName: string,\n inputs: unknown[],\n fn: () => T,\n entry: AutoTraceEntry,\n link: NestedTraceLink | undefined,\n ): T\n}\n\ninterface AutoTraceScope {\n entries: readonly AutoTraceEntry[]\n nodeConfiguration?: AutoTraceNodeConfiguration\n}\n\ninterface AutoTraceState {\n storage: AsyncLocalStorageLike<AutoTraceScope> | null\n browserScope: AutoTraceScope | undefined\n // Older SDK copies read this field when sharing the process-wide V4 state.\n capturePolicies: WeakMap<object, Map<string, ReadonlySet<string>>>\n activeRoots: number\n}\n\ninterface AutoTraceGlobal {\n __bitfabAutoTraceStateV4?: AutoTraceState\n __bitfab_auto_trace_transformed_v1__?: boolean\n}\n\ninterface AutoTraceAsyncGenerator {\n next(value?: unknown): Promise<IteratorResult<unknown, unknown>>\n return(value?: unknown): Promise<IteratorResult<unknown, unknown>>\n throw(error?: unknown): Promise<IteratorResult<unknown, unknown>>\n [Symbol.asyncIterator](): AutoTraceAsyncGenerator\n}\n\nconst autoTraceGlobal = globalThis as unknown as AutoTraceGlobal\nconst autoTraceState: AutoTraceState =\n autoTraceGlobal.__bitfabAutoTraceStateV4 ?? {\n storage: null,\n browserScope: undefined,\n capturePolicies: new WeakMap(),\n activeRoots: 0,\n }\nautoTraceGlobal.__bitfabAutoTraceStateV4 = autoTraceState\n\nfunction initializeAutoTraceStorage(): void {\n autoTraceState.storage ??= createAsyncLocalStorage<AutoTraceScope>()\n}\n\nfunction runWithAutoTraceScope<T>(scope: AutoTraceScope, fn: () => T): T {\n initializeAutoTraceStorage()\n if (autoTraceState.storage) {\n return autoTraceState.storage.run(scope, fn)\n }\n\n const previous = autoTraceState.browserScope\n autoTraceState.browserScope = scope\n try {\n return fn()\n } finally {\n autoTraceState.browserScope = previous\n }\n}\n\nexport function currentAutoTraceEntries(): readonly AutoTraceEntry[] {\n return currentAutoTraceScope()?.entries ?? []\n}\n\nfunction replaceEntry(\n entries: readonly AutoTraceEntry[],\n previous: AutoTraceEntry,\n updated: AutoTraceEntry,\n): readonly AutoTraceEntry[] {\n return entries.map((entry) => (entry === previous ? updated : entry))\n}\n\nexport function runWithAutoTraceEntry<T>(\n previous: AutoTraceEntry,\n updated: AutoTraceEntry,\n fn: () => T,\n): T {\n const result = runWithAutoTraceScope(\n { entries: replaceEntry(currentAutoTraceEntries(), previous, updated) },\n fn,\n )\n if (isAutoTraceAsyncGenerator(result)) {\n return wrapAutoTraceEntryAsyncGenerator(previous, updated, result) as T\n }\n return result\n}\n\nfunction wrapAutoTraceEntryAsyncGenerator(\n previous: AutoTraceEntry,\n updated: AutoTraceEntry,\n source: AutoTraceAsyncGenerator,\n): AutoTraceAsyncGenerator {\n const step = (\n method: \"next\" | \"return\" | \"throw\",\n value?: unknown,\n ): Promise<IteratorResult<unknown, unknown>> =>\n runWithAutoTraceScope(\n { entries: replaceEntry(currentAutoTraceEntries(), previous, updated) },\n () => source[method](value),\n )\n const wrapped: AutoTraceAsyncGenerator = {\n next: (value) => step(\"next\", value),\n return: (value) => step(\"return\", value),\n throw: (error) => step(\"throw\", error),\n [Symbol.asyncIterator]: () => wrapped,\n }\n return wrapped\n}\n\nexport function clearAutoTraceNodeConfiguration<T>(fn: () => T): T {\n return runWithAutoTraceScope({ entries: currentAutoTraceEntries() }, fn)\n}\n\nexport function runWithAutoTraceNodeConfiguration<T>(\n nodeConfiguration: AutoTraceNodeConfiguration,\n fn: () => T,\n): T {\n const scope = currentAutoTraceScope()\n if (!scope || scope.entries.length === 0) {\n return fn()\n }\n\n const result = runWithAutoTraceScope({ ...scope, nodeConfiguration }, fn)\n if (isAutoTraceAsyncGenerator(result)) {\n return wrapAutoTraceNodeAsyncGenerator(nodeConfiguration, result) as T\n }\n return result\n}\n\nconst SUBTREE_TRACING_DOCS_URL =\n \"https://docs.bitfab.ai/typescript-sdk#experimental-subtree-tracing\"\n\nfunction noteTransformedModule(): void {\n autoTraceGlobal.__bitfab_auto_trace_transformed_v1__ = true\n}\n\nfunction transformInstalled(): boolean {\n return autoTraceGlobal.__bitfab_auto_trace_transformed_v1__ === true\n}\n\nfunction runsOnNode(): boolean {\n return typeof process !== \"undefined\" && Boolean(process.versions?.node)\n}\n\nfunction closeAutoTraceRoot(): void {\n autoTraceState.activeRoots -= 1\n}\n\nfunction settleAutoTraceRoot(context: AutoTraceContext): void {\n closeAutoTraceRoot()\n if (transformInstalled() || !runsOnNode()) {\n return\n }\n warnOnce(\n \"auto-trace-transform-missing\",\n `trace root \"${context.traceFunctionKey}\" recorded only its own span. ` +\n \"No module in this build was compiled by @bitfab/sdk/transform, so calls \" +\n \"beneath the root were not discovered. Add the SDK's transform adapter \" +\n `to the server build: ${SUBTREE_TRACING_DOCS_URL}`,\n )\n}\n\nexport function runWithAutoTraceRootContext<T>(\n context: AutoTraceContext,\n spanStack: unknown,\n fn: () => T,\n): T {\n autoTraceState.activeRoots += 1\n const entries = [\n ...currentAutoTraceEntries(),\n { context, depth: 0, spanStack },\n ]\n let result: T\n try {\n result = runWithAutoTraceScope({ entries }, fn)\n } catch (error) {\n settleAutoTraceRoot(context)\n throw error\n }\n\n if (isAutoTraceAsyncGenerator(result)) {\n closeAutoTraceRoot()\n return wrapAutoTraceAsyncGenerator(context, spanStack, result) as T\n }\n\n if (result instanceof Promise) {\n return result.finally(() => {\n settleAutoTraceRoot(context)\n }) as T\n }\n\n settleAutoTraceRoot(context)\n return result\n}\n\nexport function invokeThroughAutoTraceEntries<T>(\n entries: readonly AutoTraceEntry[],\n step: (entry: AutoTraceEntry, next: () => T) => T,\n fn: () => T,\n): T {\n let run = fn\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index] as AutoTraceEntry\n const next = run\n run = () => step(entry, next)\n }\n return run()\n}\n\nfunction isAutoTraceAsyncGenerator(\n value: unknown,\n): value is AutoTraceAsyncGenerator {\n if (value === null || typeof value !== \"object\") {\n return false\n }\n const candidate = value as Record<PropertyKey, unknown>\n return (\n typeof candidate.next === \"function\" &&\n typeof candidate.return === \"function\" &&\n typeof candidate.throw === \"function\" &&\n typeof candidate[Symbol.asyncIterator] === \"function\"\n )\n}\n\nfunction wrapAutoTraceAsyncGenerator(\n context: AutoTraceContext,\n spanStack: unknown,\n source: AutoTraceAsyncGenerator,\n): AutoTraceAsyncGenerator {\n const step = (\n method: \"next\" | \"return\" | \"throw\",\n value?: unknown,\n ): Promise<IteratorResult<unknown, unknown>> =>\n runWithAutoTraceRootContext(context, spanStack, () => source[method](value))\n const wrapped: AutoTraceAsyncGenerator = {\n next: (value) => step(\"next\", value),\n return: (value) => step(\"return\", value),\n throw: (error) => step(\"throw\", error),\n [Symbol.asyncIterator]: () => wrapped,\n }\n return wrapped\n}\n\nfunction wrapAutoTraceNodeAsyncGenerator(\n nodeConfiguration: AutoTraceNodeConfiguration,\n source: AutoTraceAsyncGenerator,\n): AutoTraceAsyncGenerator {\n const step = (\n method: \"next\" | \"return\" | \"throw\",\n value?: unknown,\n ): Promise<IteratorResult<unknown, unknown>> =>\n runWithAutoTraceNodeConfiguration(nodeConfiguration, () =>\n source[method](value),\n )\n const wrapped: AutoTraceAsyncGenerator = {\n next: (value) => step(\"next\", value),\n return: (value) => step(\"return\", value),\n throw: (error) => step(\"throw\", error),\n [Symbol.asyncIterator]: () => wrapped,\n }\n return wrapped\n}\n\nfunction shareFinalize(\n configuration: AutoTraceNodeConfiguration,\n): AutoTraceNodeConfiguration {\n const finalize = configuration.finalize\n if (finalize === undefined) {\n return configuration\n }\n let settled: Promise<unknown> | undefined\n return {\n ...configuration,\n finalize: (result: unknown) => {\n settled ??= Promise.resolve().then(() => finalize(result))\n return settled\n },\n }\n}\n\nexport function __bitfabAutoSpan<T>(\n definition: AutoTraceFunctionDefinition,\n inputs: unknown[],\n fn: () => T,\n): T {\n noteTransformedModule()\n const scope = currentAutoTraceScope()\n if (!scope || scope.entries.length === 0 || isCaptureSuppressed()) {\n return fn()\n }\n const nameParts = definition.name.split(\".\")\n const simpleName = nameParts[nameParts.length - 1]\n const nodeConfiguration =\n simpleName === scope.nodeConfiguration?.functionName\n ? shareFinalize(scope.nodeConfiguration)\n : undefined\n return invokeThroughAutoTraceEntries(\n scope.entries,\n (entry, next) =>\n entry.context.invoke(definition, inputs, next, entry, nodeConfiguration),\n fn,\n )\n}\n\n/**\n * Preserve a function's original call arguments for transform cases where\n * parameter bindings discard them, such as destructured arrow parameters.\n *\n * This helper is internal transform/runtime protocol. The proxy preserves the\n * target's callability, arity, async identity, and non-constructibility while\n * only allocating a trace closure beneath an active automatic trace root.\n *\n * @experimental The automatic tracing protocol may change.\n */\nexport function __bitfabAutoWrap<T extends (...args: never[]) => unknown>(\n definition: AutoTraceFunctionDefinition,\n fn: T,\n): T {\n noteTransformedModule()\n if (fn.name === \"\") {\n const nameParts = definition.name.split(\".\")\n const inferredName = nameParts[nameParts.length - 1]\n if (inferredName !== undefined) {\n Object.defineProperty(fn, \"name\", {\n configurable: true,\n value: inferredName,\n })\n }\n }\n\n const target = fn as unknown as (...args: unknown[]) => unknown\n return new Proxy(target, {\n apply(callTarget, thisArg, args) {\n if (!__bitfabAutoTraceActive()) {\n return Reflect.apply(callTarget, thisArg, args)\n }\n return __bitfabAutoSpan(definition, args, () =>\n Reflect.apply(callTarget, thisArg, args),\n )\n },\n }) as unknown as T\n}\n\n/**\n * Return whether the current call is inside an automatic trace root.\n *\n * Build transforms use this before allocating function metadata, captured\n * inputs, or an invocation closure. It is internal transform/runtime protocol,\n * not a supported application API.\n *\n * @experimental The automatic tracing protocol may change.\n */\nexport function __bitfabAutoTraceActive(): boolean {\n if (autoTraceState.activeRoots === 0 || isCaptureSuppressed()) {\n return false\n }\n return currentAutoTraceEntries().length > 0\n}\n\nfunction currentAutoTraceScope(): AutoTraceScope | undefined {\n initializeAutoTraceStorage()\n return autoTraceState.storage?.getStore() ?? autoTraceState.browserScope\n}\n\nexport function _resetAutoTraceTransformDetection(): void {\n autoTraceGlobal.__bitfab_auto_trace_transformed_v1__ = undefined\n}\n","/**\n * Shared AsyncLocalStorage loader.\n *\n * Provides two ways to initialize AsyncLocalStorage:\n *\n * 1. **Synchronous registration** (preferred for Node.js):\n * `asyncStorageNode.ts` calls `registerAsyncLocalStorageClass()` at module\n * evaluation time, so the class is available immediately - no async gap.\n * The `node.ts` entry point imports it before anything else.\n *\n * 2. **Async dynamic import** (fallback for the default entry point):\n * Loads `node:async_hooks` via a bundler-safe dynamic import. This is used\n * by the default `index.ts` entry point so the SDK works in browsers\n * (where the import silently fails) and in Node.js when imported via the\n * default entry point.\n *\n * ## Why the dynamic import looks like this\n *\n * We need to handle three environments:\n *\n * 1. **Pure Node.js** - `import(\"node:async_hooks\")` works natively.\n * 2. **Webpack/Turbopack (Next.js server)** - The bundler processes\n * `import()` calls at build time. The `webpackIgnore` magic comment tells\n * webpack (and turbopack) to emit a native `import()` call instead of\n * trying to resolve it, so Node.js handles it at runtime.\n * 3. **Browsers / Edge** - The `process.versions?.node` guard prevents\n * execution entirely. If it somehow runs, `.catch(() => {})` swallows\n * the failure.\n */\n\nexport interface AsyncLocalStorageLike<T> {\n getStore(): T | undefined\n run<R>(store: T, fn: () => R): R\n}\n\nlet AsyncLocalStorageClass: (new () => AsyncLocalStorageLike<unknown>) | null =\n null\nlet initDone = false\n\n/**\n * Register the AsyncLocalStorage class synchronously.\n *\n * Called by `asyncStorageNode.ts` at module evaluation time so the class\n * is available before any span is created - no async gap, no race condition.\n *\n * Safe to call multiple times; subsequent calls are no-ops.\n */\nexport function registerAsyncLocalStorageClass(\n cls: new () => AsyncLocalStorageLike<unknown>,\n): void {\n if (!AsyncLocalStorageClass) {\n AsyncLocalStorageClass = cls\n }\n initDone = true\n}\n\n/**\n * Assert that AsyncLocalStorage was registered successfully.\n *\n * Called by `node.ts` after importing `asyncStorageNode.ts` to catch\n * import-order bugs at startup rather than silently degrading to the\n * browser fallback (flat spans with no nesting).\n *\n * This should ONLY be called from the Node.js entry point where we\n * know `node:async_hooks` must be available.\n */\nexport function assertAsyncStorageRegistered(): void {\n if (!AsyncLocalStorageClass) {\n console.warn(\n \"Bitfab: AsyncLocalStorage not available - nested span context will not propagate.\",\n )\n }\n}\n\nexport const asyncStorageReady: Promise<void> = (\n typeof process !== \"undefined\" && process.versions?.node\n ? // The join trick hides \"node:async_hooks\" from static analysis so\n // bundlers that ban Node.js built-ins don't fail at build time.\n // webpackIgnore tells webpack/turbopack to emit a native import()\n // so Node.js can resolve the module at runtime.\n import(\n /* webpackIgnore: true */\n [\"node\", \"async_hooks\"].join(\":\")\n )\n .then(\n (mod: {\n AsyncLocalStorage: new () => AsyncLocalStorageLike<unknown>\n }) => {\n registerAsyncLocalStorageClass(mod.AsyncLocalStorage)\n },\n )\n .catch(() => {})\n : Promise.resolve()\n).then(() => {\n initDone = true\n})\n\nexport function isAsyncStorageInitDone(): boolean {\n return initDone\n}\n\nexport function createAsyncLocalStorage<T>(): AsyncLocalStorageLike<T> | null {\n return AsyncLocalStorageClass\n ? (new AsyncLocalStorageClass() as AsyncLocalStorageLike<T>)\n : null\n}\n","interface CaptureSuppressionState {\n depth: number\n}\n\ninterface CaptureSuppressionGlobal {\n __bitfabCaptureSuppressionV1?: CaptureSuppressionState\n}\n\nconst suppressionGlobal = globalThis as unknown as CaptureSuppressionGlobal\nconst suppressionState: CaptureSuppressionState =\n suppressionGlobal.__bitfabCaptureSuppressionV1 ?? { depth: 0 }\nsuppressionGlobal.__bitfabCaptureSuppressionV1 = suppressionState\n\nexport function isCaptureSuppressed(): boolean {\n return suppressionState.depth > 0\n}\n\nexport function runWithCaptureSuppressed<T>(fn: () => T): T {\n suppressionState.depth += 1\n try {\n return fn()\n } finally {\n suppressionState.depth -= 1\n }\n}\n","/**\n * Emit a `console.warn` at most once per distinct `key` for the life of the\n * process.\n *\n * The SDK must NEVER crash a host app, so every failure on the user's path\n * degrades silently (a span is dropped, a call runs untraced, a payload is\n * stubbed). Silent is safe but undebuggable: a user who suddenly has no traces,\n * or sees `<unserializable>` in a span, has no signal as to why. A one-time\n * warning per distinct issue restores that signal without spamming the console\n * from a hot path.\n *\n * Keys should identify the specific degradation (e.g. include the traced\n * function key) so each distinct issue warns once, not just the first one seen.\n */\nconst warned = new Set<string>()\n\nexport function warn(message: string): void {\n try {\n console.warn(`[bitfab] ${message}`)\n } catch {\n // Logging must never crash the host app (e.g. a closed/replaced console).\n }\n}\n\nexport function warnOnce(key: string, message: string): void {\n if (warned.has(key)) {\n return\n }\n warned.add(key)\n warn(message)\n}\n\nexport function writeOnce(\n key: string,\n line: string,\n write: (line: string) => void = console.warn,\n): void {\n if (warned.has(key)) {\n return\n }\n warned.add(key)\n try {\n write(line)\n } catch {}\n}\n\n/** Test-only: clear the dedup set so a warning can fire again. */\nexport function _resetWarnOnce(): void {\n warned.clear()\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/autoTrace.ts","../src/asyncStorage.ts","../src/captureSuppression.ts","../src/warnOnce.ts"],"sourcesContent":["import {\n type AsyncLocalStorageLike,\n createAsyncLocalStorage,\n} from \"./asyncStorage.js\"\nimport { isCaptureSuppressed } from \"./captureSuppression.js\"\nimport { warnOnce } from \"./warnOnce.js\"\n\nexport interface AutoTraceFunctionDefinition {\n id: string\n name: string\n file: string\n line: number\n column: number\n async?: boolean\n wrapper?: boolean\n}\n\nexport interface AutoTraceNodeConfiguration {\n functionName: string\n name?: string\n type?: \"llm\" | \"agent\" | \"function\" | \"guardrail\" | \"handoff\" | \"custom\"\n capture: boolean\n experimentId?: string\n /** @deprecated Use experimentId. */\n testRunId?: string\n mockOnReplay?: boolean\n replayReusable?: boolean\n // biome-ignore lint/suspicious/noExplicitAny: node finalizers receive the configured function's result, whose type is owned by the caller\n finalize?: (result: any) => unknown | Promise<unknown>\n}\n\nexport interface NestedTraceLink {\n traceFunctionKey: string\n traceId?: string\n rootSpanId?: string\n}\n\nexport interface AutoTraceEntry {\n context: AutoTraceContext\n depth: number\n spanStack: unknown\n}\n\nexport interface AutoTraceContext {\n readonly traceFunctionKey: string\n invoke<T>(\n definition: AutoTraceFunctionDefinition,\n inputs: unknown[],\n fn: () => T,\n entry: AutoTraceEntry,\n nodeConfiguration?: AutoTraceNodeConfiguration,\n ): T\n invokeNestedRoot<T>(\n name: string,\n functionName: string,\n inputs: unknown[],\n fn: () => T,\n entry: AutoTraceEntry,\n link: NestedTraceLink | undefined,\n ): T\n}\n\ninterface AutoTraceRootFunction {\n name: string\n entering: boolean\n}\n\ninterface AutoTraceScope {\n entries: readonly AutoTraceEntry[]\n nodeConfiguration?: AutoTraceNodeConfiguration\n rootFunction?: AutoTraceRootFunction\n}\n\ninterface AutoTraceState {\n storage: AsyncLocalStorageLike<AutoTraceScope> | null\n browserScope: AutoTraceScope | undefined\n // Older SDK copies read this field when sharing the process-wide V4 state.\n capturePolicies: WeakMap<object, Map<string, ReadonlySet<string>>>\n activeRoots: number\n}\n\ninterface AutoTraceGlobal {\n __bitfabAutoTraceStateV4?: AutoTraceState\n __bitfab_auto_trace_transformed_v1__?: boolean\n}\n\ninterface AutoTraceAsyncGenerator {\n next(value?: unknown): Promise<IteratorResult<unknown, unknown>>\n return(value?: unknown): Promise<IteratorResult<unknown, unknown>>\n throw(error?: unknown): Promise<IteratorResult<unknown, unknown>>\n [Symbol.asyncIterator](): AutoTraceAsyncGenerator\n}\n\nconst autoTraceGlobal = globalThis as unknown as AutoTraceGlobal\nconst autoTraceState: AutoTraceState =\n autoTraceGlobal.__bitfabAutoTraceStateV4 ?? {\n storage: null,\n browserScope: undefined,\n capturePolicies: new WeakMap(),\n activeRoots: 0,\n }\nautoTraceGlobal.__bitfabAutoTraceStateV4 = autoTraceState\n\nfunction initializeAutoTraceStorage(): void {\n autoTraceState.storage ??= createAsyncLocalStorage<AutoTraceScope>()\n}\n\nfunction runWithAutoTraceScope<T>(scope: AutoTraceScope, fn: () => T): T {\n initializeAutoTraceStorage()\n if (autoTraceState.storage) {\n return autoTraceState.storage.run(scope, fn)\n }\n\n const previous = autoTraceState.browserScope\n autoTraceState.browserScope = scope\n try {\n return fn()\n } finally {\n autoTraceState.browserScope = previous\n }\n}\n\nexport function currentAutoTraceEntries(): readonly AutoTraceEntry[] {\n return currentAutoTraceScope()?.entries ?? []\n}\n\nfunction replaceEntry(\n entries: readonly AutoTraceEntry[],\n previous: AutoTraceEntry,\n updated: AutoTraceEntry,\n): readonly AutoTraceEntry[] {\n return entries.map((entry) => (entry === previous ? updated : entry))\n}\n\nexport function runWithAutoTraceEntry<T>(\n previous: AutoTraceEntry,\n updated: AutoTraceEntry,\n fn: () => T,\n): T {\n const result = runWithAutoTraceScope(\n { entries: replaceEntry(currentAutoTraceEntries(), previous, updated) },\n fn,\n )\n if (isAutoTraceAsyncGenerator(result)) {\n return wrapAutoTraceEntryAsyncGenerator(previous, updated, result) as T\n }\n return result\n}\n\nfunction wrapAutoTraceEntryAsyncGenerator(\n previous: AutoTraceEntry,\n updated: AutoTraceEntry,\n source: AutoTraceAsyncGenerator,\n): AutoTraceAsyncGenerator {\n const step = (\n method: \"next\" | \"return\" | \"throw\",\n value?: unknown,\n ): Promise<IteratorResult<unknown, unknown>> =>\n runWithAutoTraceScope(\n { entries: replaceEntry(currentAutoTraceEntries(), previous, updated) },\n () => source[method](value),\n )\n const wrapped: AutoTraceAsyncGenerator = {\n next: (value) => step(\"next\", value),\n return: (value) => step(\"return\", value),\n throw: (error) => step(\"throw\", error),\n [Symbol.asyncIterator]: () => wrapped,\n }\n return wrapped\n}\n\nexport function clearAutoTraceNodeConfiguration<T>(fn: () => T): T {\n return runWithAutoTraceScope({ entries: currentAutoTraceEntries() }, fn)\n}\n\nexport function runWithAutoTraceNodeConfiguration<T>(\n nodeConfiguration: AutoTraceNodeConfiguration,\n fn: () => T,\n): T {\n const scope = currentAutoTraceScope()\n if (!scope || scope.entries.length === 0) {\n return fn()\n }\n\n const result = runWithAutoTraceScope({ ...scope, nodeConfiguration }, fn)\n if (isAutoTraceAsyncGenerator(result)) {\n return wrapAutoTraceNodeAsyncGenerator(nodeConfiguration, result) as T\n }\n return result\n}\n\nexport function runAsAutoTraceRootFunction<T>(\n functionName: string,\n fn: () => T,\n): T {\n const scope = currentAutoTraceScope()\n if (!scope || scope.entries.length === 0 || functionName === \"\") {\n return fn()\n }\n const rootFunction: AutoTraceRootFunction = {\n name: functionName,\n entering: true,\n }\n try {\n return runWithAutoTraceScope({ ...scope, rootFunction }, fn)\n } finally {\n rootFunction.entering = false\n }\n}\n\nfunction isRootFunctionEntry(\n scope: AutoTraceScope,\n simpleName: string | undefined,\n): boolean {\n const rootFunction = scope.rootFunction\n if (rootFunction === undefined || !rootFunction.entering) {\n return false\n }\n rootFunction.entering = false\n return rootFunction.name === simpleName\n}\n\nconst SUBTREE_TRACING_DOCS_URL =\n \"https://docs.bitfab.ai/typescript-sdk#experimental-subtree-tracing\"\n\nfunction noteTransformedModule(): void {\n autoTraceGlobal.__bitfab_auto_trace_transformed_v1__ = true\n}\n\nfunction transformInstalled(): boolean {\n return autoTraceGlobal.__bitfab_auto_trace_transformed_v1__ === true\n}\n\nfunction runsOnNode(): boolean {\n return typeof process !== \"undefined\" && Boolean(process.versions?.node)\n}\n\nfunction closeAutoTraceRoot(): void {\n autoTraceState.activeRoots -= 1\n}\n\nfunction settleAutoTraceRoot(context: AutoTraceContext): void {\n closeAutoTraceRoot()\n if (transformInstalled() || !runsOnNode()) {\n return\n }\n warnOnce(\n \"auto-trace-transform-missing\",\n `trace root \"${context.traceFunctionKey}\" recorded only its own span. ` +\n \"No module in this build was compiled by @bitfab/sdk/transform, so calls \" +\n \"beneath the root were not discovered. Add the SDK's transform adapter \" +\n `to the server build: ${SUBTREE_TRACING_DOCS_URL}`,\n )\n}\n\nexport function runWithAutoTraceRootContext<T>(\n context: AutoTraceContext,\n spanStack: unknown,\n fn: () => T,\n): T {\n autoTraceState.activeRoots += 1\n const entries = [\n ...currentAutoTraceEntries(),\n { context, depth: 0, spanStack },\n ]\n let result: T\n try {\n result = runWithAutoTraceScope({ entries }, fn)\n } catch (error) {\n settleAutoTraceRoot(context)\n throw error\n }\n\n if (isAutoTraceAsyncGenerator(result)) {\n closeAutoTraceRoot()\n return wrapAutoTraceAsyncGenerator(context, spanStack, result) as T\n }\n\n if (result instanceof Promise) {\n return result.finally(() => {\n settleAutoTraceRoot(context)\n }) as T\n }\n\n settleAutoTraceRoot(context)\n return result\n}\n\nexport function invokeThroughAutoTraceEntries<T>(\n entries: readonly AutoTraceEntry[],\n step: (entry: AutoTraceEntry, next: () => T) => T,\n fn: () => T,\n): T {\n let run = fn\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index] as AutoTraceEntry\n const next = run\n run = () => step(entry, next)\n }\n return run()\n}\n\nfunction isAutoTraceAsyncGenerator(\n value: unknown,\n): value is AutoTraceAsyncGenerator {\n if (value === null || typeof value !== \"object\") {\n return false\n }\n const candidate = value as Record<PropertyKey, unknown>\n return (\n typeof candidate.next === \"function\" &&\n typeof candidate.return === \"function\" &&\n typeof candidate.throw === \"function\" &&\n typeof candidate[Symbol.asyncIterator] === \"function\"\n )\n}\n\nfunction wrapAutoTraceAsyncGenerator(\n context: AutoTraceContext,\n spanStack: unknown,\n source: AutoTraceAsyncGenerator,\n): AutoTraceAsyncGenerator {\n const step = (\n method: \"next\" | \"return\" | \"throw\",\n value?: unknown,\n ): Promise<IteratorResult<unknown, unknown>> =>\n runWithAutoTraceRootContext(context, spanStack, () => source[method](value))\n const wrapped: AutoTraceAsyncGenerator = {\n next: (value) => step(\"next\", value),\n return: (value) => step(\"return\", value),\n throw: (error) => step(\"throw\", error),\n [Symbol.asyncIterator]: () => wrapped,\n }\n return wrapped\n}\n\nfunction wrapAutoTraceNodeAsyncGenerator(\n nodeConfiguration: AutoTraceNodeConfiguration,\n source: AutoTraceAsyncGenerator,\n): AutoTraceAsyncGenerator {\n const step = (\n method: \"next\" | \"return\" | \"throw\",\n value?: unknown,\n ): Promise<IteratorResult<unknown, unknown>> =>\n runWithAutoTraceNodeConfiguration(nodeConfiguration, () =>\n source[method](value),\n )\n const wrapped: AutoTraceAsyncGenerator = {\n next: (value) => step(\"next\", value),\n return: (value) => step(\"return\", value),\n throw: (error) => step(\"throw\", error),\n [Symbol.asyncIterator]: () => wrapped,\n }\n return wrapped\n}\n\nfunction shareFinalize(\n configuration: AutoTraceNodeConfiguration,\n): AutoTraceNodeConfiguration {\n const finalize = configuration.finalize\n if (finalize === undefined) {\n return configuration\n }\n let settled: Promise<unknown> | undefined\n return {\n ...configuration,\n finalize: (result: unknown) => {\n settled ??= Promise.resolve().then(() => finalize(result))\n return settled\n },\n }\n}\n\nexport function __bitfabAutoSpan<T>(\n definition: AutoTraceFunctionDefinition,\n inputs: unknown[],\n fn: () => T,\n): T {\n noteTransformedModule()\n const scope = currentAutoTraceScope()\n if (!scope || scope.entries.length === 0 || isCaptureSuppressed()) {\n return fn()\n }\n const nameParts = definition.name.split(\".\")\n const simpleName = nameParts[nameParts.length - 1]\n if (isRootFunctionEntry(scope, simpleName)) {\n return fn()\n }\n const nodeConfiguration =\n simpleName === scope.nodeConfiguration?.functionName\n ? shareFinalize(scope.nodeConfiguration)\n : undefined\n return invokeThroughAutoTraceEntries(\n scope.entries,\n (entry, next) =>\n entry.context.invoke(definition, inputs, next, entry, nodeConfiguration),\n fn,\n )\n}\n\n/**\n * Preserve a function's original call arguments for transform cases where\n * parameter bindings discard them, such as destructured arrow parameters.\n *\n * This helper is internal transform/runtime protocol. The proxy preserves the\n * target's callability, arity, async identity, and non-constructibility while\n * only allocating a trace closure beneath an active automatic trace root.\n *\n * @experimental The automatic tracing protocol may change.\n */\nexport function __bitfabAutoWrap<T extends (...args: never[]) => unknown>(\n definition: AutoTraceFunctionDefinition,\n fn: T,\n): T {\n noteTransformedModule()\n if (fn.name === \"\") {\n const nameParts = definition.name.split(\".\")\n const inferredName = nameParts[nameParts.length - 1]\n if (inferredName !== undefined) {\n Object.defineProperty(fn, \"name\", {\n configurable: true,\n value: inferredName,\n })\n }\n }\n\n const target = fn as unknown as (...args: unknown[]) => unknown\n return new Proxy(target, {\n apply(callTarget, thisArg, args) {\n if (!__bitfabAutoTraceActive()) {\n return Reflect.apply(callTarget, thisArg, args)\n }\n return __bitfabAutoSpan(definition, args, () =>\n Reflect.apply(callTarget, thisArg, args),\n )\n },\n }) as unknown as T\n}\n\n/**\n * Return whether the current call is inside an automatic trace root.\n *\n * Build transforms use this before allocating function metadata, captured\n * inputs, or an invocation closure. It is internal transform/runtime protocol,\n * not a supported application API.\n *\n * @experimental The automatic tracing protocol may change.\n */\nexport function __bitfabAutoTraceActive(): boolean {\n if (autoTraceState.activeRoots === 0 || isCaptureSuppressed()) {\n return false\n }\n return currentAutoTraceEntries().length > 0\n}\n\nfunction currentAutoTraceScope(): AutoTraceScope | undefined {\n initializeAutoTraceStorage()\n return autoTraceState.storage?.getStore() ?? autoTraceState.browserScope\n}\n\nexport function _resetAutoTraceTransformDetection(): void {\n autoTraceGlobal.__bitfab_auto_trace_transformed_v1__ = undefined\n}\n","/**\n * Shared AsyncLocalStorage loader.\n *\n * Provides two ways to initialize AsyncLocalStorage:\n *\n * 1. **Synchronous registration** (preferred for Node.js):\n * `asyncStorageNode.ts` calls `registerAsyncLocalStorageClass()` at module\n * evaluation time, so the class is available immediately - no async gap.\n * The `node.ts` entry point imports it before anything else.\n *\n * 2. **Async dynamic import** (fallback for the default entry point):\n * Loads `node:async_hooks` via a bundler-safe dynamic import. This is used\n * by the default `index.ts` entry point so the SDK works in browsers\n * (where the import silently fails) and in Node.js when imported via the\n * default entry point.\n *\n * ## Why the dynamic import looks like this\n *\n * We need to handle three environments:\n *\n * 1. **Pure Node.js** - `import(\"node:async_hooks\")` works natively.\n * 2. **Webpack/Turbopack (Next.js server)** - The bundler processes\n * `import()` calls at build time. The `webpackIgnore` magic comment tells\n * webpack (and turbopack) to emit a native `import()` call instead of\n * trying to resolve it, so Node.js handles it at runtime.\n * 3. **Browsers / Edge** - The `process.versions?.node` guard prevents\n * execution entirely. If it somehow runs, `.catch(() => {})` swallows\n * the failure.\n */\n\nexport interface AsyncLocalStorageLike<T> {\n getStore(): T | undefined\n run<R>(store: T, fn: () => R): R\n}\n\nlet AsyncLocalStorageClass: (new () => AsyncLocalStorageLike<unknown>) | null =\n null\nlet initDone = false\n\n/**\n * Register the AsyncLocalStorage class synchronously.\n *\n * Called by `asyncStorageNode.ts` at module evaluation time so the class\n * is available before any span is created - no async gap, no race condition.\n *\n * Safe to call multiple times; subsequent calls are no-ops.\n */\nexport function registerAsyncLocalStorageClass(\n cls: new () => AsyncLocalStorageLike<unknown>,\n): void {\n if (!AsyncLocalStorageClass) {\n AsyncLocalStorageClass = cls\n }\n initDone = true\n}\n\n/**\n * Assert that AsyncLocalStorage was registered successfully.\n *\n * Called by `node.ts` after importing `asyncStorageNode.ts` to catch\n * import-order bugs at startup rather than silently degrading to the\n * browser fallback (flat spans with no nesting).\n *\n * This should ONLY be called from the Node.js entry point where we\n * know `node:async_hooks` must be available.\n */\nexport function assertAsyncStorageRegistered(): void {\n if (!AsyncLocalStorageClass) {\n console.warn(\n \"Bitfab: AsyncLocalStorage not available - nested span context will not propagate.\",\n )\n }\n}\n\nexport const asyncStorageReady: Promise<void> = (\n typeof process !== \"undefined\" && process.versions?.node\n ? // The join trick hides \"node:async_hooks\" from static analysis so\n // bundlers that ban Node.js built-ins don't fail at build time.\n // webpackIgnore tells webpack/turbopack to emit a native import()\n // so Node.js can resolve the module at runtime.\n import(\n /* webpackIgnore: true */\n [\"node\", \"async_hooks\"].join(\":\")\n )\n .then(\n (mod: {\n AsyncLocalStorage: new () => AsyncLocalStorageLike<unknown>\n }) => {\n registerAsyncLocalStorageClass(mod.AsyncLocalStorage)\n },\n )\n .catch(() => {})\n : Promise.resolve()\n).then(() => {\n initDone = true\n})\n\nexport function isAsyncStorageInitDone(): boolean {\n return initDone\n}\n\nexport function createAsyncLocalStorage<T>(): AsyncLocalStorageLike<T> | null {\n return AsyncLocalStorageClass\n ? (new AsyncLocalStorageClass() as AsyncLocalStorageLike<T>)\n : null\n}\n","interface CaptureSuppressionState {\n depth: number\n}\n\ninterface CaptureSuppressionGlobal {\n __bitfabCaptureSuppressionV1?: CaptureSuppressionState\n}\n\nconst suppressionGlobal = globalThis as unknown as CaptureSuppressionGlobal\nconst suppressionState: CaptureSuppressionState =\n suppressionGlobal.__bitfabCaptureSuppressionV1 ?? { depth: 0 }\nsuppressionGlobal.__bitfabCaptureSuppressionV1 = suppressionState\n\nexport function isCaptureSuppressed(): boolean {\n return suppressionState.depth > 0\n}\n\nexport function runWithCaptureSuppressed<T>(fn: () => T): T {\n suppressionState.depth += 1\n try {\n return fn()\n } finally {\n suppressionState.depth -= 1\n }\n}\n","/**\n * Emit a `console.warn` at most once per distinct `key` for the life of the\n * process.\n *\n * The SDK must NEVER crash a host app, so every failure on the user's path\n * degrades silently (a span is dropped, a call runs untraced, a payload is\n * stubbed). Silent is safe but undebuggable: a user who suddenly has no traces,\n * or sees `<unserializable>` in a span, has no signal as to why. A one-time\n * warning per distinct issue restores that signal without spamming the console\n * from a hot path.\n *\n * Keys should identify the specific degradation (e.g. include the traced\n * function key) so each distinct issue warns once, not just the first one seen.\n */\nconst warned = new Set<string>()\n\nexport function warn(message: string): void {\n try {\n console.warn(`[bitfab] ${message}`)\n } catch {\n // Logging must never crash the host app (e.g. a closed/replaced console).\n }\n}\n\nexport function warnOnce(key: string, message: string): void {\n if (warned.has(key)) {\n return\n }\n warned.add(key)\n warn(message)\n}\n\nexport function writeOnce(\n key: string,\n line: string,\n write: (line: string) => void = console.warn,\n): void {\n if (warned.has(key)) {\n return\n }\n warned.add(key)\n try {\n write(line)\n } catch {}\n}\n\n/** Test-only: clear the dedup set so a warning can fire again. */\nexport function _resetWarnOnce(): void {\n warned.clear()\n}\n"],"mappings":"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","names":[]}
@@ -38,6 +38,7 @@ declare function currentAutoTraceEntries(): readonly AutoTraceEntry[];
38
38
  declare function runWithAutoTraceEntry<T>(previous: AutoTraceEntry, updated: AutoTraceEntry, fn: () => T): T;
39
39
  declare function clearAutoTraceNodeConfiguration<T>(fn: () => T): T;
40
40
  declare function runWithAutoTraceNodeConfiguration<T>(nodeConfiguration: AutoTraceNodeConfiguration, fn: () => T): T;
41
+ declare function runAsAutoTraceRootFunction<T>(functionName: string, fn: () => T): T;
41
42
  declare function runWithAutoTraceRootContext<T>(context: AutoTraceContext, spanStack: unknown, fn: () => T): T;
42
43
  declare function invokeThroughAutoTraceEntries<T>(entries: readonly AutoTraceEntry[], step: (entry: AutoTraceEntry, next: () => T) => T, fn: () => T): T;
43
44
  declare function __bitfabAutoSpan<T>(definition: AutoTraceFunctionDefinition, inputs: unknown[], fn: () => T): T;
@@ -64,4 +65,4 @@ declare function __bitfabAutoWrap<T extends (...args: never[]) => unknown>(defin
64
65
  declare function __bitfabAutoTraceActive(): boolean;
65
66
  declare function _resetAutoTraceTransformDetection(): void;
66
67
 
67
- export { type AutoTraceContext, type AutoTraceEntry, type AutoTraceFunctionDefinition, type AutoTraceNodeConfiguration, type NestedTraceLink, __bitfabAutoSpan, __bitfabAutoTraceActive, __bitfabAutoWrap, _resetAutoTraceTransformDetection, clearAutoTraceNodeConfiguration, currentAutoTraceEntries, invokeThroughAutoTraceEntries, runWithAutoTraceEntry, runWithAutoTraceNodeConfiguration, runWithAutoTraceRootContext };
68
+ export { type AutoTraceContext, type AutoTraceEntry, type AutoTraceFunctionDefinition, type AutoTraceNodeConfiguration, type NestedTraceLink, __bitfabAutoSpan, __bitfabAutoTraceActive, __bitfabAutoWrap, _resetAutoTraceTransformDetection, clearAutoTraceNodeConfiguration, currentAutoTraceEntries, invokeThroughAutoTraceEntries, runAsAutoTraceRootFunction, runWithAutoTraceEntry, runWithAutoTraceNodeConfiguration, runWithAutoTraceRootContext };
@@ -38,6 +38,7 @@ declare function currentAutoTraceEntries(): readonly AutoTraceEntry[];
38
38
  declare function runWithAutoTraceEntry<T>(previous: AutoTraceEntry, updated: AutoTraceEntry, fn: () => T): T;
39
39
  declare function clearAutoTraceNodeConfiguration<T>(fn: () => T): T;
40
40
  declare function runWithAutoTraceNodeConfiguration<T>(nodeConfiguration: AutoTraceNodeConfiguration, fn: () => T): T;
41
+ declare function runAsAutoTraceRootFunction<T>(functionName: string, fn: () => T): T;
41
42
  declare function runWithAutoTraceRootContext<T>(context: AutoTraceContext, spanStack: unknown, fn: () => T): T;
42
43
  declare function invokeThroughAutoTraceEntries<T>(entries: readonly AutoTraceEntry[], step: (entry: AutoTraceEntry, next: () => T) => T, fn: () => T): T;
43
44
  declare function __bitfabAutoSpan<T>(definition: AutoTraceFunctionDefinition, inputs: unknown[], fn: () => T): T;
@@ -64,4 +65,4 @@ declare function __bitfabAutoWrap<T extends (...args: never[]) => unknown>(defin
64
65
  declare function __bitfabAutoTraceActive(): boolean;
65
66
  declare function _resetAutoTraceTransformDetection(): void;
66
67
 
67
- export { type AutoTraceContext, type AutoTraceEntry, type AutoTraceFunctionDefinition, type AutoTraceNodeConfiguration, type NestedTraceLink, __bitfabAutoSpan, __bitfabAutoTraceActive, __bitfabAutoWrap, _resetAutoTraceTransformDetection, clearAutoTraceNodeConfiguration, currentAutoTraceEntries, invokeThroughAutoTraceEntries, runWithAutoTraceEntry, runWithAutoTraceNodeConfiguration, runWithAutoTraceRootContext };
68
+ export { type AutoTraceContext, type AutoTraceEntry, type AutoTraceFunctionDefinition, type AutoTraceNodeConfiguration, type NestedTraceLink, __bitfabAutoSpan, __bitfabAutoTraceActive, __bitfabAutoWrap, _resetAutoTraceTransformDetection, clearAutoTraceNodeConfiguration, currentAutoTraceEntries, invokeThroughAutoTraceEntries, runAsAutoTraceRootFunction, runWithAutoTraceEntry, runWithAutoTraceNodeConfiguration, runWithAutoTraceRootContext };
package/dist/autoTrace.js CHANGED
@@ -6,10 +6,11 @@ import {
6
6
  clearAutoTraceNodeConfiguration,
7
7
  currentAutoTraceEntries,
8
8
  invokeThroughAutoTraceEntries,
9
+ runAsAutoTraceRootFunction,
9
10
  runWithAutoTraceEntry,
10
11
  runWithAutoTraceNodeConfiguration,
11
12
  runWithAutoTraceRootContext
12
- } from "./chunk-YWXQM6J3.js";
13
+ } from "./chunk-QRNHRY7F.js";
13
14
  import "./chunk-MT36GJMW.js";
14
15
  export {
15
16
  __bitfabAutoSpan,
@@ -19,6 +20,7 @@ export {
19
20
  clearAutoTraceNodeConfiguration,
20
21
  currentAutoTraceEntries,
21
22
  invokeThroughAutoTraceEntries,
23
+ runAsAutoTraceRootFunction,
22
24
  runWithAutoTraceEntry,
23
25
  runWithAutoTraceNodeConfiguration,
24
26
  runWithAutoTraceRootContext
@@ -6,13 +6,13 @@ import {
6
6
  serializeReplayResult,
7
7
  warnJudgeAssertionsDeprecated,
8
8
  withCliFlags
9
- } from "./chunk-JXIW4FOF.js";
9
+ } from "./chunk-Z26K57WZ.js";
10
10
  import {
11
11
  BitfabError,
12
12
  asyncStorageReady,
13
13
  createAsyncLocalStorage,
14
14
  warnOnce
15
- } from "./chunk-WW2RMMTA.js";
15
+ } from "./chunk-CLP2L5PU.js";
16
16
 
17
17
  // src/autoTrace.ts
18
18
  var autoTraceGlobal = globalThis;
@@ -458,7 +458,7 @@ async function seedFromRegistry(registry, pipeline, cases, options = {}) {
458
458
  sessionId: seedCase.sessionId
459
459
  })
460
460
  );
461
- const { flushTraces } = await import("./http-NNVOMI63.js");
461
+ const { flushTraces } = await import("./http-WP4YPPKC.js");
462
462
  await flushTraces(3e4);
463
463
  return { pipeline, traceFunctionKey, traceIds };
464
464
  }
@@ -1173,4 +1173,4 @@ export {
1173
1173
  runRegistryMain,
1174
1174
  runIfEntrypoint
1175
1175
  };
1176
- //# sourceMappingURL=chunk-3W6RLPGK.js.map
1176
+ //# sourceMappingURL=chunk-BRMGNMDO.js.map