@hominis/fireforge 0.43.1 → 0.44.0

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 (470) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +10 -1
  3. package/dist/bin/fireforge.d.ts +4 -9
  4. package/dist/bin/fireforge.js +42 -40
  5. package/dist/build-info.json +4 -4
  6. package/dist/src/cli.js +74 -8
  7. package/dist/src/commands/bootstrap.js +17 -20
  8. package/dist/src/commands/build.js +25 -32
  9. package/dist/src/commands/config.js +34 -41
  10. package/dist/src/commands/discard.js +8 -9
  11. package/dist/src/commands/doctor-check-core.d.ts +4 -18
  12. package/dist/src/commands/doctor-check-core.js +8 -35
  13. package/dist/src/commands/doctor-external-toolchains.js +2 -2
  14. package/dist/src/commands/doctor-furnace-config-sync.d.ts +18 -0
  15. package/dist/src/commands/{doctor-furnace-manifest-sync.js → doctor-furnace-config-sync.js} +27 -26
  16. package/dist/src/commands/doctor-furnace-jar.d.ts +7 -9
  17. package/dist/src/commands/doctor-furnace-jar.js +7 -9
  18. package/dist/src/commands/doctor-furnace.js +21 -24
  19. package/dist/src/commands/doctor-orphaned-harness.d.ts +16 -20
  20. package/dist/src/commands/doctor-orphaned-harness.js +18 -44
  21. package/dist/src/commands/doctor-post-rebase-audit.d.ts +2 -0
  22. package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
  23. package/dist/src/commands/doctor-working-tree.d.ts +10 -12
  24. package/dist/src/commands/doctor-working-tree.js +11 -18
  25. package/dist/src/commands/doctor.js +35 -53
  26. package/dist/src/commands/download.js +32 -48
  27. package/dist/src/commands/export-all.js +29 -33
  28. package/dist/src/commands/export-flow.js +6 -6
  29. package/dist/src/commands/export-placement-conflicts.js +10 -9
  30. package/dist/src/commands/export-placement-gate.js +5 -5
  31. package/dist/src/commands/export-shared.d.ts +30 -33
  32. package/dist/src/commands/export-shared.js +45 -43
  33. package/dist/src/commands/export.js +21 -27
  34. package/dist/src/commands/furnace/apply.js +14 -23
  35. package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
  36. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
  37. package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
  38. package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
  39. package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
  40. package/dist/src/commands/furnace/chrome-doc.js +31 -41
  41. package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
  42. package/dist/src/commands/furnace/create-browser-test.js +8 -8
  43. package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
  44. package/dist/src/commands/furnace/create-mochikit.js +8 -9
  45. package/dist/src/commands/furnace/create-readback.d.ts +12 -13
  46. package/dist/src/commands/furnace/create-readback.js +12 -13
  47. package/dist/src/commands/furnace/create-templates.d.ts +22 -28
  48. package/dist/src/commands/furnace/create-templates.js +35 -30
  49. package/dist/src/commands/furnace/create-xpcshell.js +2 -2
  50. package/dist/src/commands/furnace/create.js +33 -23
  51. package/dist/src/commands/furnace/deploy.js +16 -24
  52. package/dist/src/commands/furnace/diff.js +9 -9
  53. package/dist/src/commands/furnace/init.js +33 -36
  54. package/dist/src/commands/furnace/override.js +23 -25
  55. package/dist/src/commands/furnace/preview.js +47 -67
  56. package/dist/src/commands/furnace/refresh.js +12 -16
  57. package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
  58. package/dist/src/commands/furnace/remove-mutations.js +9 -16
  59. package/dist/src/commands/furnace/remove-state.js +1 -1
  60. package/dist/src/commands/furnace/remove.js +33 -38
  61. package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
  62. package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
  63. package/dist/src/commands/furnace/rename.js +59 -69
  64. package/dist/src/commands/furnace/scan.js +19 -33
  65. package/dist/src/commands/furnace/status.js +12 -9
  66. package/dist/src/commands/furnace/sync.js +9 -16
  67. package/dist/src/commands/furnace/validate.js +20 -20
  68. package/dist/src/commands/import.js +42 -35
  69. package/dist/src/commands/lint-per-patch.js +15 -10
  70. package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
  71. package/dist/src/commands/lint-per-run-checkjs.js +13 -13
  72. package/dist/src/commands/lint-report.js +6 -7
  73. package/dist/src/commands/lint.d.ts +15 -16
  74. package/dist/src/commands/lint.js +86 -102
  75. package/dist/src/commands/package.js +6 -11
  76. package/dist/src/commands/patch/compact.js +1 -1
  77. package/dist/src/commands/patch/delete.js +24 -33
  78. package/dist/src/commands/patch/index.d.ts +5 -6
  79. package/dist/src/commands/patch/index.js +10 -12
  80. package/dist/src/commands/patch/lint-ignore.js +14 -13
  81. package/dist/src/commands/patch/move-files-create.d.ts +6 -6
  82. package/dist/src/commands/patch/move-files-create.js +8 -8
  83. package/dist/src/commands/patch/move-files-into.d.ts +6 -6
  84. package/dist/src/commands/patch/move-files-into.js +8 -8
  85. package/dist/src/commands/patch/patch-context.d.ts +2 -3
  86. package/dist/src/commands/patch/patch-context.js +2 -3
  87. package/dist/src/commands/patch/rename.d.ts +10 -11
  88. package/dist/src/commands/patch/rename.js +11 -12
  89. package/dist/src/commands/patch/reorder.js +8 -9
  90. package/dist/src/commands/patch/split-plan.js +1 -1
  91. package/dist/src/commands/patch/split.d.ts +8 -10
  92. package/dist/src/commands/patch/split.js +9 -11
  93. package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
  94. package/dist/src/commands/patch/staged-dependency.js +7 -3
  95. package/dist/src/commands/patch/tier.d.ts +9 -9
  96. package/dist/src/commands/patch/tier.js +18 -15
  97. package/dist/src/commands/re-export-adjacent.js +9 -8
  98. package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
  99. package/dist/src/commands/re-export-bulk-scan.js +22 -21
  100. package/dist/src/commands/re-export-drift.js +10 -10
  101. package/dist/src/commands/re-export-files.d.ts +6 -7
  102. package/dist/src/commands/re-export-files.js +34 -44
  103. package/dist/src/commands/re-export-lint.d.ts +10 -13
  104. package/dist/src/commands/re-export-lint.js +16 -14
  105. package/dist/src/commands/re-export-options.d.ts +6 -6
  106. package/dist/src/commands/re-export-options.js +6 -6
  107. package/dist/src/commands/re-export-register.d.ts +4 -10
  108. package/dist/src/commands/re-export-register.js +5 -11
  109. package/dist/src/commands/re-export-scan.js +1 -1
  110. package/dist/src/commands/re-export-single.d.ts +3 -3
  111. package/dist/src/commands/re-export-single.js +33 -25
  112. package/dist/src/commands/re-export.js +13 -21
  113. package/dist/src/commands/rebase/abort.js +3 -3
  114. package/dist/src/commands/rebase/confirm.js +2 -1
  115. package/dist/src/commands/rebase/continue.d.ts +1 -1
  116. package/dist/src/commands/rebase/continue.js +14 -9
  117. package/dist/src/commands/rebase/index.js +11 -11
  118. package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
  119. package/dist/src/commands/rebase/patch-loop.js +60 -39
  120. package/dist/src/commands/rebase/summary.js +1 -1
  121. package/dist/src/commands/register.js +5 -7
  122. package/dist/src/commands/reset.js +2 -1
  123. package/dist/src/commands/resolve.d.ts +16 -13
  124. package/dist/src/commands/resolve.js +30 -33
  125. package/dist/src/commands/run.js +33 -35
  126. package/dist/src/commands/setup-support.js +15 -19
  127. package/dist/src/commands/setup.js +13 -9
  128. package/dist/src/commands/status-check.d.ts +6 -6
  129. package/dist/src/commands/status-json.d.ts +6 -6
  130. package/dist/src/commands/status-json.js +5 -0
  131. package/dist/src/commands/status-output.d.ts +5 -5
  132. package/dist/src/commands/status-output.js +6 -6
  133. package/dist/src/commands/status-ownership.d.ts +16 -14
  134. package/dist/src/commands/status-ownership.js +16 -14
  135. package/dist/src/commands/status.js +42 -34
  136. package/dist/src/commands/test-build-phase.js +26 -13
  137. package/dist/src/commands/test-diagnose.js +44 -44
  138. package/dist/src/commands/test-register.js +1 -0
  139. package/dist/src/commands/test-run.d.ts +14 -16
  140. package/dist/src/commands/test-run.js +21 -13
  141. package/dist/src/commands/test-stale-gate.d.ts +15 -16
  142. package/dist/src/commands/test-stale-gate.js +15 -16
  143. package/dist/src/commands/test-verdict.d.ts +20 -3
  144. package/dist/src/commands/test-verdict.js +30 -0
  145. package/dist/src/commands/test.js +83 -76
  146. package/dist/src/commands/token-coverage.js +3 -4
  147. package/dist/src/commands/token.js +16 -18
  148. package/dist/src/commands/tree.js +47 -31
  149. package/dist/src/commands/typecheck.d.ts +10 -12
  150. package/dist/src/commands/typecheck.js +24 -27
  151. package/dist/src/commands/verify.js +13 -15
  152. package/dist/src/commands/watch.js +21 -23
  153. package/dist/src/commands/wire.js +52 -62
  154. package/dist/src/core/ast-utils.d.ts +8 -7
  155. package/dist/src/core/ast-utils.js +8 -7
  156. package/dist/src/core/branding.d.ts +9 -14
  157. package/dist/src/core/branding.js +7 -11
  158. package/dist/src/core/browser-wire.d.ts +1 -1
  159. package/dist/src/core/browser-wire.js +14 -19
  160. package/dist/src/core/build-audit-platform.js +25 -33
  161. package/dist/src/core/build-audit-registration.js +17 -25
  162. package/dist/src/core/build-audit-resolve.d.ts +15 -17
  163. package/dist/src/core/build-audit-resolve.js +27 -33
  164. package/dist/src/core/build-audit-transforms.js +18 -19
  165. package/dist/src/core/build-audit.js +28 -42
  166. package/dist/src/core/build-baseline-types.d.ts +29 -35
  167. package/dist/src/core/build-baseline.d.ts +5 -4
  168. package/dist/src/core/build-baseline.js +14 -14
  169. package/dist/src/core/build-overwrite-guard.d.ts +7 -14
  170. package/dist/src/core/build-overwrite-guard.js +17 -27
  171. package/dist/src/core/build-prepare.d.ts +2 -2
  172. package/dist/src/core/build-prepare.js +8 -9
  173. package/dist/src/core/config-state.js +12 -4
  174. package/dist/src/core/config-validate.js +8 -5
  175. package/dist/src/core/config.d.ts +5 -8
  176. package/dist/src/core/config.js +7 -16
  177. package/dist/src/core/coverage-extend.d.ts +15 -13
  178. package/dist/src/core/coverage-extend.js +15 -13
  179. package/dist/src/core/destructive.d.ts +31 -9
  180. package/dist/src/core/destructive.js +29 -26
  181. package/dist/src/core/diff-hunks.js +10 -12
  182. package/dist/src/core/discard-baseline.d.ts +8 -8
  183. package/dist/src/core/discard-baseline.js +16 -15
  184. package/dist/src/core/display-state.d.ts +12 -14
  185. package/dist/src/core/display-state.js +12 -14
  186. package/dist/src/core/engine-changes.d.ts +2 -4
  187. package/dist/src/core/engine-changes.js +2 -4
  188. package/dist/src/core/engine-precondition.d.ts +28 -13
  189. package/dist/src/core/engine-precondition.js +25 -10
  190. package/dist/src/core/engine-session-lock.d.ts +47 -21
  191. package/dist/src/core/engine-session-lock.js +168 -33
  192. package/dist/src/core/export-moved-code-shape.d.ts +10 -10
  193. package/dist/src/core/export-moved-code-shape.js +10 -10
  194. package/dist/src/core/file-lock.d.ts +62 -5
  195. package/dist/src/core/file-lock.js +196 -59
  196. package/dist/src/core/firefox-cache.js +1 -3
  197. package/dist/src/core/firefox-extract.js +10 -14
  198. package/dist/src/core/firefox.d.ts +7 -5
  199. package/dist/src/core/firefox.js +4 -5
  200. package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
  201. package/dist/src/core/furnace-apply-dry-run.js +3 -2
  202. package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
  203. package/dist/src/core/furnace-apply-ftl.js +12 -12
  204. package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
  205. package/dist/src/core/furnace-apply-helpers.js +19 -16
  206. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
  207. package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
  208. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
  209. package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
  210. package/dist/src/core/furnace-apply.d.ts +5 -8
  211. package/dist/src/core/furnace-apply.js +19 -24
  212. package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
  213. package/dist/src/core/furnace-checksum-utils.js +16 -0
  214. package/dist/src/core/furnace-config.d.ts +21 -30
  215. package/dist/src/core/furnace-config.js +32 -54
  216. package/dist/src/core/furnace-constants.d.ts +8 -0
  217. package/dist/src/core/furnace-constants.js +10 -2
  218. package/dist/src/core/furnace-css-fragments.d.ts +5 -5
  219. package/dist/src/core/furnace-css-fragments.js +5 -5
  220. package/dist/src/core/furnace-jsconfig.d.ts +11 -12
  221. package/dist/src/core/furnace-jsconfig.js +11 -12
  222. package/dist/src/core/furnace-marker.d.ts +7 -8
  223. package/dist/src/core/furnace-marker.js +7 -8
  224. package/dist/src/core/furnace-operation.d.ts +31 -0
  225. package/dist/src/core/furnace-operation.js +54 -17
  226. package/dist/src/core/furnace-precondition.d.ts +61 -0
  227. package/dist/src/core/furnace-precondition.js +67 -0
  228. package/dist/src/core/furnace-registration-remove.d.ts +5 -6
  229. package/dist/src/core/furnace-registration-remove.js +6 -7
  230. package/dist/src/core/furnace-registration-validate.d.ts +13 -0
  231. package/dist/src/core/furnace-registration-validate.js +20 -1
  232. package/dist/src/core/furnace-registration.d.ts +5 -6
  233. package/dist/src/core/furnace-registration.js +10 -12
  234. package/dist/src/core/furnace-rollback.js +7 -7
  235. package/dist/src/core/furnace-scanner.d.ts +1 -0
  236. package/dist/src/core/furnace-scanner.js +1 -2
  237. package/dist/src/core/furnace-stale-export.d.ts +10 -10
  238. package/dist/src/core/furnace-stale-export.js +10 -10
  239. package/dist/src/core/furnace-state-persist.d.ts +6 -8
  240. package/dist/src/core/furnace-state-persist.js +6 -8
  241. package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
  242. package/dist/src/core/furnace-validate-accessibility.js +310 -1
  243. package/dist/src/core/furnace-validate-compatibility.js +141 -1
  244. package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
  245. package/dist/src/core/furnace-validate-helpers.js +0 -491
  246. package/dist/src/core/furnace-validate-registration.d.ts +7 -0
  247. package/dist/src/core/furnace-validate-registration.js +115 -41
  248. package/dist/src/core/furnace-validate.d.ts +16 -0
  249. package/dist/src/core/furnace-validate.js +188 -145
  250. package/dist/src/core/furnace-version-drift.d.ts +10 -11
  251. package/dist/src/core/furnace-version-drift.js +10 -11
  252. package/dist/src/core/git-base.d.ts +7 -9
  253. package/dist/src/core/git-base.js +7 -9
  254. package/dist/src/core/git-diff.d.ts +20 -9
  255. package/dist/src/core/git-diff.js +148 -63
  256. package/dist/src/core/git-file-ops.d.ts +19 -19
  257. package/dist/src/core/git-file-ops.js +19 -19
  258. package/dist/src/core/git-readonly-index.d.ts +53 -19
  259. package/dist/src/core/git-readonly-index.js +75 -31
  260. package/dist/src/core/git-status.d.ts +5 -6
  261. package/dist/src/core/git-status.js +5 -6
  262. package/dist/src/core/git.d.ts +9 -10
  263. package/dist/src/core/git.js +54 -70
  264. package/dist/src/core/license-headers.d.ts +17 -23
  265. package/dist/src/core/license-headers.js +17 -23
  266. package/dist/src/core/mach-build-artifacts.d.ts +9 -9
  267. package/dist/src/core/mach-build-artifacts.js +57 -19
  268. package/dist/src/core/mach-error-hints.js +42 -29
  269. package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
  270. package/dist/src/core/mach-known-noise-filter.js +18 -20
  271. package/dist/src/core/mach-mozconfig.d.ts +14 -0
  272. package/dist/src/core/mach-mozconfig.js +30 -0
  273. package/dist/src/core/mach-resource-shim.d.ts +43 -58
  274. package/dist/src/core/mach-resource-shim.js +50 -67
  275. package/dist/src/core/mach.d.ts +37 -41
  276. package/dist/src/core/mach.js +50 -51
  277. package/dist/src/core/marionette-port.d.ts +36 -1
  278. package/dist/src/core/marionette-port.js +96 -31
  279. package/dist/src/core/marionette-preflight.d.ts +8 -12
  280. package/dist/src/core/marionette-preflight.js +86 -52
  281. package/dist/src/core/mochitest-server-port.d.ts +42 -0
  282. package/dist/src/core/mochitest-server-port.js +119 -0
  283. package/dist/src/core/module-resolution-preflight.d.ts +9 -11
  284. package/dist/src/core/module-resolution-preflight.js +9 -11
  285. package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
  286. package/dist/src/core/moz-manifest-helpers.js +163 -0
  287. package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
  288. package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
  289. package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
  290. package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
  291. package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
  292. package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
  293. package/dist/src/core/ownership-table.d.ts +14 -16
  294. package/dist/src/core/ownership-table.js +14 -16
  295. package/dist/src/core/parser-fallback.d.ts +15 -1
  296. package/dist/src/core/parser-fallback.js +22 -3
  297. package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
  298. package/dist/src/core/patch-apply-fuzz.js +15 -18
  299. package/dist/src/core/patch-apply.d.ts +13 -34
  300. package/dist/src/core/patch-apply.js +16 -76
  301. package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
  302. package/dist/src/core/patch-artifact-normalize.js +13 -15
  303. package/dist/src/core/patch-export-metadata.d.ts +12 -2
  304. package/dist/src/core/patch-export-metadata.js +13 -4
  305. package/dist/src/core/patch-export-update.d.ts +13 -1
  306. package/dist/src/core/patch-export-update.js +25 -8
  307. package/dist/src/core/patch-export.d.ts +15 -8
  308. package/dist/src/core/patch-export.js +10 -11
  309. package/dist/src/core/patch-files.d.ts +5 -6
  310. package/dist/src/core/patch-files.js +5 -6
  311. package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
  312. package/dist/src/core/patch-identifier-suggest.js +14 -19
  313. package/dist/src/core/patch-lint-binary.d.ts +51 -0
  314. package/dist/src/core/patch-lint-binary.js +39 -0
  315. package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
  316. package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
  317. package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
  318. package/dist/src/core/patch-lint-checkjs.js +16 -16
  319. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
  320. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
  321. package/dist/src/core/patch-lint-creators.d.ts +8 -8
  322. package/dist/src/core/patch-lint-creators.js +8 -8
  323. package/dist/src/core/patch-lint-cross.d.ts +17 -20
  324. package/dist/src/core/patch-lint-cross.js +16 -16
  325. package/dist/src/core/patch-lint-css.d.ts +3 -5
  326. package/dist/src/core/patch-lint-css.js +17 -22
  327. package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
  328. package/dist/src/core/patch-lint-diff-tag.js +14 -16
  329. package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
  330. package/dist/src/core/patch-lint-jsdoc.js +26 -19
  331. package/dist/src/core/patch-lint-mozbuild.js +20 -20
  332. package/dist/src/core/patch-lint-observer.d.ts +7 -7
  333. package/dist/src/core/patch-lint-observer.js +7 -7
  334. package/dist/src/core/patch-lint.d.ts +38 -29
  335. package/dist/src/core/patch-lint.js +89 -121
  336. package/dist/src/core/patch-lock.d.ts +1 -1
  337. package/dist/src/core/patch-lock.js +45 -9
  338. package/dist/src/core/patch-manifest-consistency.d.ts +2 -1
  339. package/dist/src/core/patch-manifest-consistency.js +25 -27
  340. package/dist/src/core/patch-manifest-io.js +14 -18
  341. package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
  342. package/dist/src/core/patch-manifest-resolve.js +9 -12
  343. package/dist/src/core/patch-manifest-validate.d.ts +6 -1
  344. package/dist/src/core/patch-manifest-validate.js +25 -13
  345. package/dist/src/core/patch-parse.d.ts +24 -13
  346. package/dist/src/core/patch-parse.js +19 -14
  347. package/dist/src/core/patch-policy.d.ts +2 -2
  348. package/dist/src/core/patch-policy.js +2 -2
  349. package/dist/src/core/patch-registration-refs.d.ts +7 -8
  350. package/dist/src/core/patch-registration-refs.js +9 -9
  351. package/dist/src/core/process-cpu-time.d.ts +12 -0
  352. package/dist/src/core/process-cpu-time.js +52 -0
  353. package/dist/src/core/rebase-session.d.ts +38 -26
  354. package/dist/src/core/rebase-session.js +55 -28
  355. package/dist/src/core/register-browser-content.js +9 -74
  356. package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
  357. package/dist/src/core/register-module.js +10 -43
  358. package/dist/src/core/register-scaffold.d.ts +8 -8
  359. package/dist/src/core/register-scaffold.js +10 -10
  360. package/dist/src/core/register-shared-css.d.ts +9 -12
  361. package/dist/src/core/register-shared-css.js +17 -87
  362. package/dist/src/core/register-xpcshell-test.d.ts +6 -7
  363. package/dist/src/core/register-xpcshell-test.js +7 -8
  364. package/dist/src/core/shared-ftl.js +12 -4
  365. package/dist/src/core/state-file.d.ts +45 -0
  366. package/dist/src/core/state-file.js +34 -0
  367. package/dist/src/core/status-binary.d.ts +18 -11
  368. package/dist/src/core/status-binary.js +19 -12
  369. package/dist/src/core/status-classify.d.ts +30 -36
  370. package/dist/src/core/status-classify.js +21 -26
  371. package/dist/src/core/test-changed-prefs.d.ts +30 -0
  372. package/dist/src/core/test-changed-prefs.js +78 -0
  373. package/dist/src/core/test-harness-crash.d.ts +25 -63
  374. package/dist/src/core/test-harness-crash.js +121 -142
  375. package/dist/src/core/test-harness-signature.d.ts +18 -0
  376. package/dist/src/core/test-harness-signature.js +13 -0
  377. package/dist/src/core/test-path-scope.d.ts +12 -15
  378. package/dist/src/core/test-path-scope.js +15 -18
  379. package/dist/src/core/test-stale-check.d.ts +17 -17
  380. package/dist/src/core/test-stale-check.js +50 -43
  381. package/dist/src/core/test-stall-triage.d.ts +51 -0
  382. package/dist/src/core/test-stall-triage.js +126 -0
  383. package/dist/src/core/token-category.js +5 -7
  384. package/dist/src/core/token-coverage.js +9 -9
  385. package/dist/src/core/token-dark-mode.d.ts +9 -11
  386. package/dist/src/core/token-dark-mode.js +9 -11
  387. package/dist/src/core/token-docs.d.ts +2 -0
  388. package/dist/src/core/token-docs.js +2 -0
  389. package/dist/src/core/token-manager.d.ts +10 -2
  390. package/dist/src/core/token-manager.js +17 -8
  391. package/dist/src/core/token-scaffold.d.ts +7 -7
  392. package/dist/src/core/token-scaffold.js +7 -7
  393. package/dist/src/core/token-variant.d.ts +4 -4
  394. package/dist/src/core/token-variant.js +4 -4
  395. package/dist/src/core/toolchain-preflight.d.ts +20 -21
  396. package/dist/src/core/toolchain-preflight.js +28 -31
  397. package/dist/src/core/tree-guard.js +12 -2
  398. package/dist/src/core/tree-store.d.ts +41 -27
  399. package/dist/src/core/tree-store.js +63 -42
  400. package/dist/src/core/typecheck-shim.d.ts +23 -24
  401. package/dist/src/core/typecheck-shim.js +23 -24
  402. package/dist/src/core/typecheck.js +8 -8
  403. package/dist/src/core/wire-destroy.d.ts +3 -3
  404. package/dist/src/core/wire-destroy.js +7 -15
  405. package/dist/src/core/wire-dom-fragment.d.ts +9 -17
  406. package/dist/src/core/wire-dom-fragment.js +11 -44
  407. package/dist/src/core/wire-init.d.ts +2 -3
  408. package/dist/src/core/wire-init.js +8 -19
  409. package/dist/src/core/wire-subscript.d.ts +2 -3
  410. package/dist/src/core/wire-subscript.js +4 -13
  411. package/dist/src/core/wire-utils.d.ts +7 -8
  412. package/dist/src/core/wire-utils.js +7 -8
  413. package/dist/src/core/xpcshell-appdir.d.ts +38 -45
  414. package/dist/src/core/xpcshell-appdir.js +38 -45
  415. package/dist/src/errors/base.d.ts +37 -16
  416. package/dist/src/errors/base.js +37 -16
  417. package/dist/src/errors/build.d.ts +13 -1
  418. package/dist/src/errors/build.js +14 -2
  419. package/dist/src/errors/codes.d.ts +16 -0
  420. package/dist/src/errors/codes.js +16 -0
  421. package/dist/src/errors/furnace.d.ts +4 -4
  422. package/dist/src/errors/git.d.ts +6 -10
  423. package/dist/src/errors/git.js +6 -10
  424. package/dist/src/errors/patch.d.ts +7 -7
  425. package/dist/src/errors/patch.js +7 -7
  426. package/dist/src/errors/rebase.d.ts +4 -4
  427. package/dist/src/errors/rebase.js +4 -4
  428. package/dist/src/index.d.ts +7 -1
  429. package/dist/src/index.js +5 -0
  430. package/dist/src/types/commands/index.d.ts +2 -2
  431. package/dist/src/types/commands/options.d.ts +88 -70
  432. package/dist/src/types/commands/patches.d.ts +24 -34
  433. package/dist/src/types/commands/project.d.ts +5 -34
  434. package/dist/src/types/config.d.ts +25 -23
  435. package/dist/src/types/config.js +6 -1
  436. package/dist/src/types/furnace.d.ts +37 -13
  437. package/dist/src/types/furnace.js +19 -1
  438. package/dist/src/types/index.d.ts +3 -3
  439. package/dist/src/utils/build-info.d.ts +7 -8
  440. package/dist/src/utils/build-info.js +12 -11
  441. package/dist/src/utils/errors.d.ts +6 -8
  442. package/dist/src/utils/errors.js +6 -8
  443. package/dist/src/utils/logger.d.ts +27 -10
  444. package/dist/src/utils/logger.js +28 -16
  445. package/dist/src/utils/machine-output.d.ts +12 -0
  446. package/dist/src/utils/machine-output.js +33 -0
  447. package/dist/src/utils/options.d.ts +31 -14
  448. package/dist/src/utils/options.js +86 -30
  449. package/dist/src/utils/paths.d.ts +10 -8
  450. package/dist/src/utils/paths.js +10 -8
  451. package/dist/src/utils/platform.d.ts +0 -25
  452. package/dist/src/utils/platform.js +1 -42
  453. package/dist/src/utils/process-group.d.ts +9 -4
  454. package/dist/src/utils/process-group.js +19 -5
  455. package/dist/src/utils/process.d.ts +42 -7
  456. package/dist/src/utils/process.js +44 -17
  457. package/dist/src/utils/ps-duration.d.ts +31 -0
  458. package/dist/src/utils/ps-duration.js +70 -0
  459. package/dist/src/utils/regex.d.ts +9 -10
  460. package/dist/src/utils/regex.js +9 -10
  461. package/dist/src/utils/validation.d.ts +58 -28
  462. package/dist/src/utils/validation.js +60 -33
  463. package/package.json +2 -2
  464. package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
  465. package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
  466. package/dist/src/core/manifest-helpers.d.ts +0 -38
  467. package/dist/src/core/manifest-helpers.js +0 -120
  468. /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
  469. /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
  470. /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
@@ -5,14 +5,15 @@ import { auditBuildArtifacts } from '../core/build-audit.js';
5
5
  import { readBuildBaseline, writeBuildBaseline } from '../core/build-baseline.js';
6
6
  import { describeSignalShapedExit, prepareBuildEnvironment } from '../core/build-prepare.js';
7
7
  import { getProjectPaths, loadConfig } from '../core/config.js';
8
+ import { assertEngineExists } from '../core/engine-precondition.js';
8
9
  import { withEngineSessionLock } from '../core/engine-session-lock.js';
9
10
  import { attemptMozinfoRewrite, build, buildArtifactMismatchMessage, buildUI, hasBuildArtifacts, hasRunnableBundle, runMach, withBuildLock, } from '../core/mach.js';
10
11
  import { buildHarnessCrashMessage } from '../core/test-harness-crash.js';
11
12
  import { formatToolchainMismatchMessage, runToolchainPreflight, } from '../core/toolchain-preflight.js';
12
- import { GeneralError } from '../errors/base.js';
13
+ import { GeneralError, InvalidArgumentError } from '../errors/base.js';
13
14
  import { AmbiguousBuildArtifactsError, BuildError } from '../errors/build.js';
14
15
  import { toError } from '../utils/errors.js';
15
- import { checkDiskSpace, pathExists } from '../utils/fs.js';
16
+ import { checkDiskSpace } from '../utils/fs.js';
16
17
  import { error, info, intro, outro, spinner, verbose, warn } from '../utils/logger.js';
17
18
  import { addWaitLockOption, pickDefined, resolveWaitLockSeconds } from '../utils/options.js';
18
19
  import { isPositiveInteger } from '../utils/validation.js';
@@ -68,7 +69,7 @@ function resolveJobCount(options, configJobs) {
68
69
  return undefined;
69
70
  }
70
71
  if (!isPositiveInteger(jobs)) {
71
- throw new GeneralError('Build jobs must be a positive integer');
72
+ throw new InvalidArgumentError('Build jobs must be a positive integer', '--jobs');
72
73
  }
73
74
  return jobs;
74
75
  }
@@ -176,16 +177,13 @@ export async function buildCommand(projectRoot, options) {
176
177
  // Disk space pre-flight: a full Firefox build can be ~20 GB
177
178
  await checkDiskSpace(projectRoot, 20 * 1024 * 1024 * 1024, warn);
178
179
  // Check if engine exists
179
- if (!(await pathExists(paths.engine))) {
180
- throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
181
- }
180
+ await assertEngineExists(paths.engine);
182
181
  // Toolchain preflight: compare the minimums the tree itself declares
183
- // (cbindgen / rust) against the host binaries mach configure will
184
- // resolve, and fail fast naming `fireforge bootstrap` instead of dying
185
- // ~8s into configure with mach's "./mach bootstrap" remediation text
186
- // (152.0b7 → 153.0b8 source-refresh drill). Fail-soft by design: only
187
- // a definitively parsed minimum vs a definitively probed host version
188
- // can fail here; anything uncertain proceeds to mach, where the
182
+ // (cbindgen / rust) against the host binaries mach configure will resolve,
183
+ // and fail fast naming `fireforge bootstrap` instead of dying ~8s into
184
+ // configure with mach's "./mach bootstrap" remediation text. Fail-soft by
185
+ // design: only a definitively parsed minimum vs a definitively probed host
186
+ // version can fail here; anything uncertain proceeds to mach, where the
189
187
  // mach-error-hints translator still names the right remedy.
190
188
  const toolchainMismatches = await runToolchainPreflight(paths.engine);
191
189
  if (toolchainMismatches.length > 0) {
@@ -257,13 +255,12 @@ export async function buildCommand(projectRoot, options) {
257
255
  try {
258
256
  // Hold the per-project build lock across the mach invocation so two
259
257
  // overlapping `fireforge build` / `fireforge build --ui` commands
260
- // against the same engine tree serialise instead of racing through
261
- // the same obj-*. 2026-04-21 eval: a `build --ui` launched during
262
- // an in-progress full build hit `No rule to make target 'XUL'` in
263
- // mach, which is the downstream consequence of an incomplete
264
- // backend — not a clue that a concurrent build was the cause. The
265
- // lock turns the second invocation's failure into an explicit
266
- // refusal naming the holder PID.
258
+ // against the same engine tree serialise instead of racing through the
259
+ // same obj-*. A `build --ui` launched during an in-progress full build
260
+ // otherwise hits `No rule to make target 'XUL'` in mach — the
261
+ // downstream consequence of an incomplete backend, with no clue that a
262
+ // concurrent build caused it. The lock turns the second invocation's
263
+ // failure into an explicit refusal naming the holder PID.
267
264
  result = await withBuildLock(projectRoot, async () => {
268
265
  if (options.ui) {
269
266
  return buildUI(paths.engine);
@@ -290,24 +287,20 @@ export async function buildCommand(projectRoot, options) {
290
287
  : '';
291
288
  throw new BuildError(crashPreamble + buildFailureDiagnostics(result, paths.engine, buildCheck.objDir, machCommand), machCommand);
292
289
  }
293
- // Tool-managed branding edits that land on `browser/moz.configure`
294
- // before the build cause mach's post-build guard to print one of two
295
- // banners that read like build failures even though the build
296
- // completed cleanly:
290
+ // Tool-managed branding edits that land on `browser/moz.configure` before
291
+ // the build cause mach's post-build guard to print one of two banners that
292
+ // read like build failures even though the build completed cleanly:
297
293
  //
298
294
  // 1) "config.status is out of date with respect to ..."
299
295
  // 2) "Config object not found by mach. / Configure complete! /
300
296
  // Be sure to run |mach build| to pick up any changes."
301
297
  //
302
- // 2026-04-21 eval covered (1); 2026-04-26 eval Finding 8 reproduced
303
- // (2) on a successful build. The pre-fix pattern only matched (1),
304
- // so operators on the (2) path saw mach's own "Configure complete!"
305
- // and "run |mach build|" lines unexplained between mach's
306
- // "Your build was successful!" and FireForge's own "Build completed
307
- // in Xm Ys" outro — a contradictory tail. Both shapes now route
308
- // through the same annotation, emitted BEFORE FireForge's outro so
309
- // the operator's last terminal line is the explanation, not the
310
- // confusing mach guard text.
298
+ // Matching only (1) leaves operators on the (2) path seeing mach's own
299
+ // "Configure complete!" and "run |mach build|" lines unexplained between
300
+ // mach's "Your build was successful!" and FireForge's "Build completed in
301
+ // Xm Ys" outro — a contradictory tail. Both shapes route through the same
302
+ // annotation, emitted BEFORE FireForge's outro so the operator's last
303
+ // terminal line is the explanation, not the confusing mach guard text.
311
304
  const staleConfigurePatterns = [
312
305
  /config\.status is out of date/i,
313
306
  /Config object not found by mach\.[\s\S]*Configure complete!/i,
@@ -91,10 +91,10 @@ export async function configCommand(projectRoot, key, value, options = {}) {
91
91
  if (value === undefined) {
92
92
  // Get mode — read the raw document rather than the validated config so
93
93
  // keys persisted via `fireforge config <key> --force` remain readable.
94
- // `validateConfig` builds a typed clone containing only the known
95
- // schema fields; relying on it here would silently hide forced-write
96
- // keys and surface "Unknown config key" on the read even though the
97
- // key is sitting plainly inside fireforge.json.
94
+ // `validateConfig` builds a typed clone containing only the known schema
95
+ // fields, so relying on it here would hide forced-write keys and surface
96
+ // "Unknown config key" even though the key sits plainly inside
97
+ // fireforge.json.
98
98
  const rawConfig = await loadRawConfigDocument(projectRoot);
99
99
  const currentValue = getNestedValue(rawConfig, key);
100
100
  if (currentValue === undefined) {
@@ -124,43 +124,36 @@ export async function configCommand(projectRoot, key, value, options = {}) {
124
124
  let unchanged;
125
125
  try {
126
126
  // Serialise the read → mutate → write round-trip behind the sidecar
127
- // config lock so two concurrent `fireforge config` invocations can't
127
+ // config lock so two concurrent `fireforge config` invocations cannot
128
128
  // each read the pre-state, mutate their own copy, and clobber each
129
- // other on write. Before the lock, the 2026-04-21 eval reproduced
130
- // silent data loss with two parallel `fireforge config <key>
131
- // <value>` commands writing different keys: both exited 0, one key
132
- // survived, the other vanished. Atomic file writes (temp + rename)
133
- // were never enough on their own — the lost update happens before
134
- // the rename, inside the read-modify step. Readers stay lock-free
135
- // (see `withConfigFileLock` docstring).
129
+ // other on write — both exiting 0 with one key silently lost. Atomic
130
+ // file writes (temp + rename) are not enough on their own: the lost
131
+ // update happens before the rename, inside the read-modify step.
132
+ // Readers stay lock-free (see `withConfigFileLock`).
136
133
  unchanged = await withConfigFileLock(projectRoot, async () => {
137
- // 2026-04-26 eval Finding 11: short-circuit when the new value
138
- // matches the current on-disk value. Pre-fix, every set ran
139
- // through `mutateConfig` + `writeConfig`, which round-trips
140
- // through `JSON.stringify` and rewrites the file even when no
141
- // semantic change happened — the rewrite reorders top-level
142
- // keys (`license`, `markerComment`, etc.) on every harmless
143
- // re-set, producing diff churn for no reason. The check uses
144
- // the raw on-disk document so forced-keys round-trip the same
145
- // as known keys.
134
+ // Short-circuit when the new value matches the current on-disk
135
+ // value. Running every set through `mutateConfig` + `writeConfig`
136
+ // round-trips through `JSON.stringify` and rewrites the file even
137
+ // when nothing changed semantically, reordering top-level keys
138
+ // (`license`, `markerComment`, …) on every harmless re-set. The
139
+ // check uses the raw on-disk document so forced keys round-trip the
140
+ // same as known keys.
146
141
  const rawConfig = await loadRawConfigDocument(projectRoot);
147
142
  const currentValue = getNestedValue(rawConfig, key);
148
143
  if (deepEqual(currentValue, parsedValue)) {
149
144
  return true;
150
145
  }
151
- // `--force` is intended as an escape hatch for *unknown* keys; it
152
- // should not also let the user write a structurally invalid value
153
- // for a *known* key. Apply strict validation whenever the key is
154
- // listed in SUPPORTED_CONFIG_PATHS, regardless of --force, and only
155
- // skip validation for genuinely unknown key paths.
146
+ // `--force` is an escape hatch for *unknown* keys; it must not also
147
+ // let the user write a structurally invalid value for a *known* key.
148
+ // Strict validation applies whenever the key is listed in
149
+ // SUPPORTED_CONFIG_PATHS, regardless of --force.
156
150
  //
157
151
  // BOTH branches seed the mutation from the raw on-disk document.
158
- // The known-key branch used to round-trip through `loadConfig` →
159
- // `validateConfig`, which builds a typed clone containing only the
160
- // known schema fields — so any ordinary `fireforge config <key>
161
- // <value>` silently dropped every previously --force-written key
162
- // from fireforge.json (2026-07-05 review, finding H4; the --force
163
- // branch's comment described this exact hazard for its own path).
152
+ // Round-tripping the known-key branch through `loadConfig` →
153
+ // `validateConfig` builds a typed clone containing only the known
154
+ // schema fields, so any ordinary `fireforge config <key> <value>`
155
+ // would silently drop every previously --force-written key from
156
+ // fireforge.json.
164
157
  if (options.force && !keyIsKnown) {
165
158
  const updatedConfig = mutateConfig(rawConfig, key, parsedValue, true);
166
159
  await writeConfigDocument(projectRoot, updatedConfig);
@@ -181,10 +174,10 @@ export async function configCommand(projectRoot, key, value, options = {}) {
181
174
  catch (error) {
182
175
  // Only value/validation problems are the user's "invalid value".
183
176
  // Lock-acquisition timeouts and I/O failures must keep their own
184
- // types/messages — re-labelling a lock timeout as `Invalid value for
185
- // "<key>"` used to point diagnosis at the value (and return the
186
- // wrong exit-code class) when the actual problem was a concurrent
187
- // fireforge process holding the config lock.
177
+ // types and messages — re-labelling a lock timeout as `Invalid value
178
+ // for "<key>"` points diagnosis at the value (and returns the wrong
179
+ // exit-code class) when the real problem is a concurrent fireforge
180
+ // process holding the config lock.
188
181
  if (error instanceof ConfigError) {
189
182
  throw new InvalidArgumentError(`Invalid value for "${key}": ${toError(error).message}`, key);
190
183
  }
@@ -200,11 +193,11 @@ export async function configCommand(projectRoot, key, value, options = {}) {
200
193
  outro('');
201
194
  }
202
195
  /**
203
- * Structural equality check covering the shapes that
204
- * `fireforge config` accepts: primitives (strings, numbers, booleans),
205
- * `null`, arrays of primitives, and nested objects. Used to short-circuit
206
- * no-op writes (Finding 11) — when the parsed value matches the current
207
- * on-disk value, skip the mutate + write step entirely.
196
+ * Structural equality check covering the shapes `fireforge config` accepts:
197
+ * primitives (strings, numbers, booleans), `null`, arrays of primitives, and
198
+ * nested objects. Used to short-circuit no-op writes — when the parsed value
199
+ * matches the current on-disk value, the mutate + write step is skipped
200
+ * entirely.
208
201
  */
209
202
  function deepEqual(a, b) {
210
203
  if (a === b)
@@ -2,6 +2,7 @@
2
2
  import { confirm } from '@clack/prompts';
3
3
  import { Command } from 'commander';
4
4
  import { getProjectPaths } from '../core/config.js';
5
+ import { stdioIsInteractive } from '../core/destructive.js';
5
6
  import { applyDiscardBaseline, describeConflictWarning, describeDiscardBaseline, describeDiscardOutcome, planDiscardBaselines, planUpstreamDiscards, summarizeDiscardBaselines, } from '../core/discard-baseline.js';
6
7
  import { assertEngineGitReady } from '../core/engine-precondition.js';
7
8
  import { collectFurnaceManagedPrefixes } from '../core/furnace-config.js';
@@ -18,7 +19,7 @@ import { pickDefined } from '../utils/options.js';
18
19
  async function confirmDiscard(message, hint, options) {
19
20
  if (options.yes || options.dryRun)
20
21
  return true;
21
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
22
+ const isInteractive = stdioIsInteractive();
22
23
  if (!isInteractive) {
23
24
  throw new InvalidArgumentError('Interactive confirmation not available. Use --yes flag to discard without confirmation.', hint);
24
25
  }
@@ -130,14 +131,12 @@ export async function discardCommand(projectRoot, file, options = {}) {
130
131
  const statusEntries = await expandUntrackedDirectoryEntries(paths.engine, await getWorkingTreeStatus(paths.engine));
131
132
  const statusEntry = statusEntries.find((entry) => entry.file === file || entry.originalPath === file);
132
133
  // Directory recursion fallback: when the explicit path does not match a
133
- // single status entry but DOES correspond to one or more entries below
134
- // it, treat the input as a directory and discard everything inside.
135
- // 2026-04-25 eval Finding 20: `discard browser/components/storybook/
136
- // stories/furnace --yes` failed with "no changes to discard" even
137
- // though `status --unmanaged` listed 23 files under that directory —
138
- // operators were forced to discard each file individually or fall
139
- // back to non-FireForge cleanup commands. Match against the
140
- // directory-with-trailing-slash form so a path like `foo/bar` doesn't
134
+ // single status entry but DOES correspond to one or more entries below it,
135
+ // treat the input as a directory and discard everything inside. Otherwise
136
+ // `discard <dir> --yes` fails with "no changes to discard" even though
137
+ // `status --unmanaged` lists files under that directory, forcing the
138
+ // operator to discard each file individually. Match against the
139
+ // directory-with-trailing-slash form so a path like `foo/bar` does not
141
140
  // accidentally match `foo/bar2/file`.
142
141
  if (!statusEntry) {
143
142
  // Normalize the operator's input once: `discard foo/` (or `foo//`) must
@@ -88,29 +88,15 @@ export interface DoctorCheckDefinition {
88
88
  fix?: string;
89
89
  }
90
90
  /**
91
- * Resolves a {@link DoctorCheck} to its effective severity, implementing the
92
- * field-precedence contract documented on the interface
93
- * (`src/types/commands/project.ts`):
94
- *
95
- * 1. `severity`, when present, is authoritative — regardless of `passed`.
96
- * 2. Without `severity`, `passed: false` is an error unless `warning: true`
97
- * downgrades it to a warning.
98
- *
99
- * This is the single implementation on purpose. Before 0.41.0 there were two
100
- * and they disagreed on exactly one cell: `reportDoctorResults` consulted
101
- * `warning` only inside the `passed === true` branch, so the documented
102
- * downgrade produced an error and a non-zero doctor exit, while
103
- * `bootstrap.ts` — reading the same array one line earlier — classified it as
104
- * a warning. No in-tree producer emitted that combination (`failure()` always
105
- * sets `severity: 'error'`), so the contradiction was latent rather than
106
- * observable; the interface is exported, so the next hand-rolled check would
107
- * have hit it.
91
+ * Resolves a {@link DoctorCheck} to its effective severity. `severity` is the
92
+ * single source of truth; this exists so every consumer of a
93
+ * `DoctorCheck[]` reads it the same way.
108
94
  */
109
95
  export declare function resolveDoctorSeverity(check: DoctorCheck): 'ok' | 'warning' | 'error';
110
96
  /**
111
97
  * Builds a DoctorCheck object representing a successful "OK" check.
112
98
  */
113
- export declare function ok(name: string): DoctorCheck;
99
+ export declare function ok(name: string, message?: string): DoctorCheck;
114
100
  /**
115
101
  * Builds a DoctorCheck object representing a warning result.
116
102
  */
@@ -5,55 +5,28 @@
5
5
  * these symbols without creating cycles through `doctor.ts`.
6
6
  */
7
7
  /**
8
- * Resolves a {@link DoctorCheck} to its effective severity, implementing the
9
- * field-precedence contract documented on the interface
10
- * (`src/types/commands/project.ts`):
11
- *
12
- * 1. `severity`, when present, is authoritative — regardless of `passed`.
13
- * 2. Without `severity`, `passed: false` is an error unless `warning: true`
14
- * downgrades it to a warning.
15
- *
16
- * This is the single implementation on purpose. Before 0.41.0 there were two
17
- * and they disagreed on exactly one cell: `reportDoctorResults` consulted
18
- * `warning` only inside the `passed === true` branch, so the documented
19
- * downgrade produced an error and a non-zero doctor exit, while
20
- * `bootstrap.ts` — reading the same array one line earlier — classified it as
21
- * a warning. No in-tree producer emitted that combination (`failure()` always
22
- * sets `severity: 'error'`), so the contradiction was latent rather than
23
- * observable; the interface is exported, so the next hand-rolled check would
24
- * have hit it.
8
+ * Resolves a {@link DoctorCheck} to its effective severity. `severity` is the
9
+ * single source of truth; this exists so every consumer of a
10
+ * `DoctorCheck[]` reads it the same way.
25
11
  */
26
12
  export function resolveDoctorSeverity(check) {
27
- if (check.severity !== undefined) {
28
- return check.severity;
29
- }
30
- if (check.warning) {
31
- return 'warning';
32
- }
33
- return check.passed ? 'ok' : 'error';
13
+ return check.severity;
34
14
  }
35
15
  /**
36
16
  * Builds a DoctorCheck object representing a successful "OK" check.
37
17
  */
38
- export function ok(name) {
39
- return { name, passed: true, severity: 'ok', message: 'OK' };
18
+ export function ok(name, message = 'OK') {
19
+ return { name, severity: 'ok', message };
40
20
  }
41
21
  /**
42
22
  * Builds a DoctorCheck object representing a warning result.
43
23
  */
44
24
  export function warning(name, message, fix) {
45
- return {
46
- name,
47
- passed: true,
48
- severity: 'warning',
49
- warning: true,
50
- message,
51
- ...(fix ? { fix } : {}),
52
- };
25
+ return { name, severity: 'warning', message, ...(fix ? { fix } : {}) };
53
26
  }
54
27
  /**
55
28
  * Builds a DoctorCheck object representing a failure result.
56
29
  */
57
30
  export function failure(name, message, fix) {
58
- return { name, passed: false, severity: 'error', message, ...(fix ? { fix } : {}) };
31
+ return { name, severity: 'error', message, ...(fix ? { fix } : {}) };
59
32
  }
@@ -2,7 +2,7 @@
2
2
  import { isAbsolute, resolve } from 'node:path';
3
3
  import { pathExists } from '../utils/fs.js';
4
4
  import { exec, findExecutable } from '../utils/process.js';
5
- import { failure, warning } from './doctor-check-core.js';
5
+ import { failure, ok, warning } from './doctor-check-core.js';
6
6
  async function resolveDeclaredTool(projectRoot, tool) {
7
7
  if (tool.path !== undefined) {
8
8
  const candidate = isAbsolute(tool.path) ? tool.path : resolve(projectRoot, tool.path);
@@ -32,7 +32,7 @@ async function runExternalToolchainChecks(ctx) {
32
32
  const label = `External toolchain: ${toolchain.name}/${tool.name}`;
33
33
  const found = await resolveDeclaredTool(ctx.projectRoot, tool);
34
34
  if (found !== undefined) {
35
- rows.push({ name: label, passed: true, severity: 'ok', message: `OK (${found})` });
35
+ rows.push(ok(label, `OK (${found})`));
36
36
  continue;
37
37
  }
38
38
  const required = tool.required !== false;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * "Furnace config sync" doctor check.
3
+ *
4
+ * Surfaces orphaned component directories on disk whose names are missing
5
+ * from `furnace.json` — typically the concurrent-override race, where two
6
+ * parallel `furnace override` commands both leave their directory on disk
7
+ * but only the second reaches `writeFurnaceConfig`. Under
8
+ * `--repair-furnace`, override orphans are re-registered from the
9
+ * `override.json` sidecar the command wrote during the copy phase; custom
10
+ * orphans are listed for the operator to either re-run `furnace create`
11
+ * against or delete manually, because custom components have no similar
12
+ * persisted metadata.
13
+ *
14
+ * Lives in a sibling module to keep `doctor-furnace.ts` under the per-file
15
+ * LOC budget.
16
+ */
17
+ import type { DoctorCheckDefinition } from './doctor-check-core.js';
18
+ export declare const furnaceConfigSyncCheck: DoctorCheckDefinition;
@@ -1,19 +1,19 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
- * "Furnace manifest sync" doctor check.
3
+ * "Furnace config sync" doctor check.
4
4
  *
5
5
  * Surfaces orphaned component directories on disk whose names are missing
6
- * from `furnace.json`. The motivating eval case is the concurrent-override
7
- * race (eval 2) where two parallel `furnace override` commands both left
8
- * their directory on disk but only the second reached
9
- * `writeFurnaceConfig`. Under `--repair-furnace`, override orphans are
10
- * re-registered from the `override.json` sidecar the command wrote during
11
- * the copy phase; custom orphans are listed for the operator to either
12
- * re-run `furnace create` against or delete manually, because custom
13
- * components have no similar persisted metadata.
6
+ * from `furnace.json` — typically the concurrent-override race, where two
7
+ * parallel `furnace override` commands both leave their directory on disk
8
+ * but only the second reaches `writeFurnaceConfig`. Under
9
+ * `--repair-furnace`, override orphans are re-registered from the
10
+ * `override.json` sidecar the command wrote during the copy phase; custom
11
+ * orphans are listed for the operator to either re-run `furnace create`
12
+ * against or delete manually, because custom components have no similar
13
+ * persisted metadata.
14
14
  *
15
- * Lives in a sibling module to keep `doctor-furnace.ts` under the
16
- * per-file LOC budget.
15
+ * Lives in a sibling module to keep `doctor-furnace.ts` under the per-file
16
+ * LOC budget.
17
17
  */
18
18
  import { readdir, rmdir } from 'node:fs/promises';
19
19
  import { join } from 'node:path';
@@ -43,12 +43,13 @@ async function listComponentDirs(dir) {
43
43
  }
44
44
  async function recoverOverrideConfig(overrideDir, name) {
45
45
  // `furnace override` writes `override.json` alongside the copied files.
46
- // That sidecar is enough to reconstruct the furnace.json entry lost to
47
- // a concurrent-write race (eval 2: second override wrote back the
48
- // outer-snapshot config, dropping the sibling write's addition).
49
- // No `pathExists` precheck: it collapses "absent" and "unreadable" into the
50
- // same `false`, and both outcomes lead here anyway — a missing file lands in
51
- // the catch below with the same `undefined` result.
46
+ // That sidecar is enough to reconstruct the furnace.json entry lost to a
47
+ // concurrent-write race, where the second override writes back the
48
+ // outer-snapshot config and drops the sibling write's addition.
49
+ //
50
+ // No `pathExists` precheck: it collapses "absent" and "unreadable" into
51
+ // the same `false`, and both outcomes lead here anyway — a missing file
52
+ // lands in the catch below with the same `undefined` result.
52
53
  const sidecarPath = join(overrideDir, name, 'override.json');
53
54
  try {
54
55
  const raw = await readJson(sidecarPath);
@@ -148,12 +149,12 @@ async function repairOrphans(projectRoot, orphans) {
148
149
  return outcome;
149
150
  const lockPath = getFurnaceLockPath(projectRoot);
150
151
  // One catch covers four distinct phases; naming the one that failed keeps
151
- // the report honest — a lock timeout used to render as "failed while
152
+ // the report honest — a lock timeout otherwise renders as "failed while
152
153
  // writing furnace.json", the phase least likely to have been reached.
153
154
  // Held in an object rather than two `let`s: the lock callback mutates them
154
155
  // from inside a closure, and TS's control-flow analysis does not propagate
155
- // that through the call — it would narrow both to their initial literals in
156
- // the catch below.
156
+ // that through the call — it would narrow both to their initial literals
157
+ // in the catch below.
157
158
  const progress = { phase: 'waiting for the furnace lock', persisted: false };
158
159
  try {
159
160
  await withFileLock(lockPath, async () => {
@@ -246,8 +247,8 @@ async function deleteEmptyCustomOrphans(projectRoot, freshConfig, customNames, o
246
247
  }
247
248
  }
248
249
  }
249
- export const furnaceManifestSyncCheck = {
250
- name: 'Furnace manifest sync',
250
+ export const furnaceConfigSyncCheck = {
251
+ name: 'Furnace config sync',
251
252
  dependsOn: ['Furnace configuration'],
252
253
  skipIf: (ctx) => !ctx.furnaceConfigExists || !ctx.furnaceConfig,
253
254
  run: async (ctx) => {
@@ -258,15 +259,15 @@ export const furnaceManifestSyncCheck = {
258
259
  const overrideCount = orphans.overrides.length;
259
260
  const customCount = orphans.customNames.length;
260
261
  if (overrideCount === 0 && customCount === 0) {
261
- return ok('Furnace manifest sync');
262
+ return ok('Furnace config sync');
262
263
  }
263
264
  const summary = formatOrphanSummary(orphans);
264
265
  if (!ctx.options.repairFurnace) {
265
- return warning('Furnace manifest sync', summary, 'Run "fireforge doctor --repair-furnace" to re-register override orphans from their override.json sidecars (custom orphans are listed for manual follow-up).');
266
+ return warning('Furnace config sync', summary, 'Run "fireforge doctor --repair-furnace" to re-register override orphans from their override.json sidecars (custom orphans are listed for manual follow-up).');
266
267
  }
267
268
  const repairResult = await repairOrphans(ctx.projectRoot, orphans);
268
269
  if (repairResult.repairError) {
269
- return failure('Furnace manifest sync', `Repair failed ${repairResult.repairError}`, 'Fix the underlying error and retry the doctor command.');
270
+ return failure('Furnace config sync', `Repair failed ${repairResult.repairError}`, 'Fix the underlying error and retry the doctor command.');
270
271
  }
271
272
  const customRepair = repairResult;
272
273
  const { restored, unrecoverable, reconciled } = repairResult;
@@ -288,7 +289,7 @@ export const furnaceManifestSyncCheck = {
288
289
  const customErrorDetail = customRepair.errors.length > 0
289
290
  ? ` Could not inspect or delete ${customRepair.errors.length} custom orphan ${customRepair.errors.length === 1 ? 'directory' : 'directories'} (${customRepair.errors.join('; ')}).`
290
291
  : '';
291
- return warning('Furnace manifest sync', `${restoreDetail}${unrecoverableDetail}${customDetail}${retainedCustomDetail}${customErrorDetail}${reconciledDetail}`.trim() ||
292
+ return warning('Furnace config sync', `${restoreDetail}${unrecoverableDetail}${customDetail}${retainedCustomDetail}${customErrorDetail}${reconciledDetail}`.trim() ||
292
293
  'Nothing to repair (orphans surfaced but all were already recoverable).');
293
294
  },
294
295
  };
@@ -1,16 +1,14 @@
1
1
  /**
2
- * `doctor` check for stale furnace jar.mn registrations (0.34.0 field
3
- * report): a component-file rename left the old toolkit jar.mn line
4
- * pointing at the deleted file, every build failed at packaging, and
5
- * `doctor --repair-furnace` reported success without pruning. Split out
6
- * of `doctor-furnace.ts` to keep that file within the line budget.
2
+ * `doctor` check for stale furnace jar.mn registrations: a component-file
3
+ * rename can leave the old toolkit jar.mn line pointing at the deleted file,
4
+ * failing every build at packaging. Split out of `doctor-furnace.ts` to keep
5
+ * that file within the line budget.
7
6
  */
8
7
  import type { DoctorCheckDefinition } from './doctor-check-core.js';
9
8
  /**
10
9
  * "Furnace jar.mn registrations" check: detect widget registration lines
11
- * pointing at component files that no longer exist in the workspace
12
- * (typically left by a rename under an older FireForge). These break
13
- * `mach build` at packaging; `--repair-furnace` prunes them (0.34.0 field
14
- * report: doctor --repair-furnace reported success without pruning).
10
+ * pointing at component files that no longer exist in the workspace,
11
+ * typically left by a rename. These break `mach build` at packaging;
12
+ * `--repair-furnace` prunes them.
15
13
  */
16
14
  export declare const furnaceStaleJarRegistrationCheck: DoctorCheckDefinition;
@@ -1,10 +1,9 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
- * `doctor` check for stale furnace jar.mn registrations (0.34.0 field
4
- * report): a component-file rename left the old toolkit jar.mn line
5
- * pointing at the deleted file, every build failed at packaging, and
6
- * `doctor --repair-furnace` reported success without pruning. Split out
7
- * of `doctor-furnace.ts` to keep that file within the line budget.
3
+ * `doctor` check for stale furnace jar.mn registrations: a component-file
4
+ * rename can leave the old toolkit jar.mn line pointing at the deleted file,
5
+ * failing every build at packaging. Split out of `doctor-furnace.ts` to keep
6
+ * that file within the line budget.
8
7
  */
9
8
  import { getFurnacePaths } from '../core/furnace-config.js';
10
9
  import { findStaleJarMnEntries, pruneStaleJarMnEntries } from '../core/furnace-registration.js';
@@ -12,10 +11,9 @@ import { toError } from '../utils/errors.js';
12
11
  import { failure, ok, warning } from './doctor-check-core.js';
13
12
  /**
14
13
  * "Furnace jar.mn registrations" check: detect widget registration lines
15
- * pointing at component files that no longer exist in the workspace
16
- * (typically left by a rename under an older FireForge). These break
17
- * `mach build` at packaging; `--repair-furnace` prunes them (0.34.0 field
18
- * report: doctor --repair-furnace reported success without pruning).
14
+ * pointing at component files that no longer exist in the workspace,
15
+ * typically left by a rename. These break `mach build` at packaging;
16
+ * `--repair-furnace` prunes them.
19
17
  */
20
18
  export const furnaceStaleJarRegistrationCheck = {
21
19
  name: 'Furnace jar.mn registrations',