@hominis/fireforge 0.45.0 → 0.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (662) hide show
  1. package/CHANGELOG.md +409 -446
  2. package/dist/bin/fireforge.d.ts +1 -1
  3. package/dist/bin/fireforge.js +21 -15
  4. package/dist/build-info.json +4 -4
  5. package/dist/src/cli-usage-error.d.ts +46 -0
  6. package/dist/src/cli-usage-error.js +81 -0
  7. package/dist/src/cli.d.ts +7 -2
  8. package/dist/src/cli.js +47 -47
  9. package/dist/src/commands/bootstrap-checks.js +2 -1
  10. package/dist/src/commands/bootstrap.js +3 -4
  11. package/dist/src/commands/build.js +27 -17
  12. package/dist/src/commands/config.js +9 -9
  13. package/dist/src/commands/destructive-decision.d.ts +19 -0
  14. package/dist/src/commands/destructive-decision.js +31 -0
  15. package/dist/src/commands/discard.js +5 -5
  16. package/dist/src/commands/doctor-check-core.d.ts +4 -10
  17. package/dist/src/commands/doctor-check-core.js +0 -8
  18. package/dist/src/commands/doctor-external-toolchains.js +2 -2
  19. package/dist/src/commands/doctor-furnace-config-sync.d.ts +2 -2
  20. package/dist/src/commands/doctor-furnace-config-sync.js +20 -20
  21. package/dist/src/commands/doctor-furnace-jar.d.ts +1 -1
  22. package/dist/src/commands/doctor-furnace-jar.js +1 -1
  23. package/dist/src/commands/doctor-furnace.js +22 -22
  24. package/dist/src/commands/doctor-orphaned-harness.d.ts +10 -17
  25. package/dist/src/commands/doctor-orphaned-harness.js +14 -23
  26. package/dist/src/commands/doctor-patch-manifest.d.ts +2 -1
  27. package/dist/src/commands/doctor-patch-manifest.js +7 -6
  28. package/dist/src/commands/doctor-post-rebase-audit.js +3 -3
  29. package/dist/src/commands/doctor-source-pin.d.ts +12 -12
  30. package/dist/src/commands/doctor-source-pin.js +14 -14
  31. package/dist/src/commands/doctor-working-tree.d.ts +3 -3
  32. package/dist/src/commands/doctor-working-tree.js +3 -3
  33. package/dist/src/commands/doctor.d.ts +3 -1
  34. package/dist/src/commands/doctor.js +15 -16
  35. package/dist/src/commands/download.js +50 -37
  36. package/dist/src/commands/export-all.js +29 -12
  37. package/dist/src/commands/export-flow.d.ts +5 -5
  38. package/dist/src/commands/export-flow.js +29 -59
  39. package/dist/src/commands/export-placement-conflicts.d.ts +3 -3
  40. package/dist/src/commands/export-placement-conflicts.js +12 -12
  41. package/dist/src/commands/export-placement-gate.d.ts +2 -2
  42. package/dist/src/commands/export-placement-gate.js +7 -13
  43. package/dist/src/commands/export-placement-policy.d.ts +1 -1
  44. package/dist/src/commands/export-placement-policy.js +4 -6
  45. package/dist/src/commands/export-shared.d.ts +51 -32
  46. package/dist/src/commands/export-shared.js +17 -32
  47. package/dist/src/commands/export.js +14 -5
  48. package/dist/src/commands/furnace/apply.js +16 -16
  49. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  50. package/dist/src/commands/furnace/authoring-config.js +19 -0
  51. package/dist/src/commands/furnace/chrome-doc-remove.d.ts +1 -1
  52. package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
  53. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +14 -14
  54. package/dist/src/commands/furnace/chrome-doc-templates.js +17 -17
  55. package/dist/src/commands/furnace/chrome-doc-tests.d.ts +2 -2
  56. package/dist/src/commands/furnace/chrome-doc-tests.js +4 -4
  57. package/dist/src/commands/furnace/chrome-doc.d.ts +8 -8
  58. package/dist/src/commands/furnace/chrome-doc.js +18 -18
  59. package/dist/src/commands/furnace/create-browser-test.d.ts +1 -1
  60. package/dist/src/commands/furnace/create-browser-test.js +7 -12
  61. package/dist/src/commands/furnace/create-dry-run.d.ts +4 -4
  62. package/dist/src/commands/furnace/create-dry-run.js +6 -9
  63. package/dist/src/commands/furnace/create-features.d.ts +2 -2
  64. package/dist/src/commands/furnace/create-features.js +2 -2
  65. package/dist/src/commands/furnace/create-mochikit.d.ts +4 -4
  66. package/dist/src/commands/furnace/create-mochikit.js +7 -7
  67. package/dist/src/commands/furnace/create-templates.d.ts +31 -15
  68. package/dist/src/commands/furnace/create-templates.js +20 -19
  69. package/dist/src/commands/furnace/create-xpcshell.d.ts +5 -5
  70. package/dist/src/commands/furnace/create-xpcshell.js +8 -8
  71. package/dist/src/commands/furnace/create.d.ts +1 -1
  72. package/dist/src/commands/furnace/create.js +35 -29
  73. package/dist/src/commands/furnace/deploy.js +15 -6
  74. package/dist/src/commands/furnace/diff.js +7 -7
  75. package/dist/src/commands/furnace/index.js +1 -1
  76. package/dist/src/commands/furnace/init.js +17 -16
  77. package/dist/src/commands/furnace/list.js +2 -2
  78. package/dist/src/commands/furnace/override.d.ts +1 -1
  79. package/dist/src/commands/furnace/override.js +43 -34
  80. package/dist/src/commands/furnace/preview.js +17 -17
  81. package/dist/src/commands/furnace/refresh.js +15 -7
  82. package/dist/src/commands/furnace/remove-mutations.d.ts +9 -10
  83. package/dist/src/commands/furnace/remove-mutations.js +19 -19
  84. package/dist/src/commands/furnace/remove-state.d.ts +1 -1
  85. package/dist/src/commands/furnace/remove-state.js +1 -1
  86. package/dist/src/commands/furnace/remove.js +4 -8
  87. package/dist/src/commands/furnace/rename-browser-test.js +10 -15
  88. package/dist/src/commands/furnace/rename-xpcshell.d.ts +2 -2
  89. package/dist/src/commands/furnace/rename-xpcshell.js +3 -3
  90. package/dist/src/commands/furnace/rename.js +36 -36
  91. package/dist/src/commands/furnace/scan.js +3 -3
  92. package/dist/src/commands/furnace/status.js +19 -16
  93. package/dist/src/commands/furnace/sync.js +6 -6
  94. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  95. package/dist/src/commands/furnace/test-file-name.js +39 -0
  96. package/dist/src/commands/furnace/validate.js +11 -12
  97. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  98. package/dist/src/commands/furnace/validation-output.js +3 -8
  99. package/dist/src/commands/import.js +22 -29
  100. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  101. package/dist/src/commands/lint-per-patch.js +27 -12
  102. package/dist/src/commands/lint-per-run-checkjs.d.ts +14 -15
  103. package/dist/src/commands/lint-per-run-checkjs.js +14 -15
  104. package/dist/src/commands/lint-report.d.ts +3 -3
  105. package/dist/src/commands/lint-report.js +4 -4
  106. package/dist/src/commands/lint.d.ts +5 -4
  107. package/dist/src/commands/lint.js +36 -36
  108. package/dist/src/commands/manifest.d.ts +4 -4
  109. package/dist/src/commands/manifest.js +1 -1
  110. package/dist/src/commands/package.js +4 -6
  111. package/dist/src/commands/patch/compact.d.ts +2 -2
  112. package/dist/src/commands/patch/compact.js +19 -20
  113. package/dist/src/commands/patch/delete.d.ts +1 -1
  114. package/dist/src/commands/patch/delete.js +23 -33
  115. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  116. package/dist/src/commands/patch/entry-projection.js +24 -0
  117. package/dist/src/commands/patch/lint-ignore.d.ts +3 -12
  118. package/dist/src/commands/patch/lint-ignore.js +33 -44
  119. package/dist/src/commands/patch/move-files-create.d.ts +2 -2
  120. package/dist/src/commands/patch/move-files-create.js +29 -15
  121. package/dist/src/commands/patch/move-files-into.d.ts +2 -2
  122. package/dist/src/commands/patch/move-files-into.js +41 -37
  123. package/dist/src/commands/patch/move-files.js +5 -14
  124. package/dist/src/commands/patch/patch-context.d.ts +1 -1
  125. package/dist/src/commands/patch/rename.d.ts +4 -4
  126. package/dist/src/commands/patch/rename.js +35 -49
  127. package/dist/src/commands/patch/reorder.js +35 -48
  128. package/dist/src/commands/patch/split-plan.d.ts +7 -8
  129. package/dist/src/commands/patch/split-plan.js +10 -13
  130. package/dist/src/commands/patch/split.d.ts +2 -2
  131. package/dist/src/commands/patch/split.js +50 -48
  132. package/dist/src/commands/patch/staged-dependency-validate.d.ts +1 -1
  133. package/dist/src/commands/patch/staged-dependency-validate.js +3 -3
  134. package/dist/src/commands/patch/staged-dependency.d.ts +1 -1
  135. package/dist/src/commands/patch/staged-dependency.js +22 -28
  136. package/dist/src/commands/patch/tier.d.ts +2 -2
  137. package/dist/src/commands/patch/tier.js +20 -30
  138. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  139. package/dist/src/commands/patch-rename-map.js +36 -0
  140. package/dist/src/commands/pending-resolution.d.ts +15 -0
  141. package/dist/src/commands/pending-resolution.js +25 -0
  142. package/dist/src/commands/re-export-adjacent.d.ts +25 -3
  143. package/dist/src/commands/re-export-adjacent.js +40 -24
  144. package/dist/src/commands/re-export-bulk-scan.d.ts +3 -3
  145. package/dist/src/commands/re-export-bulk-scan.js +23 -27
  146. package/dist/src/commands/re-export-drift.d.ts +10 -10
  147. package/dist/src/commands/re-export-drift.js +33 -33
  148. package/dist/src/commands/re-export-files.d.ts +2 -3
  149. package/dist/src/commands/re-export-files.js +52 -44
  150. package/dist/src/commands/re-export-lint.d.ts +29 -30
  151. package/dist/src/commands/re-export-lint.js +36 -29
  152. package/dist/src/commands/re-export-options.d.ts +1 -1
  153. package/dist/src/commands/re-export-options.js +1 -1
  154. package/dist/src/commands/re-export-register.d.ts +1 -1
  155. package/dist/src/commands/re-export-register.js +1 -1
  156. package/dist/src/commands/re-export-scan.d.ts +1 -1
  157. package/dist/src/commands/re-export-scan.js +12 -9
  158. package/dist/src/commands/re-export-single.d.ts +23 -4
  159. package/dist/src/commands/re-export-single.js +39 -31
  160. package/dist/src/commands/re-export.js +35 -17
  161. package/dist/src/commands/rebase/abort.js +9 -14
  162. package/dist/src/commands/rebase/continue.js +20 -19
  163. package/dist/src/commands/rebase/index.d.ts +1 -1
  164. package/dist/src/commands/rebase/index.js +5 -5
  165. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  166. package/dist/src/commands/rebase/patch-loop.js +20 -24
  167. package/dist/src/commands/register.js +5 -5
  168. package/dist/src/commands/reset.js +1 -1
  169. package/dist/src/commands/resolve.d.ts +2 -2
  170. package/dist/src/commands/resolve.js +21 -13
  171. package/dist/src/commands/run.d.ts +0 -13
  172. package/dist/src/commands/run.js +38 -51
  173. package/dist/src/commands/setup-support.d.ts +16 -4
  174. package/dist/src/commands/setup-support.js +20 -20
  175. package/dist/src/commands/setup.js +4 -10
  176. package/dist/src/commands/source.js +9 -2
  177. package/dist/src/commands/status-check.d.ts +3 -3
  178. package/dist/src/commands/status-check.js +6 -6
  179. package/dist/src/commands/status-json.d.ts +14 -13
  180. package/dist/src/commands/status-json.js +9 -8
  181. package/dist/src/commands/status-output.d.ts +3 -3
  182. package/dist/src/commands/status-output.js +5 -5
  183. package/dist/src/commands/status-ownership.d.ts +3 -3
  184. package/dist/src/commands/status-ownership.js +3 -3
  185. package/dist/src/commands/status.js +25 -26
  186. package/dist/src/commands/test-build-phase.d.ts +3 -3
  187. package/dist/src/commands/test-build-phase.js +48 -24
  188. package/dist/src/commands/test-diagnose.d.ts +1 -1
  189. package/dist/src/commands/test-diagnose.js +20 -20
  190. package/dist/src/commands/test-modes.d.ts +4 -4
  191. package/dist/src/commands/test-modes.js +4 -5
  192. package/dist/src/commands/test-register.js +5 -3
  193. package/dist/src/commands/test-run.d.ts +11 -11
  194. package/dist/src/commands/test-run.js +40 -33
  195. package/dist/src/commands/test-stale-gate.d.ts +12 -12
  196. package/dist/src/commands/test-stale-gate.js +17 -17
  197. package/dist/src/commands/test-verdict.d.ts +19 -5
  198. package/dist/src/commands/test-verdict.js +26 -11
  199. package/dist/src/commands/test.d.ts +3 -3
  200. package/dist/src/commands/test.js +73 -36
  201. package/dist/src/commands/token-coverage.js +3 -3
  202. package/dist/src/commands/token-list.d.ts +26 -0
  203. package/dist/src/commands/token-list.js +153 -0
  204. package/dist/src/commands/token.js +26 -10
  205. package/dist/src/commands/tree.js +26 -21
  206. package/dist/src/commands/typecheck.d.ts +6 -6
  207. package/dist/src/commands/typecheck.js +16 -16
  208. package/dist/src/commands/verify.d.ts +3 -3
  209. package/dist/src/commands/verify.js +10 -10
  210. package/dist/src/commands/watch.js +11 -11
  211. package/dist/src/commands/wire.js +17 -16
  212. package/dist/src/core/ast-utils.d.ts +7 -7
  213. package/dist/src/core/ast-utils.js +8 -8
  214. package/dist/src/core/branding.d.ts +5 -12
  215. package/dist/src/core/branding.js +6 -5
  216. package/dist/src/core/browser-wire.d.ts +1 -1
  217. package/dist/src/core/browser-wire.js +10 -7
  218. package/dist/src/core/build-audit-platform.d.ts +32 -3
  219. package/dist/src/core/build-audit-platform.js +153 -13
  220. package/dist/src/core/build-audit-registration.d.ts +5 -5
  221. package/dist/src/core/build-audit-registration.js +10 -9
  222. package/dist/src/core/build-audit-resolve.d.ts +5 -5
  223. package/dist/src/core/build-audit-resolve.js +6 -6
  224. package/dist/src/core/build-audit-skip.d.ts +57 -0
  225. package/dist/src/core/build-audit-skip.js +112 -0
  226. package/dist/src/core/build-audit-transforms.d.ts +2 -2
  227. package/dist/src/core/build-audit-transforms.js +3 -3
  228. package/dist/src/core/build-audit.d.ts +21 -8
  229. package/dist/src/core/build-audit.js +142 -40
  230. package/dist/src/core/build-baseline-types.d.ts +19 -18
  231. package/dist/src/core/build-baseline-types.js +1 -1
  232. package/dist/src/core/build-baseline.d.ts +48 -31
  233. package/dist/src/core/build-baseline.js +20 -43
  234. package/dist/src/core/build-jar-escalation.d.ts +8 -8
  235. package/dist/src/core/build-jar-escalation.js +18 -17
  236. package/dist/src/core/build-overwrite-guard.d.ts +4 -4
  237. package/dist/src/core/build-overwrite-guard.js +14 -14
  238. package/dist/src/core/build-prepare.d.ts +4 -12
  239. package/dist/src/core/build-prepare.js +22 -26
  240. package/dist/src/core/config-mutate.js +4 -4
  241. package/dist/src/core/config-paths.d.ts +2 -6
  242. package/dist/src/core/config-paths.js +4 -2
  243. package/dist/src/core/config-state.d.ts +0 -6
  244. package/dist/src/core/config-state.js +3 -21
  245. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  246. package/dist/src/core/config-validate-build-audit.js +80 -0
  247. package/dist/src/core/config-validate-file-size.d.ts +1 -1
  248. package/dist/src/core/config-validate-file-size.js +3 -3
  249. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  250. package/dist/src/core/config-validate-patch-policy.js +3 -3
  251. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  252. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  253. package/dist/src/core/config-validate.js +22 -12
  254. package/dist/src/core/config.d.ts +12 -12
  255. package/dist/src/core/config.js +21 -25
  256. package/dist/src/core/coverage-extend.d.ts +16 -16
  257. package/dist/src/core/coverage-extend.js +20 -20
  258. package/dist/src/core/destructive.d.ts +14 -14
  259. package/dist/src/core/destructive.js +9 -9
  260. package/dist/src/core/diff-hunks.d.ts +2 -2
  261. package/dist/src/core/diff-hunks.js +7 -7
  262. package/dist/src/core/discard-baseline.d.ts +10 -10
  263. package/dist/src/core/discard-baseline.js +22 -23
  264. package/dist/src/core/display-state.d.ts +11 -11
  265. package/dist/src/core/display-state.js +12 -12
  266. package/dist/src/core/engine-changes.d.ts +2 -2
  267. package/dist/src/core/engine-changes.js +2 -2
  268. package/dist/src/core/engine-precondition.d.ts +5 -13
  269. package/dist/src/core/engine-precondition.js +5 -10
  270. package/dist/src/core/engine-session-lock.d.ts +18 -18
  271. package/dist/src/core/engine-session-lock.js +50 -54
  272. package/dist/src/core/export-moved-code-shape.d.ts +7 -7
  273. package/dist/src/core/export-moved-code-shape.js +10 -10
  274. package/dist/src/core/file-lock-owner.d.ts +103 -0
  275. package/dist/src/core/file-lock-owner.js +259 -0
  276. package/dist/src/core/file-lock.d.ts +14 -19
  277. package/dist/src/core/file-lock.js +90 -128
  278. package/dist/src/core/firefox-cache.d.ts +13 -1
  279. package/dist/src/core/firefox-cache.js +61 -36
  280. package/dist/src/core/firefox-download.d.ts +15 -0
  281. package/dist/src/core/firefox-download.js +38 -22
  282. package/dist/src/core/firefox-extract.d.ts +8 -7
  283. package/dist/src/core/firefox-extract.js +45 -27
  284. package/dist/src/core/firefox.d.ts +34 -36
  285. package/dist/src/core/firefox.js +13 -44
  286. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  287. package/dist/src/core/furnace-apply-dry-run.js +2 -8
  288. package/dist/src/core/furnace-apply-ftl.d.ts +5 -5
  289. package/dist/src/core/furnace-apply-ftl.js +8 -8
  290. package/dist/src/core/furnace-apply-helpers.d.ts +9 -20
  291. package/dist/src/core/furnace-apply-helpers.js +28 -39
  292. package/dist/src/core/furnace-apply-output.js +1 -1
  293. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +9 -9
  294. package/dist/src/core/furnace-apply-overwrite-warn.js +13 -11
  295. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +1 -1
  296. package/dist/src/core/furnace-apply-undeploy-actions.js +1 -1
  297. package/dist/src/core/furnace-apply.d.ts +4 -4
  298. package/dist/src/core/furnace-apply.js +50 -29
  299. package/dist/src/core/furnace-checksum-utils.d.ts +5 -5
  300. package/dist/src/core/furnace-checksum-utils.js +5 -5
  301. package/dist/src/core/furnace-config-custom.d.ts +1 -1
  302. package/dist/src/core/furnace-config-custom.js +2 -2
  303. package/dist/src/core/furnace-config-order.js +1 -1
  304. package/dist/src/core/furnace-config-tokens.d.ts +3 -3
  305. package/dist/src/core/furnace-config-tokens.js +3 -3
  306. package/dist/src/core/furnace-config-validate.js +2 -2
  307. package/dist/src/core/furnace-config.d.ts +5 -5
  308. package/dist/src/core/furnace-config.js +40 -53
  309. package/dist/src/core/furnace-constants.d.ts +6 -6
  310. package/dist/src/core/furnace-constants.js +9 -8
  311. package/dist/src/core/furnace-css-fragments.d.ts +6 -6
  312. package/dist/src/core/furnace-css-fragments.js +11 -11
  313. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  314. package/dist/src/core/furnace-dir-entry.js +21 -0
  315. package/dist/src/core/furnace-jsconfig.d.ts +7 -7
  316. package/dist/src/core/furnace-jsconfig.js +22 -20
  317. package/dist/src/core/furnace-operation.d.ts +22 -19
  318. package/dist/src/core/furnace-operation.js +38 -34
  319. package/dist/src/core/furnace-precondition.d.ts +10 -10
  320. package/dist/src/core/furnace-precondition.js +8 -8
  321. package/dist/src/core/furnace-refresh.d.ts +18 -6
  322. package/dist/src/core/furnace-refresh.js +5 -8
  323. package/dist/src/core/furnace-registration-ast.d.ts +2 -3
  324. package/dist/src/core/furnace-registration-ast.js +9 -12
  325. package/dist/src/core/furnace-registration-remove.d.ts +4 -4
  326. package/dist/src/core/furnace-registration-remove.js +6 -6
  327. package/dist/src/core/furnace-registration-validate.d.ts +2 -2
  328. package/dist/src/core/furnace-registration-validate.js +2 -2
  329. package/dist/src/core/furnace-registration.d.ts +7 -6
  330. package/dist/src/core/furnace-registration.js +29 -36
  331. package/dist/src/core/furnace-rollback.d.ts +1 -1
  332. package/dist/src/core/furnace-rollback.js +5 -5
  333. package/dist/src/core/furnace-scanner.js +3 -6
  334. package/dist/src/core/furnace-stale-export.d.ts +17 -16
  335. package/dist/src/core/furnace-stale-export.js +16 -15
  336. package/dist/src/core/furnace-staleness.d.ts +2 -2
  337. package/dist/src/core/furnace-staleness.js +2 -2
  338. package/dist/src/core/furnace-state-persist.d.ts +12 -12
  339. package/dist/src/core/furnace-state-persist.js +10 -10
  340. package/dist/src/core/furnace-step-errors.d.ts +6 -6
  341. package/dist/src/core/furnace-step-errors.js +6 -6
  342. package/dist/src/core/furnace-validate-accessibility.js +14 -14
  343. package/dist/src/core/furnace-validate-compatibility.js +9 -9
  344. package/dist/src/core/furnace-validate-registration.d.ts +2 -2
  345. package/dist/src/core/furnace-validate-registration.js +18 -20
  346. package/dist/src/core/furnace-validate-structure.d.ts +1 -1
  347. package/dist/src/core/furnace-validate-structure.js +4 -4
  348. package/dist/src/core/furnace-validate.js +9 -9
  349. package/dist/src/core/furnace-version-drift.d.ts +5 -5
  350. package/dist/src/core/furnace-version-drift.js +5 -5
  351. package/dist/src/core/git-base.d.ts +6 -4
  352. package/dist/src/core/git-base.js +29 -7
  353. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  354. package/dist/src/core/git-diff-new-file.js +119 -0
  355. package/dist/src/core/git-diff.d.ts +13 -13
  356. package/dist/src/core/git-diff.js +121 -189
  357. package/dist/src/core/git-file-ops.d.ts +6 -13
  358. package/dist/src/core/git-file-ops.js +11 -35
  359. package/dist/src/core/git-readonly-index.d.ts +12 -14
  360. package/dist/src/core/git-readonly-index.js +12 -16
  361. package/dist/src/core/git-status.d.ts +9 -2
  362. package/dist/src/core/git-status.js +23 -11
  363. package/dist/src/core/git.d.ts +3 -9
  364. package/dist/src/core/git.js +28 -57
  365. package/dist/src/core/harness-orphans.d.ts +17 -17
  366. package/dist/src/core/harness-orphans.js +28 -27
  367. package/dist/src/core/history-log.d.ts +19 -0
  368. package/dist/src/core/history-log.js +29 -0
  369. package/dist/src/core/license-headers.d.ts +19 -18
  370. package/dist/src/core/license-headers.js +29 -28
  371. package/dist/src/core/mach-build-artifacts.d.ts +21 -21
  372. package/dist/src/core/mach-build-artifacts.js +23 -23
  373. package/dist/src/core/mach-error-hints.d.ts +5 -10
  374. package/dist/src/core/mach-error-hints.js +14 -14
  375. package/dist/src/core/mach-known-noise-filter.d.ts +42 -38
  376. package/dist/src/core/mach-known-noise-filter.js +57 -56
  377. package/dist/src/core/mach-mozconfig.d.ts +6 -6
  378. package/dist/src/core/mach-mozconfig.js +17 -16
  379. package/dist/src/core/mach-python.d.ts +3 -0
  380. package/dist/src/core/mach-python.js +13 -7
  381. package/dist/src/core/mach-resource-shim.d.ts +24 -12
  382. package/dist/src/core/mach-resource-shim.js +124 -21
  383. package/dist/src/core/mach.d.ts +59 -73
  384. package/dist/src/core/mach.js +47 -115
  385. package/dist/src/core/marionette-port.d.ts +15 -30
  386. package/dist/src/core/marionette-port.js +46 -89
  387. package/dist/src/core/marionette-preflight.d.ts +6 -6
  388. package/dist/src/core/marionette-preflight.js +32 -50
  389. package/dist/src/core/markdown-table.d.ts +9 -9
  390. package/dist/src/core/markdown-table.js +14 -14
  391. package/dist/src/core/mochitest-server-port.d.ts +8 -8
  392. package/dist/src/core/mochitest-server-port.js +17 -17
  393. package/dist/src/core/module-resolution-preflight.d.ts +8 -8
  394. package/dist/src/core/module-resolution-preflight.js +11 -11
  395. package/dist/src/core/moz-manifest-helpers.d.ts +7 -7
  396. package/dist/src/core/moz-manifest-helpers.js +7 -7
  397. package/dist/src/core/moz-manifest-register.d.ts +1 -1
  398. package/dist/src/core/moz-manifest-register.js +2 -2
  399. package/dist/src/core/moz-manifest-rules.js +46 -42
  400. package/dist/src/core/moz-manifest-tokenizers.js +1 -1
  401. package/dist/src/core/ownership-table.d.ts +5 -5
  402. package/dist/src/core/ownership-table.js +7 -7
  403. package/dist/src/core/parser-fallback.d.ts +3 -3
  404. package/dist/src/core/parser-fallback.js +3 -3
  405. package/dist/src/core/patch-apply-fuzz.d.ts +5 -5
  406. package/dist/src/core/patch-apply-fuzz.js +13 -13
  407. package/dist/src/core/patch-apply.d.ts +13 -14
  408. package/dist/src/core/patch-apply.js +36 -42
  409. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  410. package/dist/src/core/patch-export-coverage.js +1 -30
  411. package/dist/src/core/patch-export-update.d.ts +21 -4
  412. package/dist/src/core/patch-export-update.js +7 -6
  413. package/dist/src/core/patch-export.d.ts +20 -30
  414. package/dist/src/core/patch-export.js +55 -77
  415. package/dist/src/core/patch-files.d.ts +0 -4
  416. package/dist/src/core/patch-files.js +0 -11
  417. package/dist/src/core/patch-identifier-suggest.d.ts +1 -1
  418. package/dist/src/core/patch-identifier-suggest.js +4 -4
  419. package/dist/src/core/patch-lint-binary.d.ts +12 -18
  420. package/dist/src/core/patch-lint-cache.d.ts +18 -2
  421. package/dist/src/core/patch-lint-cache.js +16 -19
  422. package/dist/src/core/patch-lint-checkjs.d.ts +55 -38
  423. package/dist/src/core/patch-lint-checkjs.js +78 -64
  424. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +8 -9
  425. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -11
  426. package/dist/src/core/patch-lint-creators.d.ts +5 -9
  427. package/dist/src/core/patch-lint-creators.js +3 -3
  428. package/dist/src/core/patch-lint-cross.d.ts +27 -19
  429. package/dist/src/core/patch-lint-cross.js +67 -110
  430. package/dist/src/core/patch-lint-css.d.ts +1 -1
  431. package/dist/src/core/patch-lint-css.js +15 -11
  432. package/dist/src/core/patch-lint-diff-tag.d.ts +5 -5
  433. package/dist/src/core/patch-lint-diff-tag.js +5 -5
  434. package/dist/src/core/patch-lint-diff.d.ts +4 -4
  435. package/dist/src/core/patch-lint-diff.js +4 -4
  436. package/dist/src/core/patch-lint-file-size.d.ts +3 -3
  437. package/dist/src/core/patch-lint-file-size.js +6 -6
  438. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  439. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  440. package/dist/src/core/patch-lint-jsdoc.d.ts +2 -2
  441. package/dist/src/core/patch-lint-jsdoc.js +46 -46
  442. package/dist/src/core/patch-lint-module-registration.d.ts +3 -9
  443. package/dist/src/core/patch-lint-module-registration.js +1 -1
  444. package/dist/src/core/patch-lint-mozbuild.d.ts +1 -1
  445. package/dist/src/core/patch-lint-mozbuild.js +10 -10
  446. package/dist/src/core/patch-lint-observer.d.ts +3 -3
  447. package/dist/src/core/patch-lint-observer.js +7 -7
  448. package/dist/src/core/patch-lint-ownership.d.ts +6 -6
  449. package/dist/src/core/patch-lint-ownership.js +6 -6
  450. package/dist/src/core/patch-lint-prettier.d.ts +6 -6
  451. package/dist/src/core/patch-lint-prettier.js +7 -7
  452. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  453. package/dist/src/core/patch-lint-queue-types.js +12 -0
  454. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  455. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  456. package/dist/src/core/patch-lint-unmanaged-companion.d.ts +23 -23
  457. package/dist/src/core/patch-lint-unmanaged-companion.js +26 -26
  458. package/dist/src/core/patch-lint.d.ts +41 -35
  459. package/dist/src/core/patch-lint.js +52 -49
  460. package/dist/src/core/patch-lock.d.ts +1 -1
  461. package/dist/src/core/patch-lock.js +11 -11
  462. package/dist/src/core/patch-manifest-consistency.d.ts +7 -7
  463. package/dist/src/core/patch-manifest-consistency.js +16 -17
  464. package/dist/src/core/patch-manifest-files-affected.d.ts +1 -1
  465. package/dist/src/core/patch-manifest-files-affected.js +3 -3
  466. package/dist/src/core/patch-manifest-io.d.ts +14 -14
  467. package/dist/src/core/patch-manifest-io.js +35 -34
  468. package/dist/src/core/patch-manifest-query.d.ts +1 -1
  469. package/dist/src/core/patch-manifest-query.js +4 -4
  470. package/dist/src/core/patch-manifest-resolve.d.ts +5 -5
  471. package/dist/src/core/patch-manifest-resolve.js +7 -7
  472. package/dist/src/core/patch-manifest-validate.js +3 -3
  473. package/dist/src/core/patch-manifest.d.ts +2 -2
  474. package/dist/src/core/patch-manifest.js +2 -2
  475. package/dist/src/core/patch-parse.d.ts +32 -21
  476. package/dist/src/core/patch-parse.js +40 -15
  477. package/dist/src/core/patch-policy.js +15 -27
  478. package/dist/src/core/patch-registration-refs.d.ts +4 -4
  479. package/dist/src/core/patch-registration-refs.js +7 -7
  480. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  481. package/dist/src/core/patch-source-metadata.js +0 -4
  482. package/dist/src/core/patch-transform.d.ts +8 -16
  483. package/dist/src/core/patch-transform.js +13 -23
  484. package/dist/src/core/process-cpu-time.d.ts +1 -1
  485. package/dist/src/core/process-cpu-time.js +6 -6
  486. package/dist/src/core/rebase-session.d.ts +9 -15
  487. package/dist/src/core/rebase-session.js +13 -25
  488. package/dist/src/core/register-browser-content.js +3 -2
  489. package/dist/src/core/register-browser-test.js +2 -1
  490. package/dist/src/core/register-module.js +2 -1
  491. package/dist/src/core/register-result.d.ts +1 -1
  492. package/dist/src/core/register-result.js +1 -1
  493. package/dist/src/core/register-scaffold.d.ts +2 -2
  494. package/dist/src/core/register-scaffold.js +8 -7
  495. package/dist/src/core/register-shared-css.d.ts +3 -3
  496. package/dist/src/core/register-shared-css.js +6 -5
  497. package/dist/src/core/register-xpcshell-test.d.ts +2 -2
  498. package/dist/src/core/register-xpcshell-test.js +2 -2
  499. package/dist/src/core/run-log-redact.d.ts +38 -0
  500. package/dist/src/core/run-log-redact.js +69 -0
  501. package/dist/src/core/run-log.d.ts +21 -10
  502. package/dist/src/core/run-log.js +76 -27
  503. package/dist/src/core/shared-ftl.d.ts +2 -2
  504. package/dist/src/core/shared-ftl.js +6 -6
  505. package/dist/src/core/signal-critical.d.ts +2 -2
  506. package/dist/src/core/signal-critical.js +4 -6
  507. package/dist/src/core/smoke-patterns.d.ts +5 -6
  508. package/dist/src/core/smoke-patterns.js +15 -20
  509. package/dist/src/core/state-file.d.ts +15 -4
  510. package/dist/src/core/state-file.js +14 -1
  511. package/dist/src/core/status-binary.d.ts +8 -8
  512. package/dist/src/core/status-binary.js +12 -12
  513. package/dist/src/core/status-classify.d.ts +17 -17
  514. package/dist/src/core/status-classify.js +13 -12
  515. package/dist/src/core/test-changed-prefs.d.ts +12 -12
  516. package/dist/src/core/test-changed-prefs.js +40 -25
  517. package/dist/src/core/test-harness-crash.d.ts +39 -39
  518. package/dist/src/core/test-harness-crash.js +69 -69
  519. package/dist/src/core/test-harness-output.d.ts +7 -7
  520. package/dist/src/core/test-harness-output.js +7 -7
  521. package/dist/src/core/test-harness-verdict-notes.d.ts +13 -14
  522. package/dist/src/core/test-harness-verdict-notes.js +4 -4
  523. package/dist/src/core/test-path-scope.d.ts +8 -8
  524. package/dist/src/core/test-path-scope.js +11 -11
  525. package/dist/src/core/test-stale-check.d.ts +22 -23
  526. package/dist/src/core/test-stale-check.js +31 -30
  527. package/dist/src/core/test-stale-symlink.d.ts +1 -1
  528. package/dist/src/core/test-stale-symlink.js +1 -1
  529. package/dist/src/core/test-stall-triage.d.ts +9 -9
  530. package/dist/src/core/test-stall-triage.js +22 -22
  531. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  532. package/dist/src/core/test-xpcshell-retry.js +14 -11
  533. package/dist/src/core/token-category.d.ts +35 -2
  534. package/dist/src/core/token-category.js +67 -58
  535. package/dist/src/core/token-coverage.js +4 -5
  536. package/dist/src/core/token-dark-mode.d.ts +9 -9
  537. package/dist/src/core/token-dark-mode.js +13 -25
  538. package/dist/src/core/token-docs.d.ts +1 -1
  539. package/dist/src/core/token-inventory.d.ts +56 -0
  540. package/dist/src/core/token-inventory.js +109 -0
  541. package/dist/src/core/token-manager.d.ts +4 -4
  542. package/dist/src/core/token-manager.js +22 -22
  543. package/dist/src/core/token-scaffold.d.ts +4 -4
  544. package/dist/src/core/token-scaffold.js +4 -4
  545. package/dist/src/core/token-variant.d.ts +14 -14
  546. package/dist/src/core/token-variant.js +52 -53
  547. package/dist/src/core/toolchain-preflight.d.ts +15 -15
  548. package/dist/src/core/toolchain-preflight.js +23 -23
  549. package/dist/src/core/tree-cow.d.ts +3 -5
  550. package/dist/src/core/tree-cow.js +8 -8
  551. package/dist/src/core/tree-guard.d.ts +3 -3
  552. package/dist/src/core/tree-guard.js +23 -23
  553. package/dist/src/core/tree-store.d.ts +15 -15
  554. package/dist/src/core/tree-store.js +39 -38
  555. package/dist/src/core/typecheck-shim.d.ts +16 -15
  556. package/dist/src/core/typecheck-shim.js +26 -25
  557. package/dist/src/core/typecheck.d.ts +12 -12
  558. package/dist/src/core/typecheck.js +33 -33
  559. package/dist/src/core/wire-destroy.d.ts +2 -2
  560. package/dist/src/core/wire-destroy.js +5 -5
  561. package/dist/src/core/wire-dom-fragment.d.ts +3 -3
  562. package/dist/src/core/wire-dom-fragment.js +12 -23
  563. package/dist/src/core/wire-init.d.ts +2 -2
  564. package/dist/src/core/wire-init.js +7 -7
  565. package/dist/src/core/wire-subscript.d.ts +1 -1
  566. package/dist/src/core/wire-subscript.js +3 -3
  567. package/dist/src/core/wire-utils.d.ts +9 -9
  568. package/dist/src/core/wire-utils.js +16 -28
  569. package/dist/src/core/xpcshell-appdir.d.ts +20 -20
  570. package/dist/src/core/xpcshell-appdir.js +28 -30
  571. package/dist/src/core/xpcshell-profile-dir.d.ts +3 -3
  572. package/dist/src/core/xpcshell-profile-dir.js +7 -7
  573. package/dist/src/errors/base.d.ts +69 -16
  574. package/dist/src/errors/base.js +95 -26
  575. package/dist/src/errors/build.d.ts +6 -6
  576. package/dist/src/errors/build.js +26 -34
  577. package/dist/src/errors/codes.d.ts +10 -10
  578. package/dist/src/errors/codes.js +10 -10
  579. package/dist/src/errors/config.d.ts +0 -14
  580. package/dist/src/errors/config.js +13 -34
  581. package/dist/src/errors/download.d.ts +13 -0
  582. package/dist/src/errors/download.js +53 -24
  583. package/dist/src/errors/furnace.d.ts +2 -2
  584. package/dist/src/errors/furnace.js +8 -9
  585. package/dist/src/errors/git.d.ts +9 -9
  586. package/dist/src/errors/git.js +21 -31
  587. package/dist/src/errors/patch.d.ts +3 -3
  588. package/dist/src/errors/patch.js +11 -13
  589. package/dist/src/errors/rebase.d.ts +1 -1
  590. package/dist/src/errors/rebase.js +8 -8
  591. package/dist/src/errors/run.d.ts +1 -1
  592. package/dist/src/errors/run.js +1 -1
  593. package/dist/src/index.d.ts +4 -4
  594. package/dist/src/index.js +4 -4
  595. package/dist/src/types/commands/options.d.ts +155 -200
  596. package/dist/src/types/commands/patches.d.ts +19 -19
  597. package/dist/src/types/commands/project.d.ts +3 -3
  598. package/dist/src/types/config.d.ts +52 -19
  599. package/dist/src/types/furnace.d.ts +17 -17
  600. package/dist/src/types/index.d.ts +0 -1
  601. package/dist/src/types/json.d.ts +3 -3
  602. package/dist/src/types/json.js +2 -2
  603. package/dist/src/types/typecheck.d.ts +3 -3
  604. package/dist/src/utils/assert.d.ts +4 -16
  605. package/dist/src/utils/assert.js +7 -24
  606. package/dist/src/utils/build-info.d.ts +6 -6
  607. package/dist/src/utils/build-info.js +6 -6
  608. package/dist/src/utils/child-env.d.ts +7 -6
  609. package/dist/src/utils/child-env.js +5 -5
  610. package/dist/src/utils/concurrency.d.ts +2 -2
  611. package/dist/src/utils/concurrency.js +2 -2
  612. package/dist/src/utils/elapsed.js +10 -1
  613. package/dist/src/utils/errors.d.ts +8 -8
  614. package/dist/src/utils/errors.js +8 -8
  615. package/dist/src/utils/fs.d.ts +1 -1
  616. package/dist/src/utils/fs.js +15 -20
  617. package/dist/src/utils/hash.d.ts +12 -0
  618. package/dist/src/utils/hash.js +16 -0
  619. package/dist/src/utils/line-dispatch.d.ts +15 -0
  620. package/dist/src/utils/line-dispatch.js +48 -0
  621. package/dist/src/utils/logger.d.ts +4 -4
  622. package/dist/src/utils/logger.js +11 -11
  623. package/dist/src/utils/machine-output.js +2 -2
  624. package/dist/src/utils/options.d.ts +14 -20
  625. package/dist/src/utils/options.js +26 -27
  626. package/dist/src/utils/package-root.js +5 -14
  627. package/dist/src/utils/paths.d.ts +4 -4
  628. package/dist/src/utils/paths.js +4 -4
  629. package/dist/src/utils/platform.d.ts +0 -4
  630. package/dist/src/utils/process-group.d.ts +10 -9
  631. package/dist/src/utils/process-group.js +37 -36
  632. package/dist/src/utils/process.d.ts +70 -55
  633. package/dist/src/utils/process.js +129 -179
  634. package/dist/src/utils/ps-duration.d.ts +4 -4
  635. package/dist/src/utils/ps-duration.js +9 -9
  636. package/dist/src/utils/regex.d.ts +4 -4
  637. package/dist/src/utils/regex.js +7 -7
  638. package/dist/src/utils/sleep.d.ts +26 -0
  639. package/dist/src/utils/sleep.js +31 -0
  640. package/dist/src/utils/stdio-drain.d.ts +7 -7
  641. package/dist/src/utils/stdio-drain.js +8 -8
  642. package/dist/src/utils/validation.d.ts +12 -49
  643. package/dist/src/utils/validation.js +26 -36
  644. package/docs/README.md +2 -2
  645. package/docs/configuration.md +163 -69
  646. package/docs/exit-codes.md +66 -49
  647. package/docs/furnace.md +20 -19
  648. package/docs/lifecycle-invariants.md +414 -247
  649. package/docs/machine-output.md +58 -34
  650. package/docs/patch-workflow.md +48 -44
  651. package/docs/run-logs.md +72 -28
  652. package/docs/testing.md +105 -98
  653. package/docs/verification-trees.md +23 -22
  654. package/package.json +7 -6
  655. package/dist/src/commands/rebase.d.ts +0 -4
  656. package/dist/src/commands/rebase.js +0 -5
  657. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  658. package/dist/src/core/patch-artifact-normalize.js +0 -24
  659. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  660. package/dist/src/core/patch-lint-reexports.js +0 -8
  661. package/dist/src/core/wire-targets.d.ts +0 -7
  662. package/dist/src/core/wire-targets.js +0 -8
@@ -0,0 +1,69 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Conservative secret redaction for the run-log FILE tee.
4
+ *
5
+ * A run log is a complete copy of mach's output, and mach echoes its
6
+ * environment in places (configure summaries, `mach env`, harness
7
+ * diagnostics that print the command line). The terminal already showed
8
+ * every byte to the operator who ran it. The file, by contrast, is retained
9
+ * twenty deep and gets attached to bug reports. So the file gets a narrow
10
+ * masking pass and the terminal does not.
11
+ *
12
+ * Minimal and pattern-based. See {@link redactRunLogText} for the exact
13
+ * contract. It is a seatbelt against the common accidental leak, not a
14
+ * guarantee that a log is free of secrets.
15
+ */
16
+ /** Env-style keys whose assigned value is masked. */
17
+ const SECRET_KEY_PATTERN = /(TOKEN|SECRET|PASSWORD|PASSWD|API_KEY|AUTH)/i;
18
+ /**
19
+ * `KEY=value` where KEY is an identifier and the value runs to the next
20
+ * whitespace, or is a single/double-quoted string. The leading group keeps
21
+ * the assignment anchored at a token boundary so `x.y=z` is not split
22
+ * mid-token. A hyphen IS a boundary: mach and configure echo their argv, so
23
+ * `--token=…`, `--password=…` and `-DAUTH_TOKEN=…` are exactly the forms a
24
+ * retained log would otherwise carry verbatim (the lead keeps the dash, and
25
+ * the key is what follows it). Empty values are left alone (nothing to hide).
26
+ */
27
+ const ENV_ASSIGNMENT_PATTERN = /(^|[\s"'([{,;-])([A-Za-z_][A-Za-z0-9_]*)=("(?:[^"\\]|\\.)*"|'[^']*'|[^\s"',;)\]}]+)/g;
28
+ /** `Authorization: <scheme> <credential>` (scheme optional) in HTTP dumps. */
29
+ const AUTHORIZATION_HEADER_PATTERN = /(authorization\s*:\s*)(?:(bearer|basic|token)\s+)?(\S+)/gi;
30
+ /** Marker written in place of a masked value. */
31
+ const REDACTED = '<redacted>';
32
+ function redactLine(line) {
33
+ return line
34
+ .replace(ENV_ASSIGNMENT_PATTERN, (whole, lead, key) => SECRET_KEY_PATTERN.test(key) ? `${lead}${key}=${REDACTED}` : whole)
35
+ .replace(AUTHORIZATION_HEADER_PATTERN, (_whole, lead, scheme) => scheme === undefined ? `${lead}${REDACTED}` : `${lead}${scheme} ${REDACTED}`);
36
+ }
37
+ /**
38
+ * Masks the obvious secret shapes in run-log text, line by line.
39
+ *
40
+ * Redacted:
41
+ * - the value of an env-style assignment `KEY=value` whose KEY matches
42
+ * `/(TOKEN|SECRET|PASSWORD|PASSWD|API_KEY|AUTH)/i`. The value up to the
43
+ * next whitespace, or a whole quoted string, becomes `KEY=<redacted>`.
44
+ * The `--flag=value` and `-DNAME=value` spellings of the same shape are
45
+ * covered (`--password=x` → `--password=<redacted>`).
46
+ * - the credential of an `Authorization:` header (`Bearer`, `Basic` and
47
+ * `Token` schemes keep the scheme word, and anything else masks the
48
+ * whole value).
49
+ *
50
+ * Not redacted: secrets that appear without one of those key names or
51
+ * headers (a bare token in a URL query string, a space-separated
52
+ * `--password foo` CLI argument, JSON `"token": "..."` fields, cookies).
53
+ * Line structure and every other byte pass through unchanged. `\n`, `\r\n`
54
+ * and lone `\r` all delimit lines, so carriage-return-repainted progress
55
+ * output is masked per repaint.
56
+ *
57
+ * @param text - One or more complete lines
58
+ * @returns The same text with masked values
59
+ */
60
+ export function redactRunLogText(text) {
61
+ if (text.length === 0)
62
+ return text;
63
+ // Split with a capturing group so the terminators survive the round trip.
64
+ // Odd indices are the terminators themselves.
65
+ return text
66
+ .split(/(\r\n|\n|\r)/)
67
+ .map((part, index) => (index % 2 === 0 ? redactLine(part) : part))
68
+ .join('');
69
+ }
@@ -2,19 +2,19 @@
2
2
  * Per-run output logs under `.fireforge/logs/`.
3
3
  *
4
4
  * A run's diagnosis used to exist in exactly one place: the terminal. Piping
5
- * through `tail`/`grep` — the ergonomic default when output is long — keeps
6
- * the summary and discards the `TEST-UNEXPECTED-FAIL` lines that say WHAT
5
+ * through `tail`/`grep`, the ergonomic default when output is long, keeps
6
+ * the summary and discards the `TEST-UNEXPECTED-FAIL` lines that say what
7
7
  * broke, and it launders the exit code besides. The operator rule ("never
8
8
  * pipe a run") was written down three times downstream and broken after each
9
- * writing, which is the tell that it is not an operator problem: the tool is
10
- * the only party that can make the log survive the mistake.
9
+ * writing, so this is not an operator problem: the tool is the only party
10
+ * that can make the log survive the mistake.
11
11
  *
12
12
  * So `test` and `build` write their own complete copy as they stream, and the
13
13
  * `FIREFORGE-VERDICT:` line names the path. A piped, truncated, or
14
14
  * backgrounded run then still leaves a re-readable artifact.
15
15
  *
16
- * Everything here is BEST-EFFORT by construction. A log is a diagnostic aid;
17
- * a run must never fail because one could not be opened, written, or pruned.
16
+ * Everything here is best-effort. A log is a diagnostic aid, and a run must
17
+ * never fail because one could not be opened, written, or pruned.
18
18
  * Every entry point swallows its errors and degrades to "no log", which is
19
19
  * exactly the behaviour that existed before this module.
20
20
  */
@@ -22,7 +22,11 @@
22
22
  export interface RunLog {
23
23
  /** Absolute path to the log file. */
24
24
  path: string;
25
- /** Appends raw output. Silently drops on any stream error. */
25
+ /**
26
+ * Appends output. Complete lines pass through {@link redactRunLogText}
27
+ * before hitting disk. A trailing partial line is held until its newline
28
+ * arrives (or until `close`). Silently drops on any stream error.
29
+ */
26
30
  write: (chunk: string) => void;
27
31
  /** Flushes and closes. Resolves even when the stream already failed. */
28
32
  close: () => Promise<void>;
@@ -45,13 +49,20 @@ export declare function setActiveRunLog(log: RunLog | undefined): void;
45
49
  export declare function getActiveRunLogPath(): string | undefined;
46
50
  /** Appends to the active run log. A no-op when none is open. */
47
51
  export declare function writeToActiveRunLog(chunk: string): void;
48
- /** Closes and clears the active run log. Resolves even when none is open. */
52
+ /**
53
+ * Closes and clears the active run log. Resolves even when none is open.
54
+ *
55
+ * Also called from the entry point's SIGINT/SIGTERM path: the held partial
56
+ * line and anything queued in the stream would otherwise be lost across
57
+ * `process.exit`, and a killed run's last line is usually the one that
58
+ * says what it was doing.
59
+ */
49
60
  export declare function closeActiveRunLog(): Promise<void>;
50
61
  /**
51
- * A writable that forwards to `base` AND to the active run log.
62
+ * A writable that forwards to `base` and to the active run log.
52
63
  *
53
64
  * Used by the stdio-inheriting capture path, whose collectors take a mirror
54
- * stream rather than per-chunk callbacks — so the tee reuses the mirror the
65
+ * stream rather than per-chunk callbacks, so the tee reuses the mirror the
55
66
  * exec layer already supports instead of growing a second hook.
56
67
  *
57
68
  * @param base - The stream to forward to (normally `process.stdout`)
@@ -3,19 +3,19 @@
3
3
  * Per-run output logs under `.fireforge/logs/`.
4
4
  *
5
5
  * A run's diagnosis used to exist in exactly one place: the terminal. Piping
6
- * through `tail`/`grep` — the ergonomic default when output is long — keeps
7
- * the summary and discards the `TEST-UNEXPECTED-FAIL` lines that say WHAT
6
+ * through `tail`/`grep`, the ergonomic default when output is long, keeps
7
+ * the summary and discards the `TEST-UNEXPECTED-FAIL` lines that say what
8
8
  * broke, and it launders the exit code besides. The operator rule ("never
9
9
  * pipe a run") was written down three times downstream and broken after each
10
- * writing, which is the tell that it is not an operator problem: the tool is
11
- * the only party that can make the log survive the mistake.
10
+ * writing, so this is not an operator problem: the tool is the only party
11
+ * that can make the log survive the mistake.
12
12
  *
13
13
  * So `test` and `build` write their own complete copy as they stream, and the
14
14
  * `FIREFORGE-VERDICT:` line names the path. A piped, truncated, or
15
15
  * backgrounded run then still leaves a re-readable artifact.
16
16
  *
17
- * Everything here is BEST-EFFORT by construction. A log is a diagnostic aid;
18
- * a run must never fail because one could not be opened, written, or pruned.
17
+ * Everything here is best-effort. A log is a diagnostic aid, and a run must
18
+ * never fail because one could not be opened, written, or pruned.
19
19
  * Every entry point swallows its errors and degrades to "no log", which is
20
20
  * exactly the behaviour that existed before this module.
21
21
  */
@@ -23,8 +23,11 @@ import { createWriteStream } from 'node:fs';
23
23
  import { mkdir, readdir, unlink } from 'node:fs/promises';
24
24
  import { join } from 'node:path';
25
25
  import { Writable } from 'node:stream';
26
+ import { toError } from '../utils/errors.js';
26
27
  import { verbose } from '../utils/logger.js';
27
28
  import { FIREFORGE_DIR } from './config-paths.js';
29
+ import { redactRunLogText } from './run-log-redact.js';
30
+ import { fileSafeTimestamp } from './state-file.js';
28
31
  /** Directory under `.fireforge/` holding run logs. */
29
32
  const LOGS_DIRNAME = 'logs';
30
33
  /**
@@ -33,24 +36,38 @@ const LOGS_DIRNAME = 'logs';
33
36
  * most often needs to read together.
34
37
  */
35
38
  const RETAINED_LOGS_PER_COMMAND = 20;
39
+ /**
40
+ * Cap on the partial line held back between writes. A child emitting one
41
+ * enormous unterminated line (a minified dump on stderr) would otherwise
42
+ * grow the buffer without bound (and, because each write rebuilds
43
+ * `pending + chunk`, quadratically), with nothing reaching disk until
44
+ * close. Past the cap the tail is redacted and written as-is. A `KEY=value`
45
+ * split exactly at the cap boundary is the accepted cost. Same figure as
46
+ * `line-dispatch.ts` uses for the stream matchers.
47
+ */
48
+ const MAX_PENDING_LINE_SIZE = 1024 * 1024;
49
+ /**
50
+ * Index just past the last line terminator in `text`, or -1 when there is
51
+ * none. `\n` and lone `\r` both terminate: progress-bar output (mach,
52
+ * cargo) repaints a line with carriage returns and may never send a
53
+ * newline, which would otherwise pin the whole run in `pending`. A single
54
+ * trailing `\r` is not a terminator yet: it may be the first half of a
55
+ * `\r\n` split across chunks.
56
+ */
57
+ function lastLineEnd(text) {
58
+ const lastNewline = text.lastIndexOf('\n');
59
+ const lastReturn = text.lastIndexOf('\r');
60
+ const cut = lastReturn > lastNewline && lastReturn < text.length - 1 ? lastReturn : lastNewline;
61
+ return cut === -1 ? -1 : cut + 1;
62
+ }
36
63
  /** `.fireforge/logs` under `projectRoot`. */
37
64
  export function getRunLogDir(projectRoot) {
38
65
  return join(projectRoot, FIREFORGE_DIR, LOGS_DIRNAME);
39
66
  }
40
- /**
41
- * A filesystem-safe, sortable timestamp: `2026-08-28T14-32-05-123Z`.
42
- *
43
- * Colons are not portable in filenames (Windows rejects them outright), so
44
- * the ISO form is punctuated with dashes only. Lexical order still equals
45
- * chronological order, which is what the pruning below relies on.
46
- */
47
- function logTimestamp(now) {
48
- return now.toISOString().replace(/[:.]/g, '-');
49
- }
50
67
  /**
51
68
  * Makes room for one new log by deleting the oldest, so that at most
52
69
  * {@link RETAINED_LOGS_PER_COMMAND} logs for `command` exist once the new
53
- * one is created. Pruning runs BEFORE the file is opened — hence the
70
+ * one is created. Pruning runs before the file is opened, hence the
54
71
  * `- 1`, without which the directory settles at N + 1.
55
72
  *
56
73
  * Best-effort: a failure here must not stop the run that is about to be
@@ -76,7 +93,7 @@ async function pruneRunLogs(dir, command) {
76
93
  }));
77
94
  }
78
95
  catch (error) {
79
- verbose(`Run-log pruning skipped: ${String(error)}`);
96
+ verbose(`Run-log pruning skipped: ${toError(error).message}`);
80
97
  }
81
98
  }
82
99
  /**
@@ -91,14 +108,14 @@ async function pruneRunLogs(dir, command) {
91
108
  export async function openRunLog(projectRoot, command, now = new Date()) {
92
109
  const dir = getRunLogDir(projectRoot);
93
110
  let stream;
94
- const path = join(dir, `${command}-${logTimestamp(now)}.log`);
111
+ const path = join(dir, `${command}-${fileSafeTimestamp(now)}.log`);
95
112
  try {
96
113
  await mkdir(dir, { recursive: true });
97
114
  await pruneRunLogs(dir, command);
98
115
  stream = createWriteStream(path, { flags: 'a' });
99
116
  }
100
117
  catch (error) {
101
- verbose(`Run log unavailable (${path}): ${String(error)}`);
118
+ verbose(`Run log unavailable (${path}): ${toError(error).message}`);
102
119
  return undefined;
103
120
  }
104
121
  // A stream error after open (disk full, a removed directory) must not
@@ -109,19 +126,44 @@ export async function openRunLog(projectRoot, command, now = new Date()) {
109
126
  broken = true;
110
127
  verbose(`Run log write failed (${path}): ${error.message}`);
111
128
  });
129
+ // Redaction is per LINE, and chunks arrive at arbitrary byte boundaries,
130
+ // so a `KEY=value` split across two chunks would slip past a per-chunk
131
+ // pass. Hold the trailing partial line until its terminator arrives (or
132
+ // it outgrows MAX_PENDING_LINE_SIZE, or the log is closed).
133
+ let pending = '';
134
+ const emit = (text) => {
135
+ if (broken || text.length === 0)
136
+ return;
137
+ try {
138
+ stream.write(redactRunLogText(text));
139
+ }
140
+ catch {
141
+ broken = true;
142
+ }
143
+ };
112
144
  return {
113
145
  path,
114
146
  write: (chunk) => {
115
147
  if (broken)
116
148
  return;
117
- try {
118
- stream.write(chunk);
149
+ const buffered = pending + chunk;
150
+ const lineEnd = lastLineEnd(buffered);
151
+ if (lineEnd === -1) {
152
+ pending = buffered;
119
153
  }
120
- catch {
121
- broken = true;
154
+ else {
155
+ pending = buffered.slice(lineEnd);
156
+ emit(buffered.slice(0, lineEnd));
157
+ }
158
+ if (pending.length > MAX_PENDING_LINE_SIZE) {
159
+ emit(pending);
160
+ pending = '';
122
161
  }
123
162
  },
124
163
  close: async () => {
164
+ const tail = pending;
165
+ pending = '';
166
+ emit(tail);
125
167
  await new Promise((resolve) => {
126
168
  stream.end(() => {
127
169
  resolve();
@@ -155,17 +197,24 @@ export function getActiveRunLogPath() {
155
197
  export function writeToActiveRunLog(chunk) {
156
198
  activeRunLog?.write(chunk);
157
199
  }
158
- /** Closes and clears the active run log. Resolves even when none is open. */
200
+ /**
201
+ * Closes and clears the active run log. Resolves even when none is open.
202
+ *
203
+ * Also called from the entry point's SIGINT/SIGTERM path: the held partial
204
+ * line and anything queued in the stream would otherwise be lost across
205
+ * `process.exit`, and a killed run's last line is usually the one that
206
+ * says what it was doing.
207
+ */
159
208
  export async function closeActiveRunLog() {
160
209
  const log = activeRunLog;
161
210
  activeRunLog = undefined;
162
211
  await log?.close();
163
212
  }
164
213
  /**
165
- * A writable that forwards to `base` AND to the active run log.
214
+ * A writable that forwards to `base` and to the active run log.
166
215
  *
167
216
  * Used by the stdio-inheriting capture path, whose collectors take a mirror
168
- * stream rather than per-chunk callbacks — so the tee reuses the mirror the
217
+ * stream rather than per-chunk callbacks, so the tee reuses the mirror the
169
218
  * exec layer already supports instead of growing a second hook.
170
219
  *
171
220
  * @param base - The stream to forward to (normally `process.stdout`)
@@ -2,13 +2,13 @@
2
2
  * Structural rules for the `sharedFtl` field on a custom component.
3
3
  *
4
4
  * Both the `--shared-ftl` CLI flag path (`furnace create`) and the
5
- * furnace.json parser apply the same rules — extracting them here
5
+ * furnace.json parser apply the same rules. Extracting them here
6
6
  * avoids drift between the two entry points and lets the `.mjs`
7
7
  * template generator assume the value is safe to interpolate verbatim.
8
8
  */
9
9
  /**
10
10
  * Outcome of {@link validateSharedFtl}. `ok: true` carries the trimmed
11
- * (operator-safe) value; `ok: false` carries a human-readable message
11
+ * (operator-safe) value. `ok: false` carries a human-readable message
12
12
  * suitable for throwing as a `FurnaceError` or `InvalidArgumentError`.
13
13
  */
14
14
  export type SharedFtlValidation = {
@@ -3,18 +3,18 @@
3
3
  * Structural rules for the `sharedFtl` field on a custom component.
4
4
  *
5
5
  * Both the `--shared-ftl` CLI flag path (`furnace create`) and the
6
- * furnace.json parser apply the same rules — extracting them here
6
+ * furnace.json parser apply the same rules. Extracting them here
7
7
  * avoids drift between the two entry points and lets the `.mjs`
8
8
  * template generator assume the value is safe to interpolate verbatim.
9
9
  */
10
10
  /**
11
11
  * Characters that must not appear in `sharedFtl`.
12
12
  *
13
- * A POLICY rule about what a sensible FTL path looks like, not the safety
14
- * boundary for the generated `.mjs`: the scaffold escapes the value at its
15
- * sink with `JSON.stringify` (`commands/furnace/create-templates.ts`), which
16
- * is what actually makes interpolation safe and what covers the two inputs
17
- * this rule never catches — a `"` and a raw newline.
13
+ * This is a policy rule about what a sensible FTL path looks like, not the
14
+ * safety boundary for the generated `.mjs`. The scaffold escapes the value at
15
+ * its sink with `JSON.stringify` (`commands/furnace/create-templates.ts`),
16
+ * which is what actually makes interpolation safe, and what covers the two
17
+ * inputs this rule never catches: a `"` and a raw newline.
18
18
  *
19
19
  * Kept because each remains a real sign of a malformed path:
20
20
  * - `\` is a path-escape we do not want to interpret.
@@ -13,7 +13,7 @@
13
13
  * for the slot to clear before calling `process.exit`, so a signal that
14
14
  * lands mid-body is held until the body's state write finishes.
15
15
  *
16
- * This module is a pure runtime registry — it installs no signal handlers
16
+ * This module is a pure runtime registry. It installs no signal handlers
17
17
  * itself. The bin entry point is responsible for awaiting
18
18
  * `waitForActiveCriticalSections` before terminating.
19
19
  */
@@ -23,7 +23,7 @@
23
23
  * SIGTERM that arrives during `fn` will hold exit until `fn` returns (or
24
24
  * rejects).
25
25
  *
26
- * `fn` should be short — anything that takes longer than the bounded wait in
26
+ * `fn` should be short. Anything that takes longer than the bounded wait in
27
27
  * the bin handler (`SIGNAL_CRITICAL_SECTION_TIMEOUT_MS`) will time out and
28
28
  * the handler will exit anyway. The intent is "guard the apply + state
29
29
  * persist pair," not "postpone exit indefinitely."
@@ -14,10 +14,11 @@
14
14
  * for the slot to clear before calling `process.exit`, so a signal that
15
15
  * lands mid-body is held until the body's state write finishes.
16
16
  *
17
- * This module is a pure runtime registry — it installs no signal handlers
17
+ * This module is a pure runtime registry. It installs no signal handlers
18
18
  * itself. The bin entry point is responsible for awaiting
19
19
  * `waitForActiveCriticalSections` before terminating.
20
20
  */
21
+ import { sleep } from '../utils/sleep.js';
21
22
  const activeSections = new Set();
22
23
  /**
23
24
  * Runs `fn` inside a signal-deferred critical section. The CLI entry point's
@@ -25,7 +26,7 @@ const activeSections = new Set();
25
26
  * SIGTERM that arrives during `fn` will hold exit until `fn` returns (or
26
27
  * rejects).
27
28
  *
28
- * `fn` should be short — anything that takes longer than the bounded wait in
29
+ * `fn` should be short. Anything that takes longer than the bounded wait in
29
30
  * the bin handler (`SIGNAL_CRITICAL_SECTION_TIMEOUT_MS`) will time out and
30
31
  * the handler will exit anyway. The intent is "guard the apply + state
31
32
  * persist pair," not "postpone exit indefinitely."
@@ -57,8 +58,5 @@ export async function waitForActiveCriticalSections(timeoutMs) {
57
58
  if (activeSections.size === 0)
58
59
  return;
59
60
  const snapshot = [...activeSections].map((s) => s.promise);
60
- await Promise.race([
61
- Promise.allSettled(snapshot).then(() => undefined),
62
- new Promise((resolve) => setTimeout(resolve, timeoutMs)),
63
- ]);
61
+ await Promise.race([Promise.allSettled(snapshot).then(() => undefined), sleep(timeoutMs)]);
64
62
  }
@@ -10,17 +10,16 @@
10
10
  * warning (e.g. `"pending JavaScript error cleanup"`) do not start with
11
11
  * the prefix and should not trip the scanner.
12
12
  */
13
- export declare const SMOKE_ERROR_PATTERNS: readonly RegExp[];
14
13
  /**
15
14
  * Returns `true` when `line` matches any pattern in
16
- * {@link SMOKE_ERROR_PATTERNS}. Does not consult the allowlist — that step
15
+ * {@link SMOKE_ERROR_PATTERNS}. Does not consult the allowlist. That step
17
16
  * lives in {@link matchAllowlist}, so the smoke runner can count
18
17
  * allowlisted hits separately from raw error matches for its summary.
19
18
  */
20
19
  export declare function matchesSmokeError(line: string): boolean;
21
20
  /**
22
21
  * One compiled allowlist entry with its provenance retained so the smoke
23
- * summary can attribute hits per entry — an entry that silently
22
+ * summary can attribute hits per entry. An entry that silently
24
23
  * stops matching (its suppressed shape changed upstream) is only
25
24
  * detectable when zero-hit entries are visible.
26
25
  */
@@ -33,16 +32,16 @@ export interface CompiledAllowlistEntry {
33
32
  }
34
33
  /**
35
34
  * Returns the index of the FIRST entry in `allow` matching `line`, or -1.
36
- * First-match attribution is deterministic and cheap; a line matching
35
+ * First-match attribution is deterministic and cheap. A line matching
37
36
  * several entries credits the earliest one. Safe to call with an empty
38
37
  * allowlist (always returns -1).
39
38
  */
40
39
  export declare function matchAllowlist(line: string, allow: readonly CompiledAllowlistEntry[]): number;
41
40
  /**
42
- * Parses a newline-delimited allowlist file body. Lines are trimmed; blank
41
+ * Parses a newline-delimited allowlist file body. Lines are trimmed. Blank
43
42
  * lines and `#`-prefixed comments are skipped. Each remaining line is
44
43
  * compiled as a RegExp with its `<file>:<line>` origin retained. A bad
45
- * pattern throws immediately — better to fail fast at CLI parse time than
44
+ * pattern throws immediately: better to fail fast at CLI parse time than
46
45
  * to silently let a typo match nothing.
47
46
  */
48
47
  export declare function compileAllowlistFromFile(body: string, sourcePath: string): CompiledAllowlistEntry[];
@@ -16,25 +16,25 @@
16
16
  * process. A hit on any of these patterns is a smoke failure unless the
17
17
  * line also matches the caller-supplied allowlist.
18
18
  *
19
- * Additions here should be conservative — false positives turn every
19
+ * Additions here should be conservative. False positives turn every
20
20
  * smoke run into noise for operators and every CI run into flake.
21
21
  */
22
22
  import { toError } from '../utils/errors.js';
23
- export const SMOKE_ERROR_PATTERNS = [
24
- // Firefox chrome error lines — `JavaScript error: chrome://…, line N: TypeError: …`.
23
+ const SMOKE_ERROR_PATTERNS = [
24
+ // Firefox chrome error lines: `JavaScript error: chrome://…, line N: TypeError: …`.
25
25
  /^\s*JavaScript error:/i,
26
26
  // Some log paths prefix browser-console `console.error(...)` with the literal label below.
27
27
  /^\s*console\.error:/i,
28
28
  // Older bracketed-prefix variant still seen in some chrome logs / test runs.
29
29
  /^\s*\[JavaScript (Error|Warning)\]/i,
30
- // IPC-layer fatal assertions — Firefox prints `###!!! [Parent] Error: …` on content-process crashes.
30
+ // IPC-layer fatal assertions. Firefox prints `###!!! [Parent] Error: …` on content-process crashes.
31
31
  /^\s*###!!! \[Parent\]/,
32
32
  // A chrome:// or resource:// URL that resolves to nothing. Outside
33
33
  // automation Gecko only PRINTS this (printf_stderr in
34
- // `CheckForBrokenChromeURL`, netwerk/base/nsNetUtil.cpp); under
34
+ // `CheckForBrokenChromeURL`, netwerk/base/nsNetUtil.cpp). Under
35
35
  // `xpc::IsInAutomation()` the same condition is a
36
36
  // `MOZ_CRASH_UNSAFE_PRINTF` in whichever process opened the channel. So
37
- // the smoke probe — which runs OUTSIDE automation — sees the printed
37
+ // the smoke probe, which runs outside automation, sees the printed
38
38
  // line for the same defect that hard-crashes every harness run, and a
39
39
  // probe that exits 0 on it reports "startup is healthy" for a build that
40
40
  // hangs at "Waiting for browser…". Both spellings are matched: the
@@ -44,7 +44,7 @@ export const SMOKE_ERROR_PATTERNS = [
44
44
  ];
45
45
  /**
46
46
  * Returns `true` when `line` matches any pattern in
47
- * {@link SMOKE_ERROR_PATTERNS}. Does not consult the allowlist — that step
47
+ * {@link SMOKE_ERROR_PATTERNS}. Does not consult the allowlist. That step
48
48
  * lives in {@link matchAllowlist}, so the smoke runner can count
49
49
  * allowlisted hits separately from raw error matches for its summary.
50
50
  */
@@ -58,23 +58,18 @@ export function matchesSmokeError(line) {
58
58
  }
59
59
  /**
60
60
  * Returns the index of the FIRST entry in `allow` matching `line`, or -1.
61
- * First-match attribution is deterministic and cheap; a line matching
61
+ * First-match attribution is deterministic and cheap. A line matching
62
62
  * several entries credits the earliest one. Safe to call with an empty
63
63
  * allowlist (always returns -1).
64
64
  */
65
65
  export function matchAllowlist(line, allow) {
66
- for (let i = 0; i < allow.length; i++) {
67
- if (allow[i]?.pattern.test(line)) {
68
- return i;
69
- }
70
- }
71
- return -1;
66
+ return allow.findIndex((entry) => entry.pattern.test(line));
72
67
  }
73
68
  /**
74
- * Parses a newline-delimited allowlist file body. Lines are trimmed; blank
69
+ * Parses a newline-delimited allowlist file body. Lines are trimmed. Blank
75
70
  * lines and `#`-prefixed comments are skipped. Each remaining line is
76
71
  * compiled as a RegExp with its `<file>:<line>` origin retained. A bad
77
- * pattern throws immediately — better to fail fast at CLI parse time than
72
+ * pattern throws immediately: better to fail fast at CLI parse time than
78
73
  * to silently let a typo match nothing.
79
74
  */
80
75
  export function compileAllowlistFromFile(body, sourcePath) {
@@ -88,12 +83,12 @@ export function compileAllowlistFromFile(body, sourcePath) {
88
83
  compiled.push({
89
84
  pattern: new RegExp(line),
90
85
  source: line,
91
- origin: `${sourcePath}:${String(index + 1)}`,
86
+ origin: `${sourcePath}:${index + 1}`,
92
87
  });
93
88
  }
94
89
  catch (error) {
95
90
  const message = toError(error).message;
96
- throw new Error(`Invalid allowlist regex at ${sourcePath}:${String(index + 1)}: ${message}`, {
91
+ throw new Error(`Invalid allowlist regex at ${sourcePath}:${index + 1}: ${message}`, {
97
92
  cause: error,
98
93
  });
99
94
  }
@@ -111,12 +106,12 @@ export function compileAllowlistFromStrings(sources) {
111
106
  compiled.push({
112
107
  pattern: new RegExp(source),
113
108
  source,
114
- origin: `--console-allow #${String(index + 1)}`,
109
+ origin: `--console-allow #${index + 1}`,
115
110
  });
116
111
  }
117
112
  catch (error) {
118
113
  const message = toError(error).message;
119
- throw new Error(`Invalid --console-allow regex at position ${String(index + 1)} ("${source}"): ${message}`, { cause: error });
114
+ throw new Error(`Invalid --console-allow regex at position ${index + 1} ("${source}"): ${message}`, { cause: error });
120
115
  }
121
116
  });
122
117
  return compiled;
@@ -1,21 +1,32 @@
1
1
  /** Runs an operation while holding the sidecar lock for a FireForge state file. */
2
2
  export declare function withStateFileLock<T>(statePath: string, operation: () => Promise<T>): Promise<T>;
3
+ /**
4
+ * A filesystem-safe, sortable timestamp: `2026-08-28T14-32-05-123Z`.
5
+ *
6
+ * Colons are not portable in filenames (Windows rejects them outright), so
7
+ * the ISO form is punctuated with dashes only. Lexical order still equals
8
+ * chronological order, which is what run-log pruning relies on.
9
+ *
10
+ * @param now - Instant to render
11
+ * @returns The ISO-8601 form with `:` and `.` replaced by `-`
12
+ */
13
+ export declare function fileSafeTimestamp(now: Date): string;
3
14
  /** Renames a state file out of the way while preserving it for later inspection. */
4
15
  export declare function quarantineStateFile(statePath: string, reason?: string): Promise<string | undefined>;
5
16
  /**
6
17
  * Outcome of a version check on a persisted FireForge document.
7
18
  *
8
19
  * The distinction that matters is `newer` vs `corrupt`. A document written
9
- * by a NEWER FireForge is not damaged — the operator simply needs to upgrade
10
- * — but a check that collapses the two offers destructive remedies. The
20
+ * by a newer FireForge is not damaged. The operator simply needs to upgrade.
21
+ * A check that collapses the two offers destructive remedies. The
11
22
  * sharpest case is the tree marker, whose `schemaVersion` check inside a
12
23
  * boolean type guard makes a `schemaVersion: 2` marker read as "missing or
13
- * mistypes a required field"; `tree-guard.ts` is default-deny on corrupt, so
24
+ * mistypes a required field". `tree-guard.ts` is default-deny on corrupt, so
14
25
  * a newer FireForge would lock the operator out of every mutating command
15
26
  * inside that tree.
16
27
  *
17
28
  * `furnace.json` is the pattern the rest should follow: it refuses a newer
18
- * version by name and tells the operator to upgrade (`migrateFurnaceConfig`
29
+ * version by name and tells the operator to upgrade (`validateConfigVersion`
19
30
  * in `furnace-config.ts`).
20
31
  */
21
32
  export type DocumentVersionCheck = {
@@ -12,12 +12,25 @@ export async function withStateFileLock(statePath, operation) {
12
12
  `(age: ${Math.round(ageMs / 1000)}s). A previous fireforge process may have crashed.`,
13
13
  });
14
14
  }
15
+ /**
16
+ * A filesystem-safe, sortable timestamp: `2026-08-28T14-32-05-123Z`.
17
+ *
18
+ * Colons are not portable in filenames (Windows rejects them outright), so
19
+ * the ISO form is punctuated with dashes only. Lexical order still equals
20
+ * chronological order, which is what run-log pruning relies on.
21
+ *
22
+ * @param now - Instant to render
23
+ * @returns The ISO-8601 form with `:` and `.` replaced by `-`
24
+ */
25
+ export function fileSafeTimestamp(now) {
26
+ return now.toISOString().replace(/[:.]/g, '-');
27
+ }
15
28
  /** Renames a state file out of the way while preserving it for later inspection. */
16
29
  export async function quarantineStateFile(statePath, reason = 'corrupt') {
17
30
  if (!(await pathExists(statePath))) {
18
31
  return undefined;
19
32
  }
20
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
33
+ const timestamp = fileSafeTimestamp(new Date());
21
34
  const quarantinedPath = `${statePath}.${reason}-${timestamp}`;
22
35
  await rename(statePath, quarantinedPath);
23
36
  return basename(quarantinedPath);