@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
@@ -3,7 +3,7 @@
3
3
  * FireForge CLI entry point, and the only file that may call process.exit().
4
4
  * Library code propagates errors via CommandError or FireForgeError instead.
5
5
  *
6
- * The signal pipeline below composes several lifecycle modules; see
6
+ * The signal pipeline below composes several lifecycle modules. See
7
7
  * docs/lifecycle-invariants.md for who owns what.
8
8
  */
9
9
  export {};
@@ -4,13 +4,14 @@
4
4
  * FireForge CLI entry point, and the only file that may call process.exit().
5
5
  * Library code propagates errors via CommandError or FireForgeError instead.
6
6
  *
7
- * The signal pipeline below composes several lifecycle modules; see
7
+ * The signal pipeline below composes several lifecycle modules. See
8
8
  * docs/lifecycle-invariants.md for who owns what.
9
9
  */
10
10
  import { installBrokenPipeHandler, main } from '../src/cli.js';
11
11
  import { emitKilledVerdict } from '../src/commands/test-verdict.js';
12
12
  import { forceReleaseHeldLocksForSignal } from '../src/core/file-lock.js';
13
13
  import { forceReleaseFurnaceLocksForActiveOperations, isSignalRollbackInFlight, rollbackActiveOperationsForSignal, } from '../src/core/furnace-operation.js';
14
+ import { closeActiveRunLog } from '../src/core/run-log.js';
14
15
  import { waitForActiveCriticalSections } from '../src/core/signal-critical.js';
15
16
  import { CommandError } from '../src/errors/base.js';
16
17
  import { waitForActiveChildShutdown } from '../src/utils/process.js';
@@ -27,22 +28,22 @@ const SIGNAL_CRITICAL_SECTION_TIMEOUT_MS = 5_000;
27
28
  * under `run`/`test`, mach under `build`) to shut down after the signal was
28
29
  * forwarded to them. Must exceed the largest child grace window
29
30
  * (execSmokeRun's killGraceMs default of 10 s) so the SIGTERM → grace →
30
- * SIGKILL escalation can complete; exiting earlier orphans hung Firefox
31
+ * SIGKILL escalation can complete. Exiting earlier orphans hung Firefox
31
32
  * trees. A second Ctrl+C escalates to SIGKILL immediately.
32
33
  */
33
34
  const CHILD_SHUTDOWN_TIMEOUT_MS = 12_000;
34
35
  /**
35
36
  * Upper bound (ms) a delayed exit waits for stdout/stderr to drain. A pipe
36
- * is ASYNC, so `process.exit()` discards anything queued past the 64 KiB
37
- * kernel buffer — enough to truncate a JSON payload mid-object. Bounded so
38
- * a stalled reader cannot wedge a failing process; an EPIPE'd/closed pipe
39
- * releases the wait immediately.
37
+ * is async, so `process.exit()` discards anything queued past the 64 KiB
38
+ * kernel buffer, which is enough to truncate a JSON payload mid-object.
39
+ * Bounded so a stalled reader cannot wedge a failing process. An
40
+ * EPIPE'd/closed pipe releases the wait immediately.
40
41
  */
41
42
  const STDIO_FLUSH_TIMEOUT_MS = 5_000;
42
43
  /**
43
- * Drains stdio, then exits. Every delayed exit routes through here — only
44
- * the second-Ctrl+C force-exit (an explicit "out NOW") and this helper's
45
- * own call terminate directly.
44
+ * Drains stdio, then exits. Every delayed exit routes through here. Only
45
+ * the second-Ctrl+C force-exit (an explicit request to quit at once) and
46
+ * this helper's own call terminate directly.
46
47
  */
47
48
  function exitAfterStdioFlush(code) {
48
49
  void waitForStdioDrain(STDIO_FLUSH_TIMEOUT_MS).finally(() => {
@@ -75,9 +76,9 @@ function installFurnaceSignalHandler(signal, exitCode) {
75
76
  // A diagnostic must never keep the process from exiting.
76
77
  }
77
78
  if (isSignalRollbackInFlight()) {
78
- // A second Ctrl+C while we're already rolling back is a noisy "I want
79
- // out now" — let the second signal terminate the process forcefully
80
- // rather than queueing another rollback that will race the first.
79
+ // A second Ctrl+C while we're already rolling back means the user
80
+ // wants out now, so let it terminate the process forcefully rather
81
+ // than queueing another rollback that will race the first.
81
82
  process.exit(exitCode);
82
83
  }
83
84
  // Rollback, critical-section drain, and child shutdown run in parallel.
@@ -86,7 +87,7 @@ function installFurnaceSignalHandler(signal, exitCode) {
86
87
  // applied to the engine but a stale session file that mis-tracks progress
87
88
  // on `--continue`. Waiting on child shutdown keeps the parent alive through
88
89
  // the SIGTERM → grace → SIGKILL escalation execInherit/execSmokeRun forward
89
- // to Firefox/mach; exiting first orphans those trees.
90
+ // to Firefox/mach. Exiting first orphans those trees.
90
91
  void Promise.allSettled([
91
92
  rollbackActiveOperationsForSignal(signal).catch((error) => {
92
93
  console.error(`Furnace rollback after ${signal} failed:`, error instanceof Error ? error.message : error);
@@ -97,11 +98,16 @@ function installFurnaceSignalHandler(signal, exitCode) {
97
98
  // `withFileLock`'s `finally { rm }` never runs across `process.exit`,
98
99
  // so both sweeps exist for the same reason. The generic one covers
99
100
  // every lock this process holds (engine session, patch directory,
100
- // build); the furnace one stays because it carries rollback
101
+ // build). The furnace one stays because it carries rollback
101
102
  // bookkeeping the generic sweep does not, and runs after it so the
102
103
  // ownership checks see a settled tree.
103
104
  .then(() => forceReleaseHeldLocksForSignal())
104
105
  .then(() => forceReleaseFurnaceLocksForActiveOperations())
106
+ // The run log's held partial line and queued writes do not survive
107
+ // `process.exit`. The command's own `finally` that would close it is
108
+ // racing this handler and usually loses. Best-effort like the log
109
+ // itself: a diagnostic must never keep the process from exiting.
110
+ .then(() => closeActiveRunLog().catch(() => undefined))
105
111
  .finally(() => {
106
112
  exitAfterStdioFlush(exitCode);
107
113
  });
@@ -114,7 +120,7 @@ main().catch((error) => {
114
120
  exitAfterStdioFlush(error.exitCode);
115
121
  return;
116
122
  }
117
- // Truly unexpected — CommandError should have been thrown by withErrorHandling
123
+ // Unexpected: withErrorHandling should have thrown a CommandError.
118
124
  console.error('Fatal error:', error);
119
125
  exitAfterStdioFlush(1);
120
126
  });
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.45.0",
4
- "commit": "19d461eb43b89962970510f9a2e655704e9fb952",
5
- "shortCommit": "19d461eb43b8",
3
+ "version": "0.46.1",
4
+ "commit": "821990fd7d076a205f37618055c0a01a95180aed",
5
+ "shortCommit": "821990fd7d07",
6
6
  "dirty": false,
7
7
  "dirtyHash": null,
8
- "buildTime": "2026-09-01T10:47:47.465Z"
8
+ "buildTime": "2026-09-05T22:29:34.415Z"
9
9
  }
@@ -0,0 +1,46 @@
1
+ import { FireForgeError } from './errors/base.js';
2
+ /**
3
+ * Stable machine-readable tag for an error class, for the `--json` envelope.
4
+ *
5
+ * Derived from the class name rather than hand-mapped: a new error class gets
6
+ * a sensible tag automatically, and the mapping cannot drift out of sync with
7
+ * the taxonomy. `ConfigNotFoundError` becomes `config-not-found`.
8
+ *
9
+ * @param error - The error being rendered
10
+ * @returns A kebab-case tag with the `Error` suffix stripped
11
+ */
12
+ export declare function machineErrorCode(error: FireForgeError): string;
13
+ /**
14
+ * Whether the invocation asked for machine-readable output.
15
+ *
16
+ * Reading the flag from argv is blunt but exact: if the invocation asked for
17
+ * machine output, stdout belongs to the payload from the first byte, before
18
+ * commander has parsed anything.
19
+ *
20
+ * @param argv - Process argv (defaults to `process.argv`)
21
+ * @returns True when `--json` or `--raw` is present
22
+ */
23
+ export declare function wantsMachineOutput(argv?: readonly string[]): boolean;
24
+ /**
25
+ * Converts a `CommanderError` thrown out of `parseAsync` into the exit
26
+ * contract, and rethrows anything else untouched.
27
+ *
28
+ * `--help` and `--version` also travel through `exitOverride()` (codes
29
+ * `commander.helpDisplayed` / `commander.version`, exit 0): commander has
30
+ * already written their output, so those simply return. So does
31
+ * `commander.help`: a command group invoked with no subcommand (`fireforge`,
32
+ * `fireforge tree`), for which commander prints the group's help and would
33
+ * exit 1. That is an informational invocation rather than a wrong flag.
34
+ * `patch`, `token` and `furnace` already answer it with help and exit 0
35
+ * through an explicit action, and the groups without one must not exit 8 (or
36
+ * leak commander's `(outputHelp)` placeholder into a `--json` envelope)
37
+ * merely for lacking it. A genuine usage error has already had its message
38
+ * written to stderr by commander. It becomes exit 8, and under `--json` it
39
+ * also gets the standard refusal envelope on stdout so a scripted consumer
40
+ * sees a parseable failure.
41
+ *
42
+ * @param error - Whatever `parseAsync` rejected with
43
+ * @param machineOutput - Whether the invocation asked for `--json`/`--raw`
44
+ * @throws CommandError carrying `INVALID_ARGUMENT` for a usage error
45
+ */
46
+ export declare function handleParseError(error: unknown, machineOutput: boolean): void;
@@ -0,0 +1,81 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Maps commander's own parse failures onto the FireForge exit-code contract.
4
+ *
5
+ * Commander terminates the process itself on a usage error (unknown option,
6
+ * missing argument, unknown command) with exit 1, which contradicts the
7
+ * documented `INVALID_ARGUMENT` (8) in `docs/exit-codes.md` and bypasses the
8
+ * `--json` refusal envelope because no action (and therefore no
9
+ * `withErrorHandling` wrapper) ever runs. `createProgram` installs
10
+ * `exitOverride()` so those failures surface as a thrown `CommanderError`,
11
+ * and `main` routes them through here.
12
+ */
13
+ import { CommanderError } from 'commander';
14
+ import { CommandError, FireForgeError, InvalidArgumentError } from './errors/base.js';
15
+ import { emitMachineError } from './utils/machine-output.js';
16
+ /**
17
+ * Stable machine-readable tag for an error class, for the `--json` envelope.
18
+ *
19
+ * Derived from the class name rather than hand-mapped: a new error class gets
20
+ * a sensible tag automatically, and the mapping cannot drift out of sync with
21
+ * the taxonomy. `ConfigNotFoundError` becomes `config-not-found`.
22
+ *
23
+ * @param error - The error being rendered
24
+ * @returns A kebab-case tag with the `Error` suffix stripped
25
+ */
26
+ export function machineErrorCode(error) {
27
+ return error.name
28
+ .replace(/Error$/, '')
29
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
30
+ .toLowerCase();
31
+ }
32
+ /**
33
+ * Whether the invocation asked for machine-readable output.
34
+ *
35
+ * Reading the flag from argv is blunt but exact: if the invocation asked for
36
+ * machine output, stdout belongs to the payload from the first byte, before
37
+ * commander has parsed anything.
38
+ *
39
+ * @param argv - Process argv (defaults to `process.argv`)
40
+ * @returns True when `--json` or `--raw` is present
41
+ */
42
+ export function wantsMachineOutput(argv = process.argv) {
43
+ return argv.includes('--json') || argv.includes('--raw');
44
+ }
45
+ /**
46
+ * Converts a `CommanderError` thrown out of `parseAsync` into the exit
47
+ * contract, and rethrows anything else untouched.
48
+ *
49
+ * `--help` and `--version` also travel through `exitOverride()` (codes
50
+ * `commander.helpDisplayed` / `commander.version`, exit 0): commander has
51
+ * already written their output, so those simply return. So does
52
+ * `commander.help`: a command group invoked with no subcommand (`fireforge`,
53
+ * `fireforge tree`), for which commander prints the group's help and would
54
+ * exit 1. That is an informational invocation rather than a wrong flag.
55
+ * `patch`, `token` and `furnace` already answer it with help and exit 0
56
+ * through an explicit action, and the groups without one must not exit 8 (or
57
+ * leak commander's `(outputHelp)` placeholder into a `--json` envelope)
58
+ * merely for lacking it. A genuine usage error has already had its message
59
+ * written to stderr by commander. It becomes exit 8, and under `--json` it
60
+ * also gets the standard refusal envelope on stdout so a scripted consumer
61
+ * sees a parseable failure.
62
+ *
63
+ * @param error - Whatever `parseAsync` rejected with
64
+ * @param machineOutput - Whether the invocation asked for `--json`/`--raw`
65
+ * @throws CommandError carrying `INVALID_ARGUMENT` for a usage error
66
+ */
67
+ export function handleParseError(error, machineOutput) {
68
+ if (!(error instanceof CommanderError)) {
69
+ throw error;
70
+ }
71
+ if (error.exitCode === 0 || error.code === 'commander.help') {
72
+ return;
73
+ }
74
+ // Commander's message carries its own `error: ` prefix. Strip it so the
75
+ // envelope text matches how every other refusal reads.
76
+ const usage = new InvalidArgumentError(error.message.replace(/^error:\s*/, ''));
77
+ if (machineOutput) {
78
+ emitMachineError(machineErrorCode(usage), usage.message, usage.code);
79
+ }
80
+ throw new CommandError(usage.code);
81
+ }
package/dist/src/cli.d.ts CHANGED
@@ -6,13 +6,18 @@ import { Command } from 'commander';
6
6
  * We treat this as a clean exit.
7
7
  */
8
8
  export declare function installBrokenPipeHandler(): void;
9
- /** Removes the broken-pipe handler installed for CLI tests. */
9
+ /**
10
+ * Removes the broken-pipe handler installed for CLI tests.
11
+ *
12
+ * @internal Exported only so tests can reach it. It is not part of the
13
+ * public surface.
14
+ */
10
15
  export declare function resetBrokenPipeHandlerForTests(): void;
11
16
  /**
12
17
  * Gets the project root directory.
13
18
  * Walks up from the current working directory until a fireforge.json is found.
14
19
  * Throws a {@link ConfigNotFoundError} (code: CONFIG_ERROR) when no
15
- * fireforge.json is found within the walk depth limit — the error is
20
+ * fireforge.json is found within the walk depth limit. The error is
16
21
  * user-facing so `withErrorHandling` can print the guidance without
17
22
  * the stack dump that a plain `Error` would trigger.
18
23
  */
package/dist/src/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { existsSync } from 'node:fs';
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
  import { Command, Help } from 'commander';
5
+ import { handleParseError, machineErrorCode, wantsMachineOutput } from './cli-usage-error.js';
5
6
  import { COMMAND_MANIFEST } from './commands/manifest.js';
6
7
  import { runTreeGuardHook } from './core/tree-guard.js';
7
8
  import { CancellationError, CommandError, FireForgeError, InternalInvariantError, } from './errors/base.js';
@@ -32,27 +33,11 @@ function getBrokenPipeHandler(state) {
32
33
  state[brokenPipeListenerKey] = handler;
33
34
  return handler;
34
35
  }
35
- /**
36
- * Stable machine-readable tag for an error class, for the `--json` envelope.
37
- *
38
- * Derived from the class name rather than hand-mapped: a new error class gets
39
- * a sensible tag automatically, and the mapping cannot drift out of sync with
40
- * the taxonomy. `ConfigNotFoundError` becomes `config-not-found`.
41
- *
42
- * @param error - The error being rendered
43
- * @returns A kebab-case tag with the `Error` suffix stripped
44
- */
45
- function machineErrorCode(error) {
46
- return error.name
47
- .replace(/Error$/, '')
48
- .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
49
- .toLowerCase();
50
- }
51
36
  /**
52
37
  * Prints an error's `cause` chain under `--verbose`.
53
38
  *
54
- * Nine error classes declare a `cause` and twenty-two throw sites pass one;
55
- * without this the underlying git stderr, errno or parse failure never
39
+ * Nine error classes declare a `cause` and twenty-two throw sites pass one.
40
+ * Without this the underlying git stderr, errno or parse failure never
56
41
  * reaches the operator, because a `FireForgeError` renders as its
57
42
  * `userMessage` and stops.
58
43
  *
@@ -93,7 +78,12 @@ export function installBrokenPipeHandler() {
93
78
  process.stderr.on('error', handleStreamError);
94
79
  state[brokenPipeInstalledKey] = true;
95
80
  }
96
- /** Removes the broken-pipe handler installed for CLI tests. */
81
+ /**
82
+ * Removes the broken-pipe handler installed for CLI tests.
83
+ *
84
+ * @internal Exported only so tests can reach it. It is not part of the
85
+ * public surface.
86
+ */
97
87
  export function resetBrokenPipeHandlerForTests() {
98
88
  const state = getProcessState();
99
89
  const handleStreamError = state[brokenPipeListenerKey];
@@ -108,14 +98,14 @@ export function resetBrokenPipeHandlerForTests() {
108
98
  * Maximum number of directory levels to walk when searching for
109
99
  * `fireforge.json`. `dirname()` walking is pure string manipulation and
110
100
  * cannot cycle (the `parent === current` check already terminates at the
111
- * root); this cap only bounds the cost on pathologically deep paths.
101
+ * root). This cap only bounds the cost on pathologically deep paths.
112
102
  */
113
103
  const MAX_PROJECT_ROOT_WALK_DEPTH = 50;
114
104
  /**
115
105
  * Gets the project root directory.
116
106
  * Walks up from the current working directory until a fireforge.json is found.
117
107
  * Throws a {@link ConfigNotFoundError} (code: CONFIG_ERROR) when no
118
- * fireforge.json is found within the walk depth limit — the error is
108
+ * fireforge.json is found within the walk depth limit. The error is
119
109
  * user-facing so `withErrorHandling` can print the guidance without
120
110
  * the stack dump that a plain `Error` would trigger.
121
111
  */
@@ -143,15 +133,15 @@ export function getProjectRoot() {
143
133
  */
144
134
  export function withErrorHandling(handler) {
145
135
  return async (...args) => {
146
- // Rule 3 of docs/machine-output.md: machine mode engages BEFORE any
147
- // output. Anything that throws on the way in — most visibly
148
- // `getProjectRoot`'s ConfigNotFoundError — would otherwise render a
136
+ // Rule 3 of docs/machine-output.md: machine mode engages before any
137
+ // output. Anything that throws on the way in (most visibly
138
+ // `getProjectRoot`'s ConfigNotFoundError) would otherwise render a
149
139
  // clack-styled block to stdout and leave a `--json` consumer with
150
140
  // un-parseable output. Reading the flag from argv is blunt but exact: if
151
141
  // the invocation asked for machine output, stdout belongs to the payload
152
142
  // from the first byte.
153
- const wantsMachineOutput = process.argv.includes('--json') || process.argv.includes('--raw');
154
- if (wantsMachineOutput) {
143
+ const machineOutput = wantsMachineOutput();
144
+ if (machineOutput) {
155
145
  setMachineOutputMode(true);
156
146
  }
157
147
  try {
@@ -166,9 +156,9 @@ export function withErrorHandling(handler) {
166
156
  }
167
157
  if (error instanceof CancellationError) {
168
158
  cancel('Operation cancelled');
169
- // 130 (128+SIGINT) is the conventional "user interrupted" code —
170
- // scripts/CI can distinguish a deliberate prompt cancellation from
171
- // a real failure (which exits 1).
159
+ // 130 (128+SIGINT) is the conventional "user interrupted" code, so
160
+ // scripts/CI can distinguish a prompt cancellation from a real
161
+ // failure (which exits 1).
172
162
  throw new CommandError(ExitCode.USER_CANCELLED);
173
163
  }
174
164
  // An invariant failure is the one FireForgeError whose stack is part
@@ -189,9 +179,9 @@ export function withErrorHandling(handler) {
189
179
  logError(error.userMessage);
190
180
  printCauseChain(error);
191
181
  // Not when a payload already owns stdout: `status --json --fail-on`
192
- // writes its full document and THEN refuses, and appending an
182
+ // writes its full document and then refuses, and appending an
193
183
  // envelope would make that two JSON documents.
194
- if (wantsMachineOutput && !isStdoutSealed()) {
184
+ if (machineOutput && !isStdoutSealed()) {
195
185
  emitMachineError(machineErrorCode(error), error.message, error.code);
196
186
  }
197
187
  throw new CommandError(error.code);
@@ -205,9 +195,9 @@ export function withErrorHandling(handler) {
205
195
  }
206
196
  finally {
207
197
  // Central machine-mode and stdout-seal reset. Commands leave both
208
- // ENGAGED while an error propagates (a mid-throw restore would route
198
+ // engaged while an error propagates (a mid-throw restore would route
209
199
  // the styled error to stdout, corrupting the `--json`/`--raw` payload
210
- // stream or displacing the FIREFORGE-VERDICT line); the reset happens
200
+ // stream or displacing the FIREFORGE-VERDICT line). The reset happens
211
201
  // here, after logError has picked its stream, so no state leaks into a
212
202
  // subsequent in-process invocation.
213
203
  setMachineOutputMode(false);
@@ -255,9 +245,9 @@ function buildGroupedHelpFormatter(manifest) {
255
245
  return formatHelpLine(term, desc, termWidth, helpWidth);
256
246
  });
257
247
  // -V/--version is handled in main() before commander parses (a real
258
- // root-level `.version()` option would claim `--version` ANYWHERE in
248
+ // root-level `.version()` option would claim `--version` anywhere in
259
249
  // argv under commander's default parsing, breaking subcommand flags
260
- // like `source set --version <v>`); advertise it here so root help
250
+ // like `source set --version <v>`). Advertise it here so root help
261
251
  // stays truthful.
262
252
  optionLines.unshift(formatHelpLine('-V, --version', 'output the version number', termWidth, helpWidth));
263
253
  if (optionLines.length > 0) {
@@ -339,6 +329,11 @@ export function createProgram() {
339
329
  const program = new Command();
340
330
  program
341
331
  .name('fireforge')
332
+ // Usage errors must not let commander call process.exit(1) itself: the
333
+ // documented code is INVALID_ARGUMENT (8) and `--json` owes a refusal
334
+ // envelope. Installed before any subcommand registers, because
335
+ // `.command()` copies the override into the child at creation time.
336
+ .exitOverride()
342
337
  .description('A build tool for customizing Firefox')
343
338
  .option('-v, --verbose', 'Enable debug output')
344
339
  .option('--ignore-corrupt-tree-marker', 'Run even when .fireforge/tree.json exists but cannot be parsed. Without this the tree guard refuses, because an unreadable marker leaves it unknown whether this is a snapshot or the primary tree.')
@@ -348,9 +343,9 @@ export function createProgram() {
348
343
  setVerbose(true);
349
344
  }
350
345
  })
351
- // Verification-tree guard: when the cwd resolves into a
352
- // tree snapshot, refuse mutating commands before their action runs —
353
- // one hook covers every command regardless of how the cwd got there.
346
+ // Verification-tree guard: when the cwd resolves into a tree snapshot,
347
+ // refuse mutating commands before their action runs. One hook covers
348
+ // every command regardless of how the cwd got there.
354
349
  .hook('preAction', async (thisCommand, actionCommand) => {
355
350
  await runTreeGuardHook(thisCommand.name(), actionCommand);
356
351
  });
@@ -365,7 +360,7 @@ export function createProgram() {
365
360
  // Uniform `--wait-lock`: scripted sequences blanket-append the flag, and a
366
361
  // subcommand that rejects it with "unknown option" kills the sequence with
367
362
  // a usage error instead of a lock message. Applied after registration so it
368
- // covers every subcommand at every depth; commands that already declare the
363
+ // covers every subcommand at every depth. Commands that already declare the
369
364
  // honoring flag keep it untouched.
370
365
  ensureWaitLockOptionEverywhere(program);
371
366
  return program;
@@ -374,13 +369,13 @@ export function createProgram() {
374
369
  * Main CLI entry point.
375
370
  */
376
371
  export async function main() {
377
- // Root-level --version/-V handling. Deliberately NOT a commander
378
- // `.version()` option: under commander's default (non-positional)
379
- // parsing, a root version option claims `--version` anywhere in argv and
380
- // hijacks subcommand flags like `source set --version <v>`. The rule
381
- // here: when NO subcommand was given, any -V/--version among the root
382
- // flags prints the version — so `fireforge --verbose --version` works,
383
- // and `fireforge source set --version 152` is untouched.
372
+ // Root-level --version/-V handling. This is not a commander `.version()`
373
+ // option, because under commander's default (non-positional) parsing a
374
+ // root version option claims `--version` anywhere in argv and hijacks
375
+ // subcommand flags like `source set --version <v>`. The rule here: when no
376
+ // subcommand was given, any -V/--version among the root flags prints the
377
+ // version, so `fireforge --verbose --version` works and
378
+ // `fireforge source set --version 152` is untouched.
384
379
  const userArgs = process.argv.slice(2);
385
380
  const hasSubcommand = userArgs.some((arg) => !arg.startsWith('-'));
386
381
  if (!hasSubcommand && userArgs.some((arg) => arg === '--version' || arg === '-V')) {
@@ -388,5 +383,10 @@ export async function main() {
388
383
  return;
389
384
  }
390
385
  const program = createProgram();
391
- await program.parseAsync(process.argv);
386
+ try {
387
+ await program.parseAsync(process.argv);
388
+ }
389
+ catch (error) {
390
+ handleParseError(error, wantsMachineOutput());
391
+ }
392
392
  }
@@ -13,7 +13,8 @@ export function detectBootstrapIssues(output) {
13
13
  const hasTraceback = /traceback \(most recent call last\):/i.test(normalized);
14
14
  const has403 = /\bhttp(?:\s+error)?\s*403\b/i.test(normalized) || /\b403\b.*forbidden/i.test(normalized);
15
15
  if (has403) {
16
- // The traceback is just the stack trace from the HTTP error — report once.
16
+ // The traceback is just the stack trace from the HTTP error, so report
17
+ // it once.
17
18
  issues.push('sdk-fetch-403');
18
19
  }
19
20
  else if (hasTraceback) {
@@ -10,7 +10,6 @@ import { ExitCode } from '../errors/codes.js';
10
10
  import { error, info, intro, outro, warn } from '../utils/logger.js';
11
11
  import { detectBootstrapIssues, runPostBootstrapChecks, } from './bootstrap-checks.js';
12
12
  import { reportDoctorResults } from './doctor.js';
13
- import { resolveDoctorSeverity } from './doctor-check-core.js';
14
13
  /** One sentence per detected issue, keyed by the scanner's own tags. */
15
14
  const BOOTSTRAP_FAILURE_SENTENCES = {
16
15
  'python-traceback': 'Bootstrap emitted a Python traceback.',
@@ -24,7 +23,7 @@ const BOOTSTRAP_FAILURE_SENTENCES = {
24
23
  function buildBootstrapFailureMessage(output) {
25
24
  // Delegates detection to the canonical scanner rather than keeping a
26
25
  // second copy of its six regexes. The copies had already disagreed: this
27
- // one reported a traceback AND a 403 separately, while
26
+ // one reported a traceback and a 403 separately, while
28
27
  // `detectBootstrapIssues` collapses them, because a bootstrap traceback
29
28
  // accompanying a 403 is just the stack trace from that HTTP error.
30
29
  const issues = detectBootstrapIssues(output).map((issue) => BOOTSTRAP_FAILURE_SENTENCES[issue]);
@@ -68,7 +67,7 @@ export async function bootstrapCommand(projectRoot) {
68
67
  // Shares one resolver with reportDoctorResults so the two consumers of
69
68
  // this same array cannot drift apart on how a check's severity is
70
69
  // derived.
71
- const hasErrors = checks.some((c) => resolveDoctorSeverity(c) === 'error');
70
+ const hasErrors = checks.some((c) => c.severity === 'error');
72
71
  info('');
73
72
  if (hasErrors) {
74
73
  warn('Bootstrap completed with issues:');
@@ -80,7 +79,7 @@ export async function bootstrapCommand(projectRoot) {
80
79
  if (hasErrors) {
81
80
  outro('Build dependencies installed with errors');
82
81
  // Propagate the failure (mirroring doctor's exit-code handling)
83
- // instead of exiting 0 — CI gating on bootstrap used to proceed to a
82
+ // instead of exiting 0. CI gating on bootstrap used to proceed to a
84
83
  // build that could not succeed because error-severity check results
85
84
  // were discarded here.
86
85
  return checksExitCode;
@@ -13,6 +13,7 @@ import { buildHarnessCrashMessage } from '../core/test-harness-crash.js';
13
13
  import { formatToolchainMismatchMessage, runToolchainPreflight, } from '../core/toolchain-preflight.js';
14
14
  import { GeneralError, InvalidArgumentError } from '../errors/base.js';
15
15
  import { AmbiguousBuildArtifactsError, BuildError } from '../errors/build.js';
16
+ import { elapsedSince } from '../utils/elapsed.js';
16
17
  import { toError } from '../utils/errors.js';
17
18
  import { checkDiskSpace } from '../utils/fs.js';
18
19
  import { error, info, intro, outro, spinner, verbose, warn } from '../utils/logger.js';
@@ -29,9 +30,9 @@ function parseJobCount(value) {
29
30
  * Patches `mozinfo.json` under the active obj-* directory so its recorded
30
31
  * paths match the current engine checkout, then runs `mach configure` to
31
32
  * regenerate every other generated path alongside it. Throws with the
32
- * original mismatch guidance when the rewriter refuses to proceed — that
33
- * always covers the unsafe cases, so the operator still gets the correct
34
- * fallback recovery instruction.
33
+ * original mismatch guidance when the rewriter refuses to proceed. That
34
+ * covers the unsafe cases, so the operator still gets the correct fallback
35
+ * recovery instruction.
35
36
  */
36
37
  async function rewriteAndReconfigure(engineDir, objDir, mismatchMessage) {
37
38
  const rewriteSpinner = spinner('Rewriting mozinfo.json paths...');
@@ -200,7 +201,7 @@ async function runBuildCommandBody(projectRoot, options) {
200
201
  // and fail fast naming `fireforge bootstrap` instead of dying ~8s into
201
202
  // configure with mach's "./mach bootstrap" remediation text. Fail-soft by
202
203
  // design: only a definitively parsed minimum vs a definitively probed host
203
- // version can fail here; anything uncertain proceeds to mach, where the
204
+ // version can fail here. Anything uncertain proceeds to mach, where the
204
205
  // mach-error-hints translator still names the right remedy.
205
206
  const toolchainMismatches = await runToolchainPreflight(paths.engine);
206
207
  if (toolchainMismatches.length > 0) {
@@ -216,7 +217,7 @@ async function runBuildCommandBody(projectRoot, options) {
216
217
  // Safe-relocation rewrite path: patch mozinfo.json paths in place so
217
218
  // `mach configure` can regenerate the backend without scrubbing the
218
219
  // whole obj tree. The rewriter refuses anything that is not a pure
219
- // prefix-move; on refusal we surface the refusal reason alongside
220
+ // prefix-move. On refusal we surface the refusal reason alongside
220
221
  // the original mismatch guidance and abort.
221
222
  await rewriteAndReconfigure(paths.engine, buildCheck.objDir, mismatchMessage);
222
223
  }
@@ -274,7 +275,7 @@ async function runBuildCommandBody(projectRoot, options) {
274
275
  // overlapping `fireforge build` / `fireforge build --ui` commands
275
276
  // against the same engine tree serialise instead of racing through the
276
277
  // same obj-*. A `build --ui` launched during an in-progress full build
277
- // otherwise hits `No rule to make target 'XUL'` in mach — the
278
+ // otherwise hits `No rule to make target 'XUL'` in mach, the
278
279
  // downstream consequence of an incomplete backend, with no clue that a
279
280
  // concurrent build caused it. The lock turns the second invocation's
280
281
  // failure into an explicit refusal naming the holder PID.
@@ -288,10 +289,7 @@ async function runBuildCommandBody(projectRoot, options) {
288
289
  catch (error) {
289
290
  throw new BuildError('Build process failed to start', options.ui ? 'mach build faster' : 'mach build', error instanceof Error ? error : undefined);
290
291
  }
291
- const duration = Date.now() - startTime;
292
- const minutes = Math.floor(duration / 60000);
293
- const seconds = Math.floor((duration % 60000) / 1000);
294
- const timeStr = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
292
+ const timeStr = elapsedSince(startTime);
295
293
  if (result.exitCode !== 0) {
296
294
  error(`Build failed after ${timeStr}`);
297
295
  const machCommand = options.ui ? 'mach build faster' : 'mach build';
@@ -315,9 +313,10 @@ async function runBuildCommandBody(projectRoot, options) {
315
313
  // Matching only (1) leaves operators on the (2) path seeing mach's own
316
314
  // "Configure complete!" and "run |mach build|" lines unexplained between
317
315
  // mach's "Your build was successful!" and FireForge's "Build completed in
318
- // Xm Ys" outro — a contradictory tail. Both shapes route through the same
319
- // annotation, emitted BEFORE FireForge's outro so the operator's last
320
- // terminal line is the explanation, not the confusing mach guard text.
316
+ // Xm Ys" outro, a contradictory tail. Both shapes route through the same
317
+ // annotation, emitted before FireForge's outro so the operator's last
318
+ // terminal line is the explanation instead of the confusing mach guard
319
+ // text.
321
320
  const staleConfigurePatterns = [
322
321
  /config\.status is out of date/i,
323
322
  /Config object not found by mach\.[\s\S]*Configure complete!/i,
@@ -331,10 +330,12 @@ async function runBuildCommandBody(projectRoot, options) {
331
330
  }
332
331
  // Warn-only post-build audit: surfaces silent packaging drops (files
333
332
  // edited in engine/ but never registered for packaging) against the
334
- // previous-build baseline. Never fails the build; the worst case is a
333
+ // previous-build baseline. Never fails the build. The worst case is a
335
334
  // warning an operator chooses to investigate.
336
335
  try {
337
- await auditBuildArtifacts(projectRoot, paths.engine, previousBaseline);
336
+ await auditBuildArtifacts(projectRoot, paths.engine, previousBaseline, {
337
+ ...(config.buildAudit?.unpackaged ? { unpackaged: config.buildAudit.unpackaged } : {}),
338
+ });
338
339
  }
339
340
  catch (auditError) {
340
341
  verbose(`Audit skipped: ${toError(auditError).message}`);
@@ -346,14 +347,23 @@ async function runBuildCommandBody(projectRoot, options) {
346
347
  outro(`Build completed in ${timeStr}!`);
347
348
  }
348
349
  /**
349
- * Persists the full-coverage baseline; a failed write never fails the build.
350
+ * Persists the full-coverage baseline. A failed write never fails the build.
350
351
  * `--ui` is a `mach build faster`, so it is recorded as such: its `jar.mn`
351
352
  * fingerprints are carried forward from the previous baseline rather than
352
353
  * refreshed, which is why that baseline is threaded through.
353
354
  */
354
355
  async function recordFullBuildBaseline(projectRoot, engineDir, binaryName, ui, previousBaseline) {
355
356
  try {
356
- await writeBuildBaseline(projectRoot, engineDir, binaryName, 'full', previousBaseline, ui ? 'fireforge build --ui' : 'fireforge build', 'auto', ui ? 'faster' : 'full');
357
+ await writeBuildBaseline({
358
+ projectRoot,
359
+ engineDir,
360
+ binaryName,
361
+ testPackagingCoverage: 'full',
362
+ previousBaseline,
363
+ recordedBy: ui ? 'fireforge build --ui' : 'fireforge build',
364
+ staticComponentsHandling: 'auto',
365
+ buildKind: ui ? 'faster' : 'full',
366
+ });
357
367
  }
358
368
  catch (baselineError) {
359
369
  verbose(`Could not persist build baseline: ${toError(baselineError).message}`);