@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,13 +3,12 @@
3
3
  * MochiKit (chrome://mochikit) test-harness scaffolder for
4
4
  * `fireforge furnace create --test-style=mochikit`.
5
5
  *
6
- * Motivation: browser-chrome mochitests require a `tabbrowser` to exist in
7
- * the top-level chrome document. Forks with a bespoke chrome document
8
- * (e.g. `mybrowser.xhtml`) that deliberately omits tabbrowser cannot run
9
- * browser-chrome tests today. MochiKit tests load the component module
10
- * directly via `chrome://global/` and assert against `customElements`, so
11
- * they work against any fork that registers the upstream toolkit test
12
- * manifest tree — including those without a tabbrowser.
6
+ * Browser-chrome mochitests require a `tabbrowser` in the top-level chrome
7
+ * document, so forks with a bespoke chrome document that deliberately omits
8
+ * one cannot run them. MochiKit tests load the component module directly via
9
+ * `chrome://global/` and assert against `customElements`, so they work
10
+ * against any fork that registers the upstream toolkit test manifest tree —
11
+ * including those without a tabbrowser.
13
12
  */
14
13
  import { join } from 'node:path';
15
14
  import { recordCreatedDir, snapshotFile, } from '../../core/furnace-rollback.js';
@@ -41,8 +40,8 @@ export async function scaffoldMochikitTestFiles(componentName, license, paths, j
41
40
  const testFileName = mochikitTestFileName(componentName);
42
41
  const testFilePath = join(testDir, testFileName);
43
42
  if (await pathExists(testFilePath)) {
44
- // Never clobber an existing test implementation (0.34.0 collision
45
- // safety, matching the browser-chrome and xpcshell scaffolds).
43
+ // Never clobber an existing test implementation, matching the
44
+ // browser-chrome and xpcshell scaffolds.
46
45
  warn(`toolkit/content/tests/widgets/${testFileName} already exists — keeping the existing file.`);
47
46
  }
48
47
  else {
@@ -4,20 +4,19 @@
4
4
  * budget.
5
5
  */
6
6
  /**
7
- * Asserts that the just-written furnace.json contains the expected
8
- * custom component entry. The eval run's finding #9 observed a
9
- * scenario where `furnace create --allow-prefix-mismatch` reported
10
- * success and wrote the component files, but the subsequent
11
- * `furnace status` found `custom: {}` in furnace.json — an invariant
12
- * violation with no clear smoking gun in the code path. Local repros
13
- * do not trigger it, so the defensive readback is the safest recovery
14
- * contract we can offer: if the new entry is not visible on the next
15
- * load, throw a `FurnaceError` so the rollback journal restores the
16
- * pre-command state and the operator sees the failure instead of a
17
- * phantom success.
7
+ * Asserts that the just-written furnace.json contains the expected custom
8
+ * component entry.
9
+ *
10
+ * A `furnace create` that reports success and writes the component files
11
+ * while the next `furnace status` finds `custom: {}` is an invariant
12
+ * violation with no obvious smoking gun in the code path. This defensive
13
+ * readback is the recovery contract: if the new entry is not visible on the
14
+ * next load, throw a `FurnaceError` so the rollback journal restores the
15
+ * pre-command state and the operator sees the failure instead of a phantom
16
+ * success.
18
17
  *
19
18
  * @param projectRoot - Root of the FireForge project
20
- * @param componentName - Custom-element tag name that must be present
21
- * in `config.custom` after the write. Throws when absent.
19
+ * @param componentName - Custom-element tag name that must be present in
20
+ * `config.custom` after the write. Throws when absent.
22
21
  */
23
22
  export declare function assertCustomEntryPersisted(projectRoot: string, componentName: string): Promise<void>;
@@ -7,21 +7,20 @@
7
7
  import { loadFurnaceConfig } from '../../core/furnace-config.js';
8
8
  import { FurnaceError } from '../../errors/furnace.js';
9
9
  /**
10
- * Asserts that the just-written furnace.json contains the expected
11
- * custom component entry. The eval run's finding #9 observed a
12
- * scenario where `furnace create --allow-prefix-mismatch` reported
13
- * success and wrote the component files, but the subsequent
14
- * `furnace status` found `custom: {}` in furnace.json — an invariant
15
- * violation with no clear smoking gun in the code path. Local repros
16
- * do not trigger it, so the defensive readback is the safest recovery
17
- * contract we can offer: if the new entry is not visible on the next
18
- * load, throw a `FurnaceError` so the rollback journal restores the
19
- * pre-command state and the operator sees the failure instead of a
20
- * phantom success.
10
+ * Asserts that the just-written furnace.json contains the expected custom
11
+ * component entry.
12
+ *
13
+ * A `furnace create` that reports success and writes the component files
14
+ * while the next `furnace status` finds `custom: {}` is an invariant
15
+ * violation with no obvious smoking gun in the code path. This defensive
16
+ * readback is the recovery contract: if the new entry is not visible on the
17
+ * next load, throw a `FurnaceError` so the rollback journal restores the
18
+ * pre-command state and the operator sees the failure instead of a phantom
19
+ * success.
21
20
  *
22
21
  * @param projectRoot - Root of the FireForge project
23
- * @param componentName - Custom-element tag name that must be present
24
- * in `config.custom` after the write. Throws when absent.
22
+ * @param componentName - Custom-element tag name that must be present in
23
+ * `config.custom` after the write. Throws when absent.
25
24
  */
26
25
  export async function assertCustomEntryPersisted(projectRoot, componentName) {
27
26
  const persisted = await loadFurnaceConfig(projectRoot);
@@ -36,19 +36,18 @@ export declare function xpcshellTestFileName(name: string): string;
36
36
  * xpcshell cannot execute a component module that imports
37
37
  * `chrome://global/content/vendor/lit.all.mjs` — the Lit bundle touches
38
38
  * `window` at module-load time and the xpcshell harness has no `window`
39
- * global. Before 0.16.0 the scaffold called `ChromeUtils.importESModule`
40
- * on the component's MJS, which reliably failed with
41
- * `ReferenceError: window is not defined` for every Lit-based fork
42
- * component. FireForge's diagnostics then misrouted the failure to the
43
- * "stale build artifacts" branch, sending operators on a rebuild loop
44
- * that couldn't fix a runtime-environment incompatibility.
39
+ * global. A scaffold that calls `ChromeUtils.importESModule` on the
40
+ * component's MJS therefore fails with `ReferenceError: window is not
41
+ * defined` for every Lit-based fork component, and FireForge's diagnostics
42
+ * misroute that to the "stale build artifacts" branch, sending operators on
43
+ * a rebuild loop that cannot fix a runtime-environment incompatibility.
45
44
  *
46
- * The rewrite here mirrors the chrome-doc packaging test: XCurProcD is
45
+ * This template mirrors the chrome-doc packaging test instead: XCurProcD is
47
46
  * probed at a pair of candidate layouts (dist/bin/browser and the macOS
48
- * .app-bundle / ESR layout) to confirm the `.mjs` and `.css` files
49
- * landed where jar.mn promised. That's the assertion xpcshell CAN make.
50
- * Functional tests that need DOM/shadow-root/keyboard behaviour belong
51
- * in a browser-chrome mochitest — scaffolded via
47
+ * .app-bundle / ESR layout) to confirm the `.mjs` and `.css` files landed
48
+ * where jar.mn promised. That is the assertion xpcshell CAN make. Functional
49
+ * tests that need DOM/shadow-root/keyboard behaviour belong in a
50
+ * browser-chrome mochitest — scaffolded via
52
51
  * `fireforge furnace create --test-style browser-chrome`.
53
52
  */
54
53
  export declare function generateXpcshellTestContent(name: string, header: string): string;
@@ -67,26 +66,21 @@ export declare function mochikitTestFileName(name: string): string;
67
66
  * MochiKit tests load the component module directly via the global chrome
68
67
  * URI and assert that `customElements.get(<tag>)` returns a constructor.
69
68
  * They run on forks whose top-level chrome document lacks a `tabbrowser`
70
- * (the class of bug that forces `--xpcshell` for storage code) because
71
- * they do not traverse `URILoadingHelper.openLinkIn`.
69
+ * because they do not traverse `URILoadingHelper.openLinkIn`.
72
70
  *
73
- * The scaffold here is a smoke test — the component is defined and the
74
- * constructor is a function. Real UI assertions (render output, l10n
75
- * wiring, keyboard interactions) are intentionally left out because they
76
- * depend on the component's shape; operators can extend the test using
77
- * the same SimpleTest APIs upstream toolkit widgets (moz-button, etc.)
78
- * rely on.
71
+ * The scaffold is a smoke test — the component is defined and the
72
+ * constructor is a function. Real UI assertions (render output, l10n wiring,
73
+ * keyboard interactions) are left out because they depend on the
74
+ * component's shape; operators can extend the test using the same SimpleTest
75
+ * APIs upstream toolkit widgets rely on.
79
76
  *
80
77
  * The template deliberately omits `SimpleTest.waitForExplicitFinish()`.
81
- * `add_task` owns the test lifecycle: when every queued task resolves,
82
- * the task harness calls `SimpleTest.finish()` on its own. Combining
83
- * `waitForExplicitFinish()` with `add_task` *and* no explicit
84
- * `SimpleTest.finish()` inside the task body makes the harness wait
85
- * forever, which the 2026-04-21 eval run tripped into as an indefinite
86
- * hang on a `fireforge test --headless` against a scaffolded widget
87
- * test. Leaving `waitForExplicitFinish()` out matches the convention
88
- * upstream toolkit widget tests use (see `test_moz-button.html` and
89
- * siblings under `toolkit/content/tests/widgets/`).
78
+ * `add_task` owns the test lifecycle: when every queued task resolves, the
79
+ * task harness calls `SimpleTest.finish()` itself. Combining
80
+ * `waitForExplicitFinish()` with `add_task` and no explicit
81
+ * `SimpleTest.finish()` in the task body makes the harness wait forever —
82
+ * an indefinite hang on `fireforge test --headless`. Omitting it matches the
83
+ * convention upstream toolkit widget tests use.
90
84
  */
91
85
  export declare function generateMochikitTestContent(name: string): string;
92
86
  /**
@@ -25,14 +25,25 @@
25
25
  * 3. `<name>.ftl` — fallback when no chrome sub-path was resolvable.
26
26
  */
27
27
  export function generateMjsContent(name, className, description, localized, header, ftlChromeSubPath, sharedFtl) {
28
+ // A `*/` in the operator-supplied description would close the JSDoc block
29
+ // early and leave the remainder as stray code. Escaped here rather than
30
+ // rejected at input, because the same text is also rendered by
31
+ // create-dry-run.ts, where no escaping is wanted.
32
+ const jsDocDescription = (description || name).replace(/\*\//g, '*\\/');
28
33
  const ftlPath = sharedFtl !== undefined
29
34
  ? sharedFtl
30
35
  : ftlChromeSubPath !== undefined
31
36
  ? `${ftlChromeSubPath}/${name}.ftl`
32
37
  : `${name}.ftl`;
38
+ // Escape at the SINK, not at the validator. `ftlPath` reaches here from
39
+ // three sources — the `--shared-ftl` flag, the furnace.json `ftlBasePath`,
40
+ // and a derived `<name>.ftl` — and only the first passes through
41
+ // `validateSharedFtl`. `JSON.stringify` produces a correctly-escaped JS
42
+ // string literal for all of them, including the `"` and raw-newline cases
43
+ // that would otherwise break the generated module into a syntax error.
33
44
  const ftlModulePreamble = localized
34
45
  ? `
35
- window.MozXULElement?.insertFTLIfNeeded("${ftlPath}");
46
+ window.MozXULElement?.insertFTLIfNeeded(${JSON.stringify(ftlPath)});
36
47
  `
37
48
  : '';
38
49
  const lifecycleHooks = localized
@@ -60,7 +71,7 @@ import { html } from "chrome://global/content/vendor/lit.all.mjs";
60
71
  import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
61
72
  ${ftlModulePreamble}
62
73
  /**
63
- * ${description || name}
74
+ * ${jsDocDescription}
64
75
  *
65
76
  * @tagname ${name}
66
77
  */
@@ -108,19 +119,18 @@ export function xpcshellTestFileName(name) {
108
119
  * xpcshell cannot execute a component module that imports
109
120
  * `chrome://global/content/vendor/lit.all.mjs` — the Lit bundle touches
110
121
  * `window` at module-load time and the xpcshell harness has no `window`
111
- * global. Before 0.16.0 the scaffold called `ChromeUtils.importESModule`
112
- * on the component's MJS, which reliably failed with
113
- * `ReferenceError: window is not defined` for every Lit-based fork
114
- * component. FireForge's diagnostics then misrouted the failure to the
115
- * "stale build artifacts" branch, sending operators on a rebuild loop
116
- * that couldn't fix a runtime-environment incompatibility.
122
+ * global. A scaffold that calls `ChromeUtils.importESModule` on the
123
+ * component's MJS therefore fails with `ReferenceError: window is not
124
+ * defined` for every Lit-based fork component, and FireForge's diagnostics
125
+ * misroute that to the "stale build artifacts" branch, sending operators on
126
+ * a rebuild loop that cannot fix a runtime-environment incompatibility.
117
127
  *
118
- * The rewrite here mirrors the chrome-doc packaging test: XCurProcD is
128
+ * This template mirrors the chrome-doc packaging test instead: XCurProcD is
119
129
  * probed at a pair of candidate layouts (dist/bin/browser and the macOS
120
- * .app-bundle / ESR layout) to confirm the `.mjs` and `.css` files
121
- * landed where jar.mn promised. That's the assertion xpcshell CAN make.
122
- * Functional tests that need DOM/shadow-root/keyboard behaviour belong
123
- * in a browser-chrome mochitest — scaffolded via
130
+ * .app-bundle / ESR layout) to confirm the `.mjs` and `.css` files landed
131
+ * where jar.mn promised. That is the assertion xpcshell CAN make. Functional
132
+ * tests that need DOM/shadow-root/keyboard behaviour belong in a
133
+ * browser-chrome mochitest — scaffolded via
124
134
  * `fireforge furnace create --test-style browser-chrome`.
125
135
  */
126
136
  export function generateXpcshellTestContent(name, header) {
@@ -225,26 +235,21 @@ export function mochikitTestFileName(name) {
225
235
  * MochiKit tests load the component module directly via the global chrome
226
236
  * URI and assert that `customElements.get(<tag>)` returns a constructor.
227
237
  * They run on forks whose top-level chrome document lacks a `tabbrowser`
228
- * (the class of bug that forces `--xpcshell` for storage code) because
229
- * they do not traverse `URILoadingHelper.openLinkIn`.
238
+ * because they do not traverse `URILoadingHelper.openLinkIn`.
230
239
  *
231
- * The scaffold here is a smoke test — the component is defined and the
232
- * constructor is a function. Real UI assertions (render output, l10n
233
- * wiring, keyboard interactions) are intentionally left out because they
234
- * depend on the component's shape; operators can extend the test using
235
- * the same SimpleTest APIs upstream toolkit widgets (moz-button, etc.)
236
- * rely on.
240
+ * The scaffold is a smoke test — the component is defined and the
241
+ * constructor is a function. Real UI assertions (render output, l10n wiring,
242
+ * keyboard interactions) are left out because they depend on the
243
+ * component's shape; operators can extend the test using the same SimpleTest
244
+ * APIs upstream toolkit widgets rely on.
237
245
  *
238
246
  * The template deliberately omits `SimpleTest.waitForExplicitFinish()`.
239
- * `add_task` owns the test lifecycle: when every queued task resolves,
240
- * the task harness calls `SimpleTest.finish()` on its own. Combining
241
- * `waitForExplicitFinish()` with `add_task` *and* no explicit
242
- * `SimpleTest.finish()` inside the task body makes the harness wait
243
- * forever, which the 2026-04-21 eval run tripped into as an indefinite
244
- * hang on a `fireforge test --headless` against a scaffolded widget
245
- * test. Leaving `waitForExplicitFinish()` out matches the convention
246
- * upstream toolkit widget tests use (see `test_moz-button.html` and
247
- * siblings under `toolkit/content/tests/widgets/`).
247
+ * `add_task` owns the test lifecycle: when every queued task resolves, the
248
+ * task harness calls `SimpleTest.finish()` itself. Combining
249
+ * `waitForExplicitFinish()` with `add_task` and no explicit
250
+ * `SimpleTest.finish()` in the task body makes the harness wait forever —
251
+ * an indefinite hang on `fireforge test --headless`. Omitting it matches the
252
+ * convention upstream toolkit widget tests use.
248
253
  */
249
254
  export function generateMochikitTestContent(name) {
250
255
  return `<!DOCTYPE html>
@@ -43,8 +43,8 @@ export async function scaffoldXpcshellTestFiles(componentName, license, forgeCon
43
43
  const testFileName = xpcshellTestFileName(componentName);
44
44
  const testFilePath = join(testDir, testFileName);
45
45
  if (await pathExists(testFilePath)) {
46
- // Never clobber an existing test implementation (0.34.0 field report:
47
- // the scaffold overwrote files owned by a different patch).
46
+ // Never clobber an existing test implementation: the scaffold must not
47
+ // overwrite files owned by a different patch.
48
48
  warn(`${testDirRel}/${testFileName} already exists — keeping the existing file.`);
49
49
  }
50
50
  else {
@@ -2,17 +2,17 @@
2
2
  import { join } from 'node:path';
3
3
  import { text } from '@clack/prompts';
4
4
  import { getProjectPaths, loadConfig } from '../../core/config.js';
5
+ import { stdioIsInteractive } from '../../core/destructive.js';
5
6
  import { createDefaultFurnaceConfig, furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig, } from '../../core/furnace-config.js';
6
- import { resolveFtlChromeSubPath, tagNameToClassName } from '../../core/furnace-constants.js';
7
- import { recordFurnaceRollbackFailure, runFurnaceMutation, } from '../../core/furnace-operation.js';
8
- import { CUSTOM_ELEMENT_TAG_PATTERN, CUSTOM_ELEMENT_TAG_RULES, } from '../../core/furnace-registration-validate.js';
9
- import { createRollbackJournal, recordCreatedDir, restoreRollbackJournalOrThrow, snapshotFile, } from '../../core/furnace-rollback.js';
7
+ import { resolveFtlChromeSubPath, tagNameToClassName, WIDGETS_DIR, } from '../../core/furnace-constants.js';
8
+ import { completeJournalRollback, runFurnaceMutation, } from '../../core/furnace-operation.js';
9
+ import { CUSTOM_ELEMENT_TAG_PATTERN, CUSTOM_ELEMENT_TAG_RULES, describeTagNameProblem, } from '../../core/furnace-registration-validate.js';
10
+ import { createRollbackJournal, recordCreatedDir, snapshotFile, } from '../../core/furnace-rollback.js';
10
11
  import { isComponentInEngine, scanWidgetsDirectory } from '../../core/furnace-scanner.js';
11
12
  import { DEFAULT_LICENSE, getLicenseHeader } from '../../core/license-headers.js';
12
13
  import { validateSharedFtl } from '../../core/shared-ftl.js';
13
14
  import { InvalidArgumentError } from '../../errors/base.js';
14
15
  import { FurnaceError } from '../../errors/furnace.js';
15
- import { toError } from '../../utils/errors.js';
16
16
  import { ensureDir, pathExists, writeText } from '../../utils/fs.js';
17
17
  import { cancel, intro, isCancel, note, outro } from '../../utils/logger.js';
18
18
  import { resolveValidatedTestDir, scaffoldTestFiles } from './create-browser-test.js';
@@ -70,6 +70,8 @@ function validateTagName(name) {
70
70
  * @param description - Human-readable component description
71
71
  * @param localized - Whether to include a Fluent file
72
72
  * @param license - Project license used for generated headers
73
+ * @param ftlChromeSubPath - chrome:// sub-path for the generated FTL reference, when localized
74
+ * @param sharedFtl - Explicit shared FTL path from `--shared-ftl`, when supplied
73
75
  * @param journal - Optional rollback journal that snapshots files before writes
74
76
  * @returns Relative filenames written for the component
75
77
  */
@@ -166,7 +168,7 @@ async function performCreateMutations(args) {
166
168
  files = await writeComponentFiles(args.componentDir, args.componentName, args.className, args.description, args.localized, args.license, resolveFtlChromeSubPath(freshConfig.ftlBasePath), args.sharedFtl, journal);
167
169
  const customEntry = {
168
170
  description: args.description,
169
- targetPath: `toolkit/content/widgets/${args.componentName}`,
171
+ targetPath: `${WIDGETS_DIR}/${args.componentName}`,
170
172
  register: args.register,
171
173
  localized: args.localized,
172
174
  };
@@ -199,15 +201,16 @@ async function performCreateMutations(args) {
199
201
  }
200
202
  }
201
203
  catch (error) {
202
- // This body owns its rollback end to end, so tell the lifecycle wrapper
203
- // not to restore the same journal again on the way out.
204
- args.operationContext?.markRolledBack();
205
- try {
206
- await restoreRollbackJournalOrThrow(journal, `Failed to create custom component "${args.componentName}"`);
207
- }
208
- catch (rollbackError) {
209
- await recordFurnaceRollbackFailure(args.projectRoot, 'create-rollback', `component "${args.componentName}": ${toError(rollbackError).message}`);
210
- throw rollbackError;
204
+ // `operationContext` is optional here (the dry-run and no-journal paths
205
+ // pass none), so the shared handler only applies when there is a
206
+ // lifecycle wrapper to tell.
207
+ if (args.operationContext) {
208
+ return await completeJournalRollback(args.operationContext, journal, error, {
209
+ projectRoot: args.projectRoot,
210
+ operation: 'create-rollback',
211
+ failureMessage: `Failed to create custom component "${args.componentName}"`,
212
+ subject: `component "${args.componentName}"`,
213
+ });
211
214
  }
212
215
  throw error;
213
216
  }
@@ -237,7 +240,7 @@ async function resolveDescription(isInteractive, options) {
237
240
  * @param options - CLI options for non-interactive mode
238
241
  */
239
242
  export async function furnaceCreateCommand(projectRoot, name, options = {}) {
240
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
243
+ const isInteractive = stdioIsInteractive();
241
244
  const isDryRun = options.dryRun ?? false;
242
245
  intro(isDryRun ? 'Furnace Create (dry run)' : 'Furnace Create');
243
246
  // --- Resolve component name ---
@@ -264,7 +267,9 @@ export async function furnaceCreateCommand(projectRoot, name, options = {}) {
264
267
  const nameResult = await text({
265
268
  message: 'Component tag name:',
266
269
  placeholder: 'moz-my-widget',
267
- validate: (value) => validateTagName(value ?? ''),
270
+ // Message-returning form: the throwing `validateTagName` escaped
271
+ // clack's validation loop and killed the prompt on a bad name.
272
+ validate: (value) => describeTagNameProblem(value ?? ''),
268
273
  });
269
274
  if (isCancel(nameResult)) {
270
275
  cancel('Create cancelled');
@@ -304,6 +309,11 @@ export async function furnaceCreateCommand(projectRoot, name, options = {}) {
304
309
  // partial mutation behind.
305
310
  const testStyle = resolveTestStyle(options);
306
311
  const testDir = resolveValidatedTestDir(options.testDir, testStyle);
312
+ // Not routed through assertFurnaceEngineReady: this rung is CONDITIONAL on
313
+ // a test style being requested, and it names the component so the refusal
314
+ // identifies which create failed. Both are outside the shared helper's
315
+ // shape, and a scaffold without --with-tests must keep working with no
316
+ // engine present.
307
317
  if (testStyle !== 'none' && !(await pathExists(paths.engine))) {
308
318
  throw new FurnaceError('Engine directory not found. Run "fireforge download" first to use --with-tests, --xpcshell, or --test-style.', componentName);
309
319
  }
@@ -314,13 +324,13 @@ export async function furnaceCreateCommand(projectRoot, name, options = {}) {
314
324
  if (await pathExists(componentDir)) {
315
325
  throw new FurnaceError(`Directory already exists: components/custom/${componentName}`, componentName);
316
326
  }
317
- // --- Normalize and validate --shared-ftl ahead of any writes. Shares the
327
+ // Normalize and validate --shared-ftl ahead of any writes. Shares the
318
328
  // structural rules with furnace-config.ts so the command and the on-disk
319
- // schema cannot diverge. Pass the resolved `localized` rather than a
320
- // `true` literal so the validator's cross-field check stays anchored to
321
- // the real feature selection — `resolveCreateFeatures` promotes localized
322
- // upstream, but hard-coding `true` here would hide a regression if that
323
- // promotion ever moved or dropped.
329
+ // schema cannot diverge. Pass the resolved `localized` rather than a `true`
330
+ // literal so the validator's cross-field check stays anchored to the real
331
+ // feature selection — `resolveCreateFeatures` promotes localized upstream,
332
+ // and hard-coding `true` here would hide a regression if that promotion
333
+ // ever moved or dropped.
324
334
  let sharedFtl;
325
335
  if (options.sharedFtl !== undefined) {
326
336
  const result = validateSharedFtl(options.sharedFtl, { localized });
@@ -1,15 +1,15 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
- import { getProjectPaths, loadConfig } from '../../core/config.js';
2
+ import { loadConfig } from '../../core/config.js';
3
3
  import { applyAllComponents } from '../../core/furnace-apply.js';
4
4
  import { logApplyResult } from '../../core/furnace-apply-output.js';
5
- import { furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, } from '../../core/furnace-config.js';
5
+ import { getFurnacePaths, loadFurnaceConfig } from '../../core/furnace-config.js';
6
6
  import { reportJsconfigPathsSync } from '../../core/furnace-jsconfig.js';
7
7
  import { runFurnaceMutation } from '../../core/furnace-operation.js';
8
+ import { assertFurnaceReady } from '../../core/furnace-precondition.js';
8
9
  import { getPersistableAppliedEntry, getStepFailureCount, persistSingleComponentState, shouldPersistSingleComponentState, } from '../../core/furnace-state-persist.js';
9
10
  import { hasBlockingStepErrors } from '../../core/furnace-step-errors.js';
10
11
  import { findOverrideBaseVersionDrift, formatOverrideBaseVersionDriftError, formatOverrideBaseVersionDriftWarning, } from '../../core/furnace-version-drift.js';
11
12
  import { FurnaceError } from '../../errors/furnace.js';
12
- import { pathExists } from '../../utils/fs.js';
13
13
  import { info, intro, note, outro, spinner, warn } from '../../utils/logger.js';
14
14
  import { runDeployValidation } from './validation-output.js';
15
15
  /**
@@ -39,28 +39,29 @@ function getFailedComponentNames(result) {
39
39
  return failed;
40
40
  }
41
41
  /**
42
- * Applies a single named override or custom component in targeted deploy mode.
42
+ * Applies a single named override or custom component in targeted deploy
43
+ * mode.
43
44
  *
44
45
  * Delegates to {@link applyAllComponents} with a `componentName` filter so
45
46
  * targeted deploys run the exact same pipeline as deploy-all — including
46
47
  * workspace-deletion detection, engine orphan undeploy, and jar.mn /
47
- * customElements.js re-sync. The previous implementation called the
48
- * per-component apply helpers directly and never pruned: renaming a helper
49
- * file in the workspace left the old deployed file and its stale jar.mn
50
- * line in the engine (field report D1).
48
+ * customElements.js re-sync. Calling the per-component apply helpers
49
+ * directly never prunes: renaming a helper file in the workspace then leaves
50
+ * the old deployed file and its stale jar.mn line in the engine.
51
51
  *
52
52
  * `persistState: false` is load-bearing: the batch persist path *replaces*
53
53
  * `appliedChecksums` wholesale with only this run's entries, which for a
54
54
  * named deploy would wipe every other component's state. Named deploy keeps
55
55
  * its per-component state merge ({@link persistSingleComponentState}) and
56
- * its atomicity gate ({@link shouldPersistNamedDeployState}) at the call
57
- * site. Rollback on failure happens inside `applyAllComponents`; the
58
- * journal returned on success is ignored (the deploy keeps its files).
56
+ * its atomicity gate ({@link shouldPersistSingleComponentState}) at the call
57
+ * site. Rollback on failure happens inside `applyAllComponents`; the journal
58
+ * returned on success is ignored (the deploy keeps its files).
59
59
  *
60
60
  * @param name - Component name to apply
61
61
  * @param config - Loaded Furnace configuration
62
62
  * @param isDryRun - Whether file writes should be skipped
63
- * @returns Apply result for the named component, or `stock` for stock-only entries
63
+ * @returns Apply result for the named component, or `stock` for stock-only
64
+ * entries
64
65
  */
65
66
  async function applyNamedComponent(name, config, isDryRun, projectRoot, operationContext) {
66
67
  if (!(name in config.overrides) && !(name in config.custom)) {
@@ -132,16 +133,7 @@ function enforceScopedOverrideVersionDriftPreflight(scopedDrift, force) {
132
133
  export async function furnaceDeployCommand(projectRoot, name, options = {}) {
133
134
  const isDryRun = options.dryRun ?? false;
134
135
  intro(isDryRun ? 'Furnace Deploy (dry run)' : 'Furnace Deploy');
135
- // Verify engine exists
136
- const paths = getProjectPaths(projectRoot);
137
- if (!(await pathExists(paths.engine))) {
138
- throw new FurnaceError('Engine directory not found. Run "fireforge download" first.');
139
- }
140
- // Load furnace config
141
- if (!(await furnaceConfigExists(projectRoot))) {
142
- throw new FurnaceError('No furnace.json found. Run "fireforge furnace create" or "fireforge furnace override" to get started.');
143
- }
144
- const config = await loadFurnaceConfig(projectRoot);
136
+ const { config } = await assertFurnaceReady(projectRoot);
145
137
  const furnacePaths = getFurnacePaths(projectRoot);
146
138
  const overrideCount = Object.keys(config.overrides).length;
147
139
  const customCount = Object.keys(config.custom).length;
@@ -196,8 +188,8 @@ export async function furnaceDeployCommand(projectRoot, name, options = {}) {
196
188
  applySpinner.stop(isDryRun ? 'Planned actions calculated' : 'Components applied');
197
189
  logApplyResult(result, isDryRun);
198
190
  // Keep the consumer jsconfig's chrome-module `paths` in step with the
199
- // deployed module set (field report D3). Only after a clean apply —
200
- // a rolled-back deploy must not advance the typecheck mapping either.
191
+ // deployed module set. Only after a clean apply — a rolled-back deploy
192
+ // must not advance the typecheck mapping either.
201
193
  if (result.errors.length === 0 && getStepFailureCount(result) === 0) {
202
194
  await reportJsconfigPathsSync(projectRoot, config, isDryRun);
203
195
  }
@@ -99,16 +99,16 @@ async function diffOverride(name, projectRoot, config) {
99
99
  }
100
100
  }
101
101
  /**
102
- * Diffs a custom component's workspace files against the engine-deployed copy.
103
- * Shows what would change (or has changed) on the next `furnace apply`.
102
+ * Diffs a custom component's workspace files against the engine-deployed
103
+ * copy. Shows what would change (or has changed) on the next
104
+ * `furnace apply`.
104
105
  *
105
- * `.ftl` files deploy to `engine/<ftlDir>/<name>.ftl` via `applyCustomFtlFile`
106
- * — NOT to `customConfig.targetPath` — so the deployment-target lookup has
107
- * to branch on extension. Before this branch existed, a component's
108
- * localization file always reported "not yet deployed to engine (new
109
- * file)" after a successful apply/deploy because diff was looking for it
110
- * under the component's `targetPath` while apply had written it into the
111
- * locale tree.
106
+ * `.ftl` files deploy to `engine/<ftlDir>/<name>.ftl` via
107
+ * `applyCustomFtlFile` — NOT to `customConfig.targetPath` — so the
108
+ * deployment-target lookup has to branch on extension. Without the branch, a
109
+ * component's localization file always reports "not yet deployed to engine
110
+ * (new file)" after a successful apply, because diff looks for it under the
111
+ * component's `targetPath` while apply wrote it into the locale tree.
112
112
  */
113
113
  async function diffCustom(name, projectRoot, config) {
114
114
  const customConfig = config.custom[name];