@hominis/fireforge 0.43.0 → 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 +127 -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 +62 -19
  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
@@ -3,12 +3,13 @@ import { readdir, unlink } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { confirm } from '@clack/prompts';
5
5
  import { getProjectPaths, loadConfig } from '../../core/config.js';
6
+ import { stdioIsInteractive } from '../../core/destructive.js';
6
7
  import { getFurnacePaths, loadFurnaceConfig, loadFurnaceState, updateFurnaceState, writeFurnaceConfig, } from '../../core/furnace-config.js';
7
8
  import { resolveFtlDir, xpcshellTestParentDir } from '../../core/furnace-constants.js';
8
- import { recordFurnaceRollbackFailure, runFurnaceMutation } from '../../core/furnace-operation.js';
9
- import { createRollbackJournal, restoreRollbackJournalOrThrow, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
9
+ import { completeJournalRollback, runFurnaceMutation } from '../../core/furnace-operation.js';
10
+ import { createRollbackJournal, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
10
11
  import { isGitRepository } from '../../core/git.js';
11
- import { deregisterTestManifest } from '../../core/manifest-register.js';
12
+ import { deregisterTestManifest } from '../../core/moz-manifest-register.js';
12
13
  import { FurnaceError } from '../../errors/furnace.js';
13
14
  import { toError } from '../../utils/errors.js';
14
15
  import { pathExists, readText, removeDir, writeText } from '../../utils/fs.js';
@@ -157,23 +158,23 @@ async function cleanupCustomTestFiles(name, projectRoot, journal) {
157
158
  return { partialFailures };
158
159
  }
159
160
  /**
160
- * Removes the MochiKit test scaffold a `furnace create --with-tests
161
- * --test-style mochikit` produced for the component (matches the rename
162
- * counterpart in `rename.ts`). The test file is `test_<name>.html` under
163
- * `engine/toolkit/content/tests/widgets/` and the registration is the
164
- * `["test_<name>.html"]` entry in the same directory's `chrome.toml`.
161
+ * Removes the MochiKit test scaffold a
162
+ * `furnace create --with-tests --test-style mochikit` produced for the
163
+ * component (matches the rename counterpart in `rename.ts`). The test file is
164
+ * `test_<name>.html` under `engine/toolkit/content/tests/widgets/` and the
165
+ * registration is the `["test_<name>.html"]` entry in the same directory's
166
+ * `chrome.toml`.
165
167
  *
166
- * 2026-04-25 eval Finding 13: the prior cleanup only handled the
167
- * browser-chrome mochitest layout under `browser/base/content/test/
168
- * <binary>/`, which left mochikit-style scaffolds and their toml entries
169
- * orphaned after `furnace remove`. The post-rename name passed in here
170
- * is the canonical one written to disk by deploy/rename, so the file
171
- * basenames match without needing to re-derive from the old name.
168
+ * Cleanup that only handles the browser-chrome mochitest layout under
169
+ * `browser/base/content/test/<binary>/` leaves mochikit-style scaffolds and
170
+ * their toml entries orphaned after `furnace remove`. The post-rename name
171
+ * passed in here is the canonical one written to disk by deploy/rename, so
172
+ * the file basenames match without re-deriving from the old name.
172
173
  *
173
- * Best-effort: each step warns on failure rather than throwing so the
174
- * rest of the remove transaction proceeds. The journal still snapshots
175
- * touched files so the outer rollback can restore them on a later
176
- * failure in the same operation.
174
+ * Best-effort: each step warns on failure rather than throwing so the rest
175
+ * of the remove transaction proceeds. The journal still snapshots touched
176
+ * files so the outer rollback can restore them on a later failure in the
177
+ * same operation.
177
178
  */
178
179
  async function cleanupCustomMochikitTestFiles(name, projectRoot, journal) {
179
180
  const partialFailures = [];
@@ -216,14 +217,13 @@ async function cleanupCustomMochikitTestFiles(name, projectRoot, journal) {
216
217
  }
217
218
  /**
218
219
  * Removes generated xpcshell test scaffolds associated with a custom
219
- * component. 2026-04-24 eval Finding 5: `furnace remove` handled
220
- * browser mochitests via `cleanupCustomTestFiles` but never touched the
221
- * xpcshell scaffold tree, so an operator who ran
222
- * `furnace create --with-tests --xpcshell` followed by `furnace remove`
223
- * was left with orphan `xpcshell.toml` + `test_<name>_packaged.js`
224
- * files still referencing the removed component. This cleanup pass
225
- * mirrors the mochitest one — snapshot before removal, warn-and-
226
- * continue semantics, explicit summary when partial failures occur.
220
+ * component. Cleanup that handles browser mochitests via
221
+ * `cleanupCustomTestFiles` but never touches the xpcshell scaffold tree
222
+ * leaves an operator who ran `furnace create --with-tests --xpcshell`
223
+ * followed by `furnace remove` with orphan `xpcshell.toml` +
224
+ * `test_<name>_packaged.js` files still referencing the removed component.
225
+ * This pass mirrors the mochitest one — snapshot before removal,
226
+ * warn-and-continue semantics, explicit summary on partial failures.
227
227
  */
228
228
  async function cleanupCustomXpcshellTestFiles(name, projectRoot, journal) {
229
229
  const partialFailures = [];
@@ -328,7 +328,7 @@ async function requireGitEngineForRemove(type, name, engineDir) {
328
328
  * @param options - CLI options
329
329
  */
330
330
  export async function furnaceRemoveCommand(projectRoot, name, options = {}) {
331
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
331
+ const isInteractive = stdioIsInteractive();
332
332
  intro('Furnace Remove');
333
333
  const config = await loadFurnaceConfig(projectRoot);
334
334
  const furnacePaths = getFurnacePaths(projectRoot);
@@ -405,17 +405,12 @@ export async function furnaceRemoveCommand(projectRoot, name, options = {}) {
405
405
  }
406
406
  }
407
407
  catch (error) {
408
- // This body owns its rollback end to end, so tell the lifecycle wrapper
409
- // not to restore the same journal again on the way out.
410
- ctx.markRolledBack();
411
- try {
412
- await restoreRollbackJournalOrThrow(journal, `Failed to remove component "${name}"`);
413
- }
414
- catch (rollbackError) {
415
- await recordFurnaceRollbackFailure(projectRoot, 'remove-rollback', `component "${name}": ${toError(rollbackError).message}`);
416
- throw rollbackError;
417
- }
418
- throw error;
408
+ return await completeJournalRollback(ctx, journal, error, {
409
+ projectRoot: projectRoot,
410
+ operation: 'remove-rollback',
411
+ failureMessage: `Failed to remove component "${name}"`,
412
+ subject: `component "${name}"`,
413
+ });
419
414
  }
420
415
  });
421
416
  info(`Removed "${name}" from furnace.json`);
@@ -1,18 +1,15 @@
1
1
  /**
2
2
  * xpcshell scaffold rename helper extracted from `rename.ts`.
3
3
  *
4
- * 2026-04-24 eval Finding 5: `furnace create --with-tests --xpcshell`
5
- * writes a scaffold at `browser/base/content/test/<binary>-xpcshell/
6
- * <name>/` and `furnace rename` did not update it. The helper below
7
- * renames the directory, updates the test filename, rewrites the
8
- * `xpcshell.toml` section header, and re-writes the test body so word-
9
- * boundary occurrences of the old tag / underscored name map to the new
10
- * ones.
4
+ * `furnace create --with-tests --xpcshell` writes a scaffold at
5
+ * `browser/base/content/test/<binary>-xpcshell/<name>/`, which a rename must
6
+ * follow. This helper renames the directory, updates the test filename,
7
+ * rewrites the `xpcshell.toml` section header, and re-writes the test body
8
+ * so word-boundary occurrences of the old tag / underscored name map to the
9
+ * new ones.
11
10
  *
12
- * Extracted to keep `rename.ts` under the per-file LOC budget —
13
- * `rename.ts` already carries mochikit + browser-mochitest + FTL
14
- * handling, and tacking xpcshell onto that tree pushed the file past
15
- * the limit.
11
+ * Extracted to keep `rename.ts` under the per-file LOC budget — it already
12
+ * carries mochikit + browser-mochitest + FTL handling.
16
13
  */
17
14
  import { type RollbackJournal } from '../../core/furnace-rollback.js';
18
15
  /**
@@ -2,18 +2,15 @@
2
2
  /**
3
3
  * xpcshell scaffold rename helper extracted from `rename.ts`.
4
4
  *
5
- * 2026-04-24 eval Finding 5: `furnace create --with-tests --xpcshell`
6
- * writes a scaffold at `browser/base/content/test/<binary>-xpcshell/
7
- * <name>/` and `furnace rename` did not update it. The helper below
8
- * renames the directory, updates the test filename, rewrites the
9
- * `xpcshell.toml` section header, and re-writes the test body so word-
10
- * boundary occurrences of the old tag / underscored name map to the new
11
- * ones.
5
+ * `furnace create --with-tests --xpcshell` writes a scaffold at
6
+ * `browser/base/content/test/<binary>-xpcshell/<name>/`, which a rename must
7
+ * follow. This helper renames the directory, updates the test filename,
8
+ * rewrites the `xpcshell.toml` section header, and re-writes the test body
9
+ * so word-boundary occurrences of the old tag / underscored name map to the
10
+ * new ones.
12
11
  *
13
- * Extracted to keep `rename.ts` under the per-file LOC budget —
14
- * `rename.ts` already carries mochikit + browser-mochitest + FTL
15
- * handling, and tacking xpcshell onto that tree pushed the file past
16
- * the limit.
12
+ * Extracted to keep `rename.ts` under the per-file LOC budget — it already
13
+ * carries mochikit + browser-mochitest + FTL handling.
17
14
  */
18
15
  import { readdir } from 'node:fs/promises';
19
16
  import { join } from 'node:path';
@@ -4,10 +4,10 @@ import { join } from 'node:path';
4
4
  import { getProjectPaths, loadConfig } from '../../core/config.js';
5
5
  import { getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig, } from '../../core/furnace-config.js';
6
6
  import { isComponentSourceFile, resolveFtlChromeSubPath, resolveFtlDir, resolveFtlLocaleJarMnPath, tagNameToClassName, } from '../../core/furnace-constants.js';
7
- import { recordFurnaceRollbackFailure, runFurnaceMutation } from '../../core/furnace-operation.js';
7
+ import { completeJournalRollback, runFurnaceMutation } from '../../core/furnace-operation.js';
8
8
  import { addCustomElementRegistration, addJarMnEntries, addLocaleFtlJarMnEntry, removeCustomElementRegistration, removeJarMnEntries, removeLocaleFtlJarMnEntry, } from '../../core/furnace-registration.js';
9
9
  import { CUSTOM_ELEMENT_TAG_PATTERN, CUSTOM_ELEMENT_TAG_RULES, } from '../../core/furnace-registration-validate.js';
10
- import { createRollbackJournal, recordCreatedDir, restoreRollbackJournalOrThrow, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
10
+ import { createRollbackJournal, recordCreatedDir, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
11
11
  import { getStoriesDir } from '../../core/furnace-stories.js';
12
12
  import { InvalidArgumentError } from '../../errors/base.js';
13
13
  import { FurnaceError } from '../../errors/furnace.js';
@@ -83,15 +83,14 @@ async function renameTestFiles(engineDir, projectRoot, oldName, newName, journal
83
83
  }
84
84
  }
85
85
  /**
86
- * Removes the deployed custom-widget directory at the old target path so
87
- * a subsequent `furnace apply` is the single writer of the new name's
86
+ * Removes the deployed custom-widget directory at the old target path so a
87
+ * subsequent `furnace apply` is the single writer of the new name's
88
88
  * deployment. Best-effort: logs a warning but never blocks the rename.
89
89
  *
90
- * 2026-04-21 eval: renaming `ff-chip-row` → `ff-chip-stack` registered
91
- * and deployed the new name correctly but left `engine/toolkit/content/
92
- * widgets/ff-chip-row/` in place. Subsequent `furnace sync` runs could
93
- * not clear the stale widget, and packaging would have pulled in both
94
- * copies. The snapshot is taken before the remove so the rollback
90
+ * Without it, a rename registers and deploys the new name correctly but
91
+ * leaves `engine/toolkit/content/widgets/<old>/` in place — subsequent
92
+ * `furnace sync` runs cannot clear the stale widget, and packaging pulls in
93
+ * both copies. The snapshot is taken before the remove so the rollback
95
94
  * journal restores the old directory if any later step in
96
95
  * `performRenameMutations` fails.
97
96
  */
@@ -110,19 +109,18 @@ async function removeStaleDeployedComponentDir(engineDir, oldTargetPath, journal
110
109
  }
111
110
  /**
112
111
  * Renames the mochikit test scaffold produced by `furnace create
113
- * --with-tests` when the default test style is used. The scaffold lives
114
- * at `engine/toolkit/content/tests/widgets/test_<name>.html`, and the
115
- * accompanying `chrome.toml` entry names the same file. Neither piece
116
- * was handled by the pre-0.16.0 rename, so operators were left with a
117
- * `test_<old>.html` file that still imported `chrome://global/content/
118
- * elements/<old>.mjs` and referenced `customElements.whenDefined("<old>")`
119
- * — the test ran against a component that no longer existed under that
120
- * name and either failed or (if the old component was still deployed)
121
- * passed for the wrong reason.
112
+ * --with-tests` when the default test style is used. The scaffold lives at
113
+ * `engine/toolkit/content/tests/widgets/test_<name>.html`, and the
114
+ * accompanying `chrome.toml` entry names the same file. Leaving both
115
+ * unhandled produces a `test_<old>.html` that still imports
116
+ * `chrome://global/content/elements/<old>.mjs` and references
117
+ * `customElements.whenDefined("<old>")` — running against a component that
118
+ * no longer exists under that name, and either failing or (if the old
119
+ * component is still deployed) passing for the wrong reason.
122
120
  *
123
- * Best-effort: individual failures log a warning. The same journal used
124
- * for the rest of the rename snapshots every touched file so a later
125
- * failure rolls the pair back together.
121
+ * Best-effort: individual failures log a warning. The same journal used for
122
+ * the rest of the rename snapshots every touched file so a later failure
123
+ * rolls the pair back together.
126
124
  */
127
125
  async function renameMochikitTestFiles(engineDir, oldName, newName, journal) {
128
126
  const testDir = join(engineDir, 'toolkit/content/tests/widgets');
@@ -223,13 +221,13 @@ async function performRenameMutations(args) {
223
221
  continue;
224
222
  const oldFileName = entry.name;
225
223
  // Rename only when the filename starts with the component name — the
226
- // scaffolding convention for both create and override is `${name}.ext`.
227
- // A plain `replace(oldName, newName)` produced wrong results when the
228
- // old name occurred more than once (e.g. `foo-foo.mjs` renamed `foo` →
229
- // `bar` became `bar-foo.mjs` instead of `bar-bar.mjs`) and also when
230
- // the old name appeared inside a file that was not the component
231
- // scaffold itself (e.g. a sibling helper). Unrelated files (stray
232
- // assets, editor backups) are copied verbatim.
224
+ // scaffolding convention for both create and override is
225
+ // `${name}.ext`. A plain `replace(oldName, newName)` produces wrong
226
+ // results when the old name occurs more than once (`foo-foo.mjs`
227
+ // renamed `foo` → `bar` becomes `bar-foo.mjs` instead of
228
+ // `bar-bar.mjs`) and when the old name appears inside a file that is
229
+ // not the component scaffold itself. Unrelated files (stray assets,
230
+ // editor backups) are copied verbatim.
233
231
  const newFileName = renameComponentFileName(oldFileName, oldName, newName);
234
232
  const oldPath = join(oldDir, oldFileName);
235
233
  const newPath = join(newDir, newFileName);
@@ -279,28 +277,25 @@ async function performRenameMutations(args) {
279
277
  // 7. Rename test files created by `furnace create --with-tests` (custom only).
280
278
  if (isCustom && (await pathExists(args.engineDir))) {
281
279
  await renameTestFiles(args.engineDir, projectRoot, oldName, newName, journal);
282
- // Mochikit scaffold + widgets/chrome.toml live in a different
280
+ // The mochikit scaffold and widgets/chrome.toml live in a different
283
281
  // tree than browser.toml-registered browser-chrome tests, so
284
- // renameTestFiles doesn't reach them. 2026-04-21 eval: a rename
285
- // left `engine/toolkit/content/tests/widgets/test_<old>.html`
286
- // and its `chrome.toml` entry pointing at the old name, which
287
- // either failed the test run outright or (worse) passed for the
288
- // wrong component.
282
+ // renameTestFiles does not reach them — leaving
283
+ // `engine/toolkit/content/tests/widgets/test_<old>.html` and its
284
+ // `chrome.toml` entry pointing at the old name, which either fails
285
+ // the test run outright or passes for the wrong component.
289
286
  await renameMochikitTestFiles(args.engineDir, oldName, newName, journal);
290
- // 2026-04-24 eval Finding 5: xpcshell scaffolds live in yet
291
- // another tree (`browser/base/content/test/<binary>-xpcshell/
292
- // <name>/`). Before this call, renaming a component scaffolded
293
- // with `--with-tests --xpcshell` left a directory whose name
294
- // still referenced the pre-rename component, plus a test file
295
- // whose underscored name referenced the old tag — both of
296
- // which then failed to match the new component.
287
+ // xpcshell scaffolds live in yet another tree
288
+ // (`browser/base/content/test/<binary>-xpcshell/<name>/`). Without
289
+ // this call, renaming a component scaffolded with
290
+ // `--with-tests --xpcshell` leaves a directory whose name still
291
+ // references the pre-rename component, plus a test file whose
292
+ // underscored name references the old tag.
297
293
  await renameXpcshellTestFiles(args.engineDir, projectRoot, oldName, newName, journal);
298
294
  // Clear the stale deployed component directory so the next
299
- // `furnace apply` is the single writer of the new name's
300
- // deployment. Without this, eval runs showed the old widget
301
- // still living at `engine/toolkit/content/widgets/<old>/`
302
- // alongside the newly-deployed `engine/toolkit/content/
303
- // widgets/<new>/`, with no signal to `status` / `verify`.
295
+ // `furnace apply` is the single writer of the new name's deployment.
296
+ // Without it the old widget stays at
297
+ // `engine/toolkit/content/widgets/<old>/` alongside the
298
+ // newly-deployed `<new>/`, with no signal to `status` / `verify`.
304
299
  if (oldCustomTargetPath) {
305
300
  await removeStaleDeployedComponentDir(args.engineDir, oldCustomTargetPath, journal);
306
301
  }
@@ -308,9 +303,9 @@ async function performRenameMutations(args) {
308
303
  info(`Renamed ${componentType} component: ${oldName} → ${newName}`);
309
304
  }
310
305
  catch (error) {
311
- // This body owns its rollback end to end, so tell the lifecycle wrapper
312
- // not to restore the same journal again on the way out.
313
- ctx.markRolledBack();
306
+ // Extra best-effort step this site owns: the half-created new
307
+ // directory is not in the journal, so drop it before the shared
308
+ // restore puts the old one back.
314
309
  try {
315
310
  if (await pathExists(newDir)) {
316
311
  await removeDir(newDir);
@@ -319,14 +314,12 @@ async function performRenameMutations(args) {
319
314
  catch {
320
315
  // Best effort cleanup
321
316
  }
322
- try {
323
- await restoreRollbackJournalOrThrow(journal, `Failed to rename component "${oldName}" to "${newName}"`);
324
- }
325
- catch (rollbackError) {
326
- await recordFurnaceRollbackFailure(projectRoot, 'rename-rollback', `rename "${oldName}" → "${newName}": ${toError(rollbackError).message}`);
327
- throw rollbackError;
328
- }
329
- throw error;
317
+ return await completeJournalRollback(ctx, journal, error, {
318
+ projectRoot,
319
+ operation: 'rename-rollback',
320
+ failureMessage: `Failed to rename component "${oldName}" to "${newName}"`,
321
+ subject: `rename "${oldName}" → "${newName}"`,
322
+ });
330
323
  }
331
324
  });
332
325
  }
@@ -360,11 +353,10 @@ async function updateEngineRegistrations(engineDir, oldName, newName, newDir, ft
360
353
  await removeFile(oldFtlPath);
361
354
  }
362
355
  // Re-wire the locale jar.mn chrome registration when the component is
363
- // localized. Before this, `updateEngineRegistrations` renamed the .ftl
364
- // file on disk but left the locale jar.mn pointing at
365
- // `locale/.../${oldName}.ftl`, so `furnace validate` passed while the
366
- // engine still carried a stale registration for the now-missing file
367
- // (eval finding: stale old-name registration after rename).
356
+ // localized. `updateEngineRegistrations` renames the .ftl file on disk but
357
+ // leaves the locale jar.mn pointing at `locale/.../${oldName}.ftl`, so
358
+ // `furnace validate` passes while the engine still carries a stale
359
+ // registration for the now-missing file.
368
360
  if (isLocalized) {
369
361
  const chromeSubPath = resolveFtlChromeSubPath(ftlDir);
370
362
  const localeJarRel = resolveFtlLocaleJarMnPath(ftlDir);
@@ -408,13 +400,11 @@ export async function furnaceRenameCommand(projectRoot, oldName, newName) {
408
400
  // `componentType` is the furnace-state key (singular: `custom` /
409
401
  // `override`); the on-disk directory label differs — custom components
410
402
  // live under `components/custom/` (singular) while overrides live under
411
- // `components/overrides/` (plural). Before 0.16.0, every rename
412
- // user-facing message appended an `s` to `componentType`, which
413
- // produced the wrong label `components/customs/` for custom components
414
- // and was technically correct for overrides only by coincidence.
415
- // `componentDirLabel` centralises the singular/plural pick so every
416
- // operator-facing string names the directory that actually exists on
417
- // disk.
403
+ // `components/overrides/` (plural). Appending an `s` to `componentType`
404
+ // produces the wrong label `components/customs/` for custom components
405
+ // and is correct for overrides only by coincidence. `componentDirLabel`
406
+ // centralises the pick so every operator-facing string names the
407
+ // directory that actually exists on disk.
418
408
  const componentDirLabel = isCustom ? 'custom' : 'overrides';
419
409
  const baseDir = isCustom ? furnacePaths.customDir : furnacePaths.overridesDir;
420
410
  const oldDir = join(baseDir, oldName);
@@ -1,13 +1,11 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { confirm, multiselect, select } from '@clack/prompts';
3
- import { getProjectPaths } from '../../core/config.js';
3
+ import { stdioIsInteractive } from '../../core/destructive.js';
4
4
  import { ensureFurnaceConfig, furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig, } from '../../core/furnace-config.js';
5
- import { recordFurnaceRollbackFailure, runFurnaceMutation } from '../../core/furnace-operation.js';
6
- import { createRollbackJournal, restoreRollbackJournalOrThrow, snapshotFile, } from '../../core/furnace-rollback.js';
5
+ import { completeJournalRollback, runFurnaceMutation } from '../../core/furnace-operation.js';
6
+ import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
7
+ import { createRollbackJournal, snapshotFile } from '../../core/furnace-rollback.js';
7
8
  import { DEEP_SCAN_PATHS, scanWidgetsDirectory } from '../../core/furnace-scanner.js';
8
- import { FurnaceError } from '../../errors/furnace.js';
9
- import { toError } from '../../utils/errors.js';
10
- import { pathExists } from '../../utils/fs.js';
11
9
  import { cancel, info, intro, isCancel, note, outro, spinner, success, } from '../../utils/logger.js';
12
10
  import { furnaceOverrideCommand } from './override.js';
13
11
  /**
@@ -61,16 +59,15 @@ async function promptAddComponents(components, tracked, projectRoot) {
61
59
  /**
62
60
  * Persists discovered component tag names into the `stock` section of
63
61
  * furnace.json. Shared by the interactive confirm flow and the
64
- * non-interactive `--track` flag (0.34.0 field report: scan printed a full
65
- * inventory but persisted nothing and said nothing about where the
66
- * inventory goes).
62
+ * non-interactive `--track` flag — without it, scan prints a full inventory
63
+ * but persists nothing and says nothing about where the inventory goes.
67
64
  *
68
65
  * Wraps the furnace.json mutation in the standard furnace lifecycle so the
69
66
  * write goes through the furnace-wide lock and is visible to the global
70
67
  * SIGINT/SIGTERM rollback pathway. The journal snapshots furnace.json
71
68
  * *before* `ensureFurnaceConfig` runs, so a failed run after the file is
72
- * auto-created cleans up after itself instead of leaving an unwanted
73
- * default config behind.
69
+ * auto-created cleans up after itself instead of leaving an unwanted default
70
+ * config behind.
74
71
  */
75
72
  async function persistStockComponents(projectRoot, names) {
76
73
  await runFurnaceMutation(projectRoot, 'scan-rollback', async (ctx) => {
@@ -92,25 +89,18 @@ async function persistStockComponents(projectRoot, names) {
92
89
  await writeFurnaceConfig(projectRoot, config);
93
90
  }
94
91
  catch (error) {
95
- // This body owns its rollback end to end, so tell the lifecycle wrapper
96
- // not to restore the same journal again on the way out.
97
- ctx.markRolledBack();
98
- try {
99
- await restoreRollbackJournalOrThrow(journal, 'Failed to update furnace.json during scan');
100
- }
101
- catch (rollbackError) {
102
- await recordFurnaceRollbackFailure(projectRoot, 'scan-rollback', `furnace.json update during scan: ${toError(rollbackError).message}`);
103
- throw rollbackError;
104
- }
105
- throw error;
92
+ return await completeJournalRollback(ctx, journal, error, {
93
+ projectRoot: projectRoot,
94
+ operation: 'scan-rollback',
95
+ failureMessage: 'Failed to update furnace.json during scan',
96
+ subject: `furnace.json update during scan`,
97
+ });
106
98
  }
107
99
  });
108
100
  }
109
101
  /**
110
- * Renders a component's discovered capabilities as a display suffix.
111
- * Written twice before 0.41.0 — once for the interactive multiselect labels
112
- * and once for the report rows — which is part of what pinned
113
- * `furnaceScanCommand` at complexity 30/30.
102
+ * Renders a component's discovered capabilities as a display suffix. Shared
103
+ * by the interactive multiselect labels and the report rows.
114
104
  */
115
105
  function formatComponentFeatures(component) {
116
106
  const features = [];
@@ -129,10 +119,7 @@ function formatComponentFeatures(component) {
129
119
  */
130
120
  export async function furnaceScanCommand(projectRoot, options = {}) {
131
121
  intro(options.deep ? 'Furnace Scan (deep)' : 'Furnace Scan');
132
- const paths = getProjectPaths(projectRoot);
133
- if (!(await pathExists(paths.engine))) {
134
- throw new FurnaceError('Engine directory not found. Run "fireforge download" first.');
135
- }
122
+ const { paths } = await assertFurnaceEngineReady(projectRoot);
136
123
  // Load scan paths from config if available, merge with deep paths if requested
137
124
  const extraScanPaths = [];
138
125
  if (await furnaceConfigExists(projectRoot)) {
@@ -165,8 +152,7 @@ export async function furnaceScanCommand(projectRoot, options = {}) {
165
152
  tracked.set(name, 'custom');
166
153
  }
167
154
  }
168
- // Partition once and reuse: the tracked/untracked split was computed twice
169
- // (imperatively here, declaratively again for --track) before 0.41.0.
155
+ // Partition once and reuse across the report and the `--track` path.
170
156
  const untrackedComponentList = components.filter((c) => !tracked.has(c.tagName));
171
157
  const untrackedCount = untrackedComponentList.length;
172
158
  const trackedCount = components.length - untrackedCount;
@@ -191,7 +177,7 @@ export async function furnaceScanCommand(projectRoot, options = {}) {
191
177
  outro('Scan complete');
192
178
  return;
193
179
  }
194
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
180
+ const isInteractive = stdioIsInteractive();
195
181
  if (isInteractive && untrackedCount > 0) {
196
182
  await promptAddComponents(components, tracked, projectRoot);
197
183
  return;
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { join } from 'node:path';
3
3
  import { getProjectPaths, loadConfig } from '../../core/config.js';
4
- import { extractComponentChecksums, hasComponentChanged, hasCustomEngineDrift, hasOverrideEngineDrift, } from '../../core/furnace-apply.js';
4
+ import { extractComponentChecksums, hasComponentChanged, hasCustomEngineDrift, hasOverrideEngineDrift, } from '../../core/furnace-apply-helpers.js';
5
5
  import { furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, loadFurnaceState, } from '../../core/furnace-config.js';
6
6
  import { resolveFtlDir } from '../../core/furnace-constants.js';
7
7
  import { checkRegistrationConsistency } from '../../core/furnace-validate-checks.js';
@@ -13,7 +13,11 @@ import { info, intro, note, outro, warn } from '../../utils/logger.js';
13
13
  * Displays detailed status for a single Furnace component, including registration drift.
14
14
  * @param name - Component tag name to inspect
15
15
  * @param config - Loaded Furnace configuration
16
+ * @param state - Loaded furnace state, for applied/engine checksums
16
17
  * @param projectRoot - Root directory of the project
18
+ * @param paths - Resolved project paths
19
+ * @param furnacePaths - Resolved Furnace-specific paths
20
+ * @param ftlDir - Engine-relative directory localized components deploy their `.ftl` into
17
21
  */
18
22
  async function showDetailedComponentStatus(name, config, state, projectRoot, paths, furnacePaths, ftlDir) {
19
23
  const customConfig = config.custom[name];
@@ -53,7 +57,7 @@ async function showDetailedComponentStatus(name, config, state, projectRoot, pat
53
57
  outro('Status complete');
54
58
  return;
55
59
  }
56
- const engineDrifted = await hasOverrideEngineDrift(paths.engine, overrideDir, overrideConfig, ftlDir);
60
+ const engineDrifted = await hasOverrideEngineDrift({ engineDir: paths.engine, componentDir: overrideDir, ftlDir }, overrideConfig);
57
61
  lines.push(`${engineDrifted ? '\u2717' : '\u2713'} Engine matches override workspace`);
58
62
  note(lines.join('\n'), `${name} Override Status`);
59
63
  outro('Status complete');
@@ -167,11 +171,10 @@ export async function furnaceStatusCommand(projectRoot, name) {
167
171
  *
168
172
  * The engine probe sits behind `else if` deliberately: a component whose
169
173
  * workspace already changed does not need an engine comparison to decide
170
- * what `status` reports, and the probe is the expensive half (it re-hashes
171
- * deployed files). The consequence is that engine drift can go UNREPORTED
172
- * for a component that also has workspace changes — the summary says
173
- * "workspace changed", not both. Preserved verbatim from the two
174
- * hand-written copies this replaced.
174
+ * what `status` reports, and the probe is the expensive half (it
175
+ * re-hashes deployed files). The consequence is that engine drift can go
176
+ * UNREPORTED for a component that also has workspace changes — the
177
+ * summary says "workspace changed", not both.
175
178
  */
176
179
  const scanFamily = async (entries, baseDir, kind, probeEngineDrift) => {
177
180
  for (const [componentName, componentConfig] of entries) {
@@ -189,8 +192,8 @@ export async function furnaceStatusCommand(projectRoot, name) {
189
192
  }
190
193
  }
191
194
  };
192
- await scanFamily(Object.entries(config.overrides), furnacePaths.overridesDir, 'override', (_name, componentDir, cfg) => hasOverrideEngineDrift(paths.engine, componentDir, cfg, ftlDir));
193
- await scanFamily(Object.entries(config.custom), furnacePaths.customDir, 'custom', (name, componentDir, cfg) => hasCustomEngineDrift(projectRoot, name, componentDir, cfg, ftlDir));
195
+ await scanFamily(Object.entries(config.overrides), furnacePaths.overridesDir, 'override', (_name, componentDir, cfg) => hasOverrideEngineDrift({ engineDir: paths.engine, componentDir, ftlDir }, cfg));
196
+ await scanFamily(Object.entries(config.custom), furnacePaths.customDir, 'custom', (name, componentDir, cfg) => hasCustomEngineDrift({ root: projectRoot, name, componentDir, ftlDir }, cfg));
194
197
  if (drift.workspaceChanged) {
195
198
  warn('Components have been modified since last apply. Run `fireforge build` or `fireforge furnace apply`.');
196
199
  }
@@ -1,12 +1,13 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { readdir } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
- import { getProjectPaths, loadConfig } from '../../core/config.js';
4
+ import { loadConfig } from '../../core/config.js';
5
5
  import { applyAllComponents } from '../../core/furnace-apply.js';
6
6
  import { logApplyResult } from '../../core/furnace-apply-output.js';
7
- import { furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, } from '../../core/furnace-config.js';
7
+ import { getFurnacePaths, loadFurnaceConfig } from '../../core/furnace-config.js';
8
8
  import { reportJsconfigPathsSync } from '../../core/furnace-jsconfig.js';
9
9
  import { runFurnaceMutation } from '../../core/furnace-operation.js';
10
+ import { assertFurnaceReady } from '../../core/furnace-precondition.js';
10
11
  import { countEntriesWithBlockingStepErrors } from '../../core/furnace-step-errors.js';
11
12
  import { containsMergeConflictMarkers } from '../../core/furnace-validate-structure.js';
12
13
  import { findOverrideBaseVersionDrift, formatOverrideBaseVersionDriftWarning, } from '../../core/furnace-version-drift.js';
@@ -63,15 +64,7 @@ async function findComponentsWithConflictMarkers(furnacePaths, config) {
63
64
  */
64
65
  export async function furnaceSyncCommand(projectRoot, options = {}) {
65
66
  intro('Furnace Sync');
66
- // Pre-flight checks
67
- const paths = getProjectPaths(projectRoot);
68
- if (!(await pathExists(paths.engine))) {
69
- throw new FurnaceError('Engine directory not found. Run "fireforge download" first.');
70
- }
71
- if (!(await furnaceConfigExists(projectRoot))) {
72
- throw new FurnaceError('No furnace.json found. Run "fireforge furnace create" or "fireforge furnace override" to get started.');
73
- }
74
- const config = await loadFurnaceConfig(projectRoot);
67
+ const { config } = await assertFurnaceReady(projectRoot);
75
68
  const forgeConfig = await loadConfig(projectRoot);
76
69
  const overrideCount = Object.keys(config.overrides).length;
77
70
  const customCount = Object.keys(config.custom).length;
@@ -100,11 +93,11 @@ export async function furnaceSyncCommand(projectRoot, options = {}) {
100
93
  }
101
94
  // Phase 2.5: post-refresh gates. Refresh deliberately leaves conflict
102
95
  // markers in workspace files and does NOT bump baseVersion on conflict —
103
- // so before this gate existed, sync would warn about conflicts and then
104
- // copy the marker-laden files straight into the engine via Phase 3,
105
- // producing a broken build from the one command whose whole purpose is a
106
- // safe upgrade. Apply/deploy both enforce a drift gate; sync must not be
107
- // the back door around it.
96
+ // so without this gate sync warns about conflicts and then copies the
97
+ // marker-laden files straight into the engine via Phase 3, producing a
98
+ // broken build from the one command whose whole purpose is a safe upgrade.
99
+ // Apply and deploy both enforce a drift gate; sync must not be the back
100
+ // door around it.
108
101
  if (!options.dryRun) {
109
102
  const refreshedConfig = await loadFurnaceConfig(projectRoot);
110
103
  const furnacePaths = getFurnacePaths(projectRoot);