@hominis/fireforge 0.43.1 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/README.md +3 -3
  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 +13 -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-patch-manifest.d.ts +8 -0
  22. package/dist/src/commands/doctor-patch-manifest.js +155 -0
  23. package/dist/src/commands/doctor-post-rebase-audit.d.ts +2 -0
  24. package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
  25. package/dist/src/commands/doctor-working-tree.d.ts +10 -12
  26. package/dist/src/commands/doctor-working-tree.js +11 -18
  27. package/dist/src/commands/doctor.d.ts +1 -1
  28. package/dist/src/commands/doctor.js +64 -96
  29. package/dist/src/commands/download.js +32 -48
  30. package/dist/src/commands/export-all.js +29 -33
  31. package/dist/src/commands/export-flow.js +6 -6
  32. package/dist/src/commands/export-placement-conflicts.js +10 -9
  33. package/dist/src/commands/export-placement-gate.js +5 -5
  34. package/dist/src/commands/export-shared.d.ts +30 -33
  35. package/dist/src/commands/export-shared.js +45 -43
  36. package/dist/src/commands/export.js +21 -27
  37. package/dist/src/commands/furnace/apply.js +14 -23
  38. package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
  39. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
  40. package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
  41. package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
  42. package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
  43. package/dist/src/commands/furnace/chrome-doc.js +31 -41
  44. package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
  45. package/dist/src/commands/furnace/create-browser-test.js +8 -8
  46. package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
  47. package/dist/src/commands/furnace/create-mochikit.js +8 -9
  48. package/dist/src/commands/furnace/create-readback.d.ts +12 -13
  49. package/dist/src/commands/furnace/create-readback.js +12 -13
  50. package/dist/src/commands/furnace/create-templates.d.ts +22 -28
  51. package/dist/src/commands/furnace/create-templates.js +35 -30
  52. package/dist/src/commands/furnace/create-xpcshell.js +2 -2
  53. package/dist/src/commands/furnace/create.js +33 -23
  54. package/dist/src/commands/furnace/deploy.js +16 -24
  55. package/dist/src/commands/furnace/diff.js +15 -10
  56. package/dist/src/commands/furnace/init.js +33 -36
  57. package/dist/src/commands/furnace/override.js +23 -25
  58. package/dist/src/commands/furnace/preview.js +47 -67
  59. package/dist/src/commands/furnace/refresh.js +12 -16
  60. package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
  61. package/dist/src/commands/furnace/remove-mutations.js +14 -17
  62. package/dist/src/commands/furnace/remove-state.js +1 -1
  63. package/dist/src/commands/furnace/remove.js +33 -38
  64. package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
  65. package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
  66. package/dist/src/commands/furnace/rename.js +59 -69
  67. package/dist/src/commands/furnace/scan.js +19 -33
  68. package/dist/src/commands/furnace/status.js +12 -9
  69. package/dist/src/commands/furnace/sync.js +9 -16
  70. package/dist/src/commands/furnace/validate.js +20 -20
  71. package/dist/src/commands/import.js +48 -37
  72. package/dist/src/commands/lint-per-patch.js +15 -10
  73. package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
  74. package/dist/src/commands/lint-per-run-checkjs.js +13 -13
  75. package/dist/src/commands/lint-report.js +6 -7
  76. package/dist/src/commands/lint.d.ts +15 -16
  77. package/dist/src/commands/lint.js +86 -102
  78. package/dist/src/commands/package.js +6 -11
  79. package/dist/src/commands/patch/compact.js +1 -1
  80. package/dist/src/commands/patch/delete.js +24 -33
  81. package/dist/src/commands/patch/index.d.ts +5 -6
  82. package/dist/src/commands/patch/index.js +10 -12
  83. package/dist/src/commands/patch/lint-ignore.js +14 -13
  84. package/dist/src/commands/patch/move-files-create.d.ts +6 -6
  85. package/dist/src/commands/patch/move-files-create.js +8 -8
  86. package/dist/src/commands/patch/move-files-into.d.ts +6 -6
  87. package/dist/src/commands/patch/move-files-into.js +8 -8
  88. package/dist/src/commands/patch/patch-context.d.ts +2 -3
  89. package/dist/src/commands/patch/patch-context.js +2 -3
  90. package/dist/src/commands/patch/rename.d.ts +10 -11
  91. package/dist/src/commands/patch/rename.js +11 -12
  92. package/dist/src/commands/patch/reorder.js +8 -9
  93. package/dist/src/commands/patch/split-plan.js +1 -1
  94. package/dist/src/commands/patch/split.d.ts +8 -10
  95. package/dist/src/commands/patch/split.js +9 -11
  96. package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
  97. package/dist/src/commands/patch/staged-dependency.js +7 -3
  98. package/dist/src/commands/patch/tier.d.ts +9 -9
  99. package/dist/src/commands/patch/tier.js +18 -15
  100. package/dist/src/commands/re-export-adjacent.js +9 -8
  101. package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
  102. package/dist/src/commands/re-export-bulk-scan.js +22 -21
  103. package/dist/src/commands/re-export-drift.js +10 -10
  104. package/dist/src/commands/re-export-files.d.ts +6 -7
  105. package/dist/src/commands/re-export-files.js +34 -44
  106. package/dist/src/commands/re-export-lint.d.ts +10 -13
  107. package/dist/src/commands/re-export-lint.js +16 -14
  108. package/dist/src/commands/re-export-options.d.ts +6 -6
  109. package/dist/src/commands/re-export-options.js +6 -6
  110. package/dist/src/commands/re-export-register.d.ts +4 -10
  111. package/dist/src/commands/re-export-register.js +5 -11
  112. package/dist/src/commands/re-export-scan.js +1 -1
  113. package/dist/src/commands/re-export-single.d.ts +3 -3
  114. package/dist/src/commands/re-export-single.js +33 -25
  115. package/dist/src/commands/re-export.js +13 -21
  116. package/dist/src/commands/rebase/abort.js +3 -3
  117. package/dist/src/commands/rebase/confirm.js +2 -1
  118. package/dist/src/commands/rebase/continue.d.ts +1 -1
  119. package/dist/src/commands/rebase/continue.js +14 -9
  120. package/dist/src/commands/rebase/index.js +11 -11
  121. package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
  122. package/dist/src/commands/rebase/patch-loop.js +60 -39
  123. package/dist/src/commands/rebase/summary.js +1 -1
  124. package/dist/src/commands/register.js +5 -7
  125. package/dist/src/commands/reset.js +2 -1
  126. package/dist/src/commands/resolve.d.ts +16 -13
  127. package/dist/src/commands/resolve.js +30 -33
  128. package/dist/src/commands/run.js +33 -35
  129. package/dist/src/commands/setup-support.js +15 -19
  130. package/dist/src/commands/setup.js +13 -9
  131. package/dist/src/commands/status-check.d.ts +6 -6
  132. package/dist/src/commands/status-json.d.ts +6 -6
  133. package/dist/src/commands/status-json.js +5 -0
  134. package/dist/src/commands/status-output.d.ts +5 -5
  135. package/dist/src/commands/status-output.js +6 -6
  136. package/dist/src/commands/status-ownership.d.ts +16 -14
  137. package/dist/src/commands/status-ownership.js +16 -14
  138. package/dist/src/commands/status.js +42 -34
  139. package/dist/src/commands/test-build-phase.js +26 -13
  140. package/dist/src/commands/test-diagnose.js +44 -44
  141. package/dist/src/commands/test-register.js +1 -0
  142. package/dist/src/commands/test-run.d.ts +14 -16
  143. package/dist/src/commands/test-run.js +21 -13
  144. package/dist/src/commands/test-stale-gate.d.ts +15 -16
  145. package/dist/src/commands/test-stale-gate.js +15 -16
  146. package/dist/src/commands/test-verdict.d.ts +20 -3
  147. package/dist/src/commands/test-verdict.js +30 -0
  148. package/dist/src/commands/test.js +83 -76
  149. package/dist/src/commands/token-coverage.js +3 -4
  150. package/dist/src/commands/token.js +16 -18
  151. package/dist/src/commands/tree.js +47 -31
  152. package/dist/src/commands/typecheck.d.ts +10 -12
  153. package/dist/src/commands/typecheck.js +24 -27
  154. package/dist/src/commands/verify.js +13 -15
  155. package/dist/src/commands/watch.js +21 -23
  156. package/dist/src/commands/wire.js +52 -62
  157. package/dist/src/core/ast-utils.d.ts +8 -7
  158. package/dist/src/core/ast-utils.js +8 -7
  159. package/dist/src/core/branding.d.ts +9 -14
  160. package/dist/src/core/branding.js +7 -11
  161. package/dist/src/core/browser-wire.d.ts +1 -1
  162. package/dist/src/core/browser-wire.js +14 -19
  163. package/dist/src/core/build-audit-platform.js +25 -33
  164. package/dist/src/core/build-audit-registration.js +17 -25
  165. package/dist/src/core/build-audit-resolve.d.ts +15 -17
  166. package/dist/src/core/build-audit-resolve.js +39 -36
  167. package/dist/src/core/build-audit-transforms.js +18 -19
  168. package/dist/src/core/build-audit.js +28 -42
  169. package/dist/src/core/build-baseline-types.d.ts +29 -35
  170. package/dist/src/core/build-baseline.d.ts +5 -4
  171. package/dist/src/core/build-baseline.js +14 -14
  172. package/dist/src/core/build-overwrite-guard.d.ts +7 -14
  173. package/dist/src/core/build-overwrite-guard.js +17 -27
  174. package/dist/src/core/build-prepare.d.ts +2 -2
  175. package/dist/src/core/build-prepare.js +8 -9
  176. package/dist/src/core/config-mutate.js +20 -19
  177. package/dist/src/core/config-state.js +12 -4
  178. package/dist/src/core/config-validate.js +8 -5
  179. package/dist/src/core/config.d.ts +5 -8
  180. package/dist/src/core/config.js +7 -16
  181. package/dist/src/core/coverage-extend.d.ts +15 -13
  182. package/dist/src/core/coverage-extend.js +15 -13
  183. package/dist/src/core/destructive.d.ts +31 -9
  184. package/dist/src/core/destructive.js +29 -26
  185. package/dist/src/core/diff-hunks.js +10 -12
  186. package/dist/src/core/discard-baseline.d.ts +8 -8
  187. package/dist/src/core/discard-baseline.js +16 -15
  188. package/dist/src/core/display-state.d.ts +12 -14
  189. package/dist/src/core/display-state.js +12 -14
  190. package/dist/src/core/engine-changes.d.ts +2 -4
  191. package/dist/src/core/engine-changes.js +2 -4
  192. package/dist/src/core/engine-precondition.d.ts +28 -13
  193. package/dist/src/core/engine-precondition.js +25 -10
  194. package/dist/src/core/engine-session-lock.d.ts +47 -21
  195. package/dist/src/core/engine-session-lock.js +168 -33
  196. package/dist/src/core/export-moved-code-shape.d.ts +10 -10
  197. package/dist/src/core/export-moved-code-shape.js +10 -10
  198. package/dist/src/core/file-lock.d.ts +62 -5
  199. package/dist/src/core/file-lock.js +196 -59
  200. package/dist/src/core/firefox-cache.js +1 -3
  201. package/dist/src/core/firefox-extract.js +10 -14
  202. package/dist/src/core/firefox.d.ts +7 -5
  203. package/dist/src/core/firefox.js +4 -5
  204. package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
  205. package/dist/src/core/furnace-apply-dry-run.js +3 -2
  206. package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
  207. package/dist/src/core/furnace-apply-ftl.js +16 -13
  208. package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
  209. package/dist/src/core/furnace-apply-helpers.js +30 -21
  210. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
  211. package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
  212. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
  213. package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
  214. package/dist/src/core/furnace-apply.d.ts +5 -8
  215. package/dist/src/core/furnace-apply.js +19 -24
  216. package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
  217. package/dist/src/core/furnace-checksum-utils.js +16 -0
  218. package/dist/src/core/furnace-config.d.ts +21 -30
  219. package/dist/src/core/furnace-config.js +32 -54
  220. package/dist/src/core/furnace-constants.d.ts +8 -0
  221. package/dist/src/core/furnace-constants.js +10 -2
  222. package/dist/src/core/furnace-css-fragments.d.ts +5 -5
  223. package/dist/src/core/furnace-css-fragments.js +5 -5
  224. package/dist/src/core/furnace-jsconfig.d.ts +11 -12
  225. package/dist/src/core/furnace-jsconfig.js +11 -12
  226. package/dist/src/core/furnace-marker.d.ts +7 -8
  227. package/dist/src/core/furnace-marker.js +7 -8
  228. package/dist/src/core/furnace-operation.d.ts +31 -0
  229. package/dist/src/core/furnace-operation.js +54 -17
  230. package/dist/src/core/furnace-precondition.d.ts +61 -0
  231. package/dist/src/core/furnace-precondition.js +67 -0
  232. package/dist/src/core/furnace-registration-remove.d.ts +5 -6
  233. package/dist/src/core/furnace-registration-remove.js +6 -7
  234. package/dist/src/core/furnace-registration-validate.d.ts +13 -0
  235. package/dist/src/core/furnace-registration-validate.js +20 -1
  236. package/dist/src/core/furnace-registration.d.ts +5 -6
  237. package/dist/src/core/furnace-registration.js +10 -12
  238. package/dist/src/core/furnace-rollback.js +7 -7
  239. package/dist/src/core/furnace-scanner.d.ts +1 -0
  240. package/dist/src/core/furnace-scanner.js +1 -2
  241. package/dist/src/core/furnace-stale-export.d.ts +10 -10
  242. package/dist/src/core/furnace-stale-export.js +10 -10
  243. package/dist/src/core/furnace-state-persist.d.ts +6 -8
  244. package/dist/src/core/furnace-state-persist.js +6 -8
  245. package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
  246. package/dist/src/core/furnace-validate-accessibility.js +310 -1
  247. package/dist/src/core/furnace-validate-compatibility.js +141 -1
  248. package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
  249. package/dist/src/core/furnace-validate-helpers.js +0 -491
  250. package/dist/src/core/furnace-validate-registration.d.ts +7 -0
  251. package/dist/src/core/furnace-validate-registration.js +115 -41
  252. package/dist/src/core/furnace-validate.d.ts +16 -0
  253. package/dist/src/core/furnace-validate.js +188 -145
  254. package/dist/src/core/furnace-version-drift.d.ts +10 -11
  255. package/dist/src/core/furnace-version-drift.js +10 -11
  256. package/dist/src/core/git-base.d.ts +21 -9
  257. package/dist/src/core/git-base.js +24 -9
  258. package/dist/src/core/git-diff.d.ts +20 -9
  259. package/dist/src/core/git-diff.js +148 -63
  260. package/dist/src/core/git-file-ops.d.ts +19 -19
  261. package/dist/src/core/git-file-ops.js +19 -19
  262. package/dist/src/core/git-readonly-index.d.ts +53 -19
  263. package/dist/src/core/git-readonly-index.js +75 -31
  264. package/dist/src/core/git-status.d.ts +5 -6
  265. package/dist/src/core/git-status.js +5 -6
  266. package/dist/src/core/git.d.ts +9 -10
  267. package/dist/src/core/git.js +57 -71
  268. package/dist/src/core/license-headers.d.ts +17 -23
  269. package/dist/src/core/license-headers.js +17 -23
  270. package/dist/src/core/mach-build-artifacts.d.ts +9 -9
  271. package/dist/src/core/mach-build-artifacts.js +57 -19
  272. package/dist/src/core/mach-error-hints.js +42 -29
  273. package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
  274. package/dist/src/core/mach-known-noise-filter.js +18 -20
  275. package/dist/src/core/mach-mozconfig.d.ts +14 -0
  276. package/dist/src/core/mach-mozconfig.js +30 -0
  277. package/dist/src/core/mach-resource-shim.d.ts +43 -58
  278. package/dist/src/core/mach-resource-shim.js +50 -67
  279. package/dist/src/core/mach.d.ts +37 -41
  280. package/dist/src/core/mach.js +50 -51
  281. package/dist/src/core/marionette-port.d.ts +36 -1
  282. package/dist/src/core/marionette-port.js +96 -31
  283. package/dist/src/core/marionette-preflight.d.ts +8 -12
  284. package/dist/src/core/marionette-preflight.js +86 -52
  285. package/dist/src/core/mochitest-server-port.d.ts +42 -0
  286. package/dist/src/core/mochitest-server-port.js +119 -0
  287. package/dist/src/core/module-resolution-preflight.d.ts +9 -11
  288. package/dist/src/core/module-resolution-preflight.js +9 -11
  289. package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
  290. package/dist/src/core/moz-manifest-helpers.js +163 -0
  291. package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
  292. package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
  293. package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
  294. package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
  295. package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
  296. package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
  297. package/dist/src/core/ownership-table.d.ts +14 -16
  298. package/dist/src/core/ownership-table.js +14 -16
  299. package/dist/src/core/parser-fallback.d.ts +15 -1
  300. package/dist/src/core/parser-fallback.js +22 -3
  301. package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
  302. package/dist/src/core/patch-apply-fuzz.js +15 -18
  303. package/dist/src/core/patch-apply.d.ts +13 -34
  304. package/dist/src/core/patch-apply.js +16 -76
  305. package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
  306. package/dist/src/core/patch-artifact-normalize.js +13 -15
  307. package/dist/src/core/patch-export-metadata.d.ts +12 -2
  308. package/dist/src/core/patch-export-metadata.js +13 -4
  309. package/dist/src/core/patch-export-update.d.ts +13 -1
  310. package/dist/src/core/patch-export-update.js +25 -8
  311. package/dist/src/core/patch-export.d.ts +15 -8
  312. package/dist/src/core/patch-export.js +10 -11
  313. package/dist/src/core/patch-files.d.ts +5 -6
  314. package/dist/src/core/patch-files.js +5 -6
  315. package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
  316. package/dist/src/core/patch-identifier-suggest.js +14 -19
  317. package/dist/src/core/patch-lint-binary.d.ts +51 -0
  318. package/dist/src/core/patch-lint-binary.js +39 -0
  319. package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
  320. package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
  321. package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
  322. package/dist/src/core/patch-lint-checkjs.js +41 -20
  323. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
  324. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
  325. package/dist/src/core/patch-lint-creators.d.ts +8 -8
  326. package/dist/src/core/patch-lint-creators.js +8 -8
  327. package/dist/src/core/patch-lint-cross.d.ts +17 -20
  328. package/dist/src/core/patch-lint-cross.js +16 -16
  329. package/dist/src/core/patch-lint-css.d.ts +3 -5
  330. package/dist/src/core/patch-lint-css.js +17 -22
  331. package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
  332. package/dist/src/core/patch-lint-diff-tag.js +14 -16
  333. package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
  334. package/dist/src/core/patch-lint-jsdoc.js +26 -19
  335. package/dist/src/core/patch-lint-mozbuild.js +20 -20
  336. package/dist/src/core/patch-lint-observer.d.ts +7 -7
  337. package/dist/src/core/patch-lint-observer.js +7 -7
  338. package/dist/src/core/patch-lint.d.ts +38 -29
  339. package/dist/src/core/patch-lint.js +89 -121
  340. package/dist/src/core/patch-lock.d.ts +1 -1
  341. package/dist/src/core/patch-lock.js +45 -9
  342. package/dist/src/core/patch-manifest-consistency.d.ts +57 -6
  343. package/dist/src/core/patch-manifest-consistency.js +111 -40
  344. package/dist/src/core/patch-manifest-files-affected.d.ts +43 -0
  345. package/dist/src/core/patch-manifest-files-affected.js +77 -0
  346. package/dist/src/core/patch-manifest-io.js +14 -18
  347. package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
  348. package/dist/src/core/patch-manifest-resolve.js +9 -12
  349. package/dist/src/core/patch-manifest-validate.d.ts +6 -1
  350. package/dist/src/core/patch-manifest-validate.js +25 -13
  351. package/dist/src/core/patch-manifest.d.ts +2 -1
  352. package/dist/src/core/patch-manifest.js +2 -1
  353. package/dist/src/core/patch-parse.d.ts +24 -13
  354. package/dist/src/core/patch-parse.js +19 -14
  355. package/dist/src/core/patch-policy.d.ts +2 -2
  356. package/dist/src/core/patch-policy.js +2 -2
  357. package/dist/src/core/patch-registration-refs.d.ts +7 -8
  358. package/dist/src/core/patch-registration-refs.js +9 -9
  359. package/dist/src/core/process-cpu-time.d.ts +12 -0
  360. package/dist/src/core/process-cpu-time.js +52 -0
  361. package/dist/src/core/rebase-session.d.ts +38 -26
  362. package/dist/src/core/rebase-session.js +55 -28
  363. package/dist/src/core/register-browser-content.js +9 -74
  364. package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
  365. package/dist/src/core/register-module.js +10 -43
  366. package/dist/src/core/register-scaffold.d.ts +8 -8
  367. package/dist/src/core/register-scaffold.js +10 -10
  368. package/dist/src/core/register-shared-css.d.ts +9 -12
  369. package/dist/src/core/register-shared-css.js +17 -87
  370. package/dist/src/core/register-xpcshell-test.d.ts +6 -7
  371. package/dist/src/core/register-xpcshell-test.js +7 -8
  372. package/dist/src/core/shared-ftl.js +12 -4
  373. package/dist/src/core/state-file.d.ts +45 -0
  374. package/dist/src/core/state-file.js +34 -0
  375. package/dist/src/core/status-binary.d.ts +18 -11
  376. package/dist/src/core/status-binary.js +19 -12
  377. package/dist/src/core/status-classify.d.ts +30 -36
  378. package/dist/src/core/status-classify.js +21 -26
  379. package/dist/src/core/test-changed-prefs.d.ts +30 -0
  380. package/dist/src/core/test-changed-prefs.js +78 -0
  381. package/dist/src/core/test-harness-crash.d.ts +25 -63
  382. package/dist/src/core/test-harness-crash.js +121 -142
  383. package/dist/src/core/test-harness-signature.d.ts +18 -0
  384. package/dist/src/core/test-harness-signature.js +13 -0
  385. package/dist/src/core/test-path-scope.d.ts +12 -15
  386. package/dist/src/core/test-path-scope.js +15 -18
  387. package/dist/src/core/test-stale-check.d.ts +17 -17
  388. package/dist/src/core/test-stale-check.js +50 -43
  389. package/dist/src/core/test-stall-triage.d.ts +51 -0
  390. package/dist/src/core/test-stall-triage.js +126 -0
  391. package/dist/src/core/token-category.js +66 -14
  392. package/dist/src/core/token-coverage.js +9 -9
  393. package/dist/src/core/token-dark-mode.d.ts +9 -11
  394. package/dist/src/core/token-dark-mode.js +29 -13
  395. package/dist/src/core/token-docs.d.ts +2 -0
  396. package/dist/src/core/token-docs.js +3 -1
  397. package/dist/src/core/token-manager.d.ts +10 -2
  398. package/dist/src/core/token-manager.js +17 -8
  399. package/dist/src/core/token-scaffold.d.ts +7 -7
  400. package/dist/src/core/token-scaffold.js +7 -7
  401. package/dist/src/core/token-variant.d.ts +4 -4
  402. package/dist/src/core/token-variant.js +28 -6
  403. package/dist/src/core/toolchain-preflight.d.ts +20 -21
  404. package/dist/src/core/toolchain-preflight.js +28 -31
  405. package/dist/src/core/tree-guard.js +12 -2
  406. package/dist/src/core/tree-store.d.ts +41 -27
  407. package/dist/src/core/tree-store.js +68 -43
  408. package/dist/src/core/typecheck-shim.d.ts +23 -24
  409. package/dist/src/core/typecheck-shim.js +23 -24
  410. package/dist/src/core/typecheck.js +15 -9
  411. package/dist/src/core/wire-destroy.d.ts +3 -3
  412. package/dist/src/core/wire-destroy.js +7 -15
  413. package/dist/src/core/wire-dom-fragment.d.ts +9 -17
  414. package/dist/src/core/wire-dom-fragment.js +11 -44
  415. package/dist/src/core/wire-init.d.ts +2 -3
  416. package/dist/src/core/wire-init.js +8 -19
  417. package/dist/src/core/wire-subscript.d.ts +2 -3
  418. package/dist/src/core/wire-subscript.js +4 -13
  419. package/dist/src/core/wire-utils.d.ts +7 -8
  420. package/dist/src/core/wire-utils.js +7 -8
  421. package/dist/src/core/xpcshell-appdir.d.ts +38 -45
  422. package/dist/src/core/xpcshell-appdir.js +38 -45
  423. package/dist/src/errors/base.d.ts +37 -16
  424. package/dist/src/errors/base.js +37 -16
  425. package/dist/src/errors/build.d.ts +13 -1
  426. package/dist/src/errors/build.js +14 -2
  427. package/dist/src/errors/codes.d.ts +16 -0
  428. package/dist/src/errors/codes.js +16 -0
  429. package/dist/src/errors/furnace.d.ts +4 -4
  430. package/dist/src/errors/git.d.ts +6 -10
  431. package/dist/src/errors/git.js +6 -10
  432. package/dist/src/errors/patch.d.ts +7 -7
  433. package/dist/src/errors/patch.js +7 -7
  434. package/dist/src/errors/rebase.d.ts +4 -4
  435. package/dist/src/errors/rebase.js +4 -4
  436. package/dist/src/index.d.ts +7 -1
  437. package/dist/src/index.js +5 -0
  438. package/dist/src/types/commands/index.d.ts +2 -2
  439. package/dist/src/types/commands/options.d.ts +111 -70
  440. package/dist/src/types/commands/patches.d.ts +24 -34
  441. package/dist/src/types/commands/project.d.ts +5 -34
  442. package/dist/src/types/config.d.ts +25 -23
  443. package/dist/src/types/config.js +6 -1
  444. package/dist/src/types/furnace.d.ts +37 -13
  445. package/dist/src/types/furnace.js +19 -1
  446. package/dist/src/types/index.d.ts +3 -3
  447. package/dist/src/utils/build-info.d.ts +7 -8
  448. package/dist/src/utils/build-info.js +12 -11
  449. package/dist/src/utils/errors.d.ts +6 -8
  450. package/dist/src/utils/errors.js +6 -8
  451. package/dist/src/utils/logger.d.ts +27 -10
  452. package/dist/src/utils/logger.js +28 -16
  453. package/dist/src/utils/machine-output.d.ts +12 -0
  454. package/dist/src/utils/machine-output.js +33 -0
  455. package/dist/src/utils/options.d.ts +31 -14
  456. package/dist/src/utils/options.js +86 -30
  457. package/dist/src/utils/paths.d.ts +10 -8
  458. package/dist/src/utils/paths.js +10 -8
  459. package/dist/src/utils/platform.d.ts +0 -25
  460. package/dist/src/utils/platform.js +1 -42
  461. package/dist/src/utils/process-group.d.ts +9 -4
  462. package/dist/src/utils/process-group.js +19 -5
  463. package/dist/src/utils/process.d.ts +42 -7
  464. package/dist/src/utils/process.js +44 -17
  465. package/dist/src/utils/ps-duration.d.ts +31 -0
  466. package/dist/src/utils/ps-duration.js +70 -0
  467. package/dist/src/utils/regex.d.ts +9 -10
  468. package/dist/src/utils/regex.js +9 -10
  469. package/dist/src/utils/validation.d.ts +58 -28
  470. package/dist/src/utils/validation.js +60 -33
  471. package/package.json +3 -3
  472. package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
  473. package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
  474. package/dist/src/core/manifest-helpers.d.ts +0 -38
  475. package/dist/src/core/manifest-helpers.js +0 -120
  476. /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
  477. /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
  478. /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
@@ -3,26 +3,26 @@ import type { ExportOptions, PatchCategory } from '../types/commands/index.js';
3
3
  import type { FireForgeConfig } from '../types/config.js';
4
4
  import type { SpinnerHandle } from '../utils/logger.js';
5
5
  /**
6
- * Runs the full patch lint pipeline and reports results.
7
- * Warnings are always displayed. Errors block the export unless skipLint is true.
6
+ * Runs the full patch lint pipeline and reports results. Warnings are always
7
+ * displayed. Errors block the export unless skipLint is true.
8
8
  *
9
9
  * @param engineDir - Engine root directory
10
10
  * @param filesAffected - Files touched by the patch
11
11
  * @param diffContent - Raw unified diff string
12
12
  * @param config - Project configuration
13
13
  * @param skipLint - If true, downgrade errors to warnings
14
- * @param patchQueueCtx - Optional cross-patch context for ownership resolution
14
+ * @param patchQueueCtx - Optional cross-patch context for ownership
15
+ * resolution
15
16
  * @param ignoreChecks - Optional per-patch set of `check` IDs to suppress
16
17
  * (threaded from `PatchMetadata.lintIgnore`). Surgical alternative to
17
18
  * `--skip-lint` when exactly one advisory rule does not apply to a
18
- * specific patch — e.g. `large-patch-lines` on a cohesive branding
19
- * bundle that genuinely cannot be split.
19
+ * specific patch — e.g. `large-patch-lines` on a cohesive branding bundle
20
+ * that genuinely cannot be split.
20
21
  * @param patchTier - Optional explicit tier override (threaded from
21
- * `PatchMetadata.tier`). Forces the branding-tier thresholds when
22
- * set, independent of the auto-detect allowlist. When the branding
23
- * tier is applied (either via this opt-in or the auto-detect), a
24
- * single `info()` line surfaces the choice so the tier decision is
25
- * visible rather than silent.
22
+ * `PatchMetadata.tier`). Forces the branding-tier thresholds when set,
23
+ * independent of the auto-detect allowlist. When the branding tier is
24
+ * applied, a single `info()` line surfaces the choice so the tier decision
25
+ * is visible rather than silent.
26
26
  */
27
27
  export declare function runPatchLint(engineDir: string, filesAffected: string[], diffContent: string, config: FireForgeConfig, skipLint?: boolean, patchQueueCtx?: import('../core/patch-lint-cross.js').PatchQueueContext, ignoreChecks?: ReadonlySet<string>, patchTier?: 'branding'): Promise<void>;
28
28
  /**
@@ -55,18 +55,18 @@ export declare function confirmSupersedePatches(patchesDir: string, filesAffecte
55
55
  /**
56
56
  * Detects new files missing license headers and offers to add them.
57
57
  *
58
- * In interactive mode the user is prompted before any files are modified.
59
- * In non-interactive mode the function is a no-op — the existing lint error
60
- * will block the export instead.
58
+ * In interactive mode the user is prompted before any files are modified. In
59
+ * non-interactive mode the function is a no-op — the existing lint error
60
+ * blocks the export instead.
61
61
  *
62
62
  * @param engineDir - Absolute path to engine directory
63
63
  * @param diffContent - Current unified diff
64
64
  * @param config - Project configuration
65
65
  * @param isInteractive - Whether interactive prompts are available
66
66
  * @param dryRun - When true, only REPORT missing headers, never prompt or
67
- * write. Dry-run must stay read-only: before this flag existed, an
68
- * interactive `export --dry-run` prompted (default Yes) and wrote license
69
- * headers into engine/ files, then closed with "no changes made".
67
+ * write. Dry-run must stay read-only: without this flag an interactive
68
+ * `export --dry-run` prompts (default Yes), writes license headers into
69
+ * engine/ files, and then closes with "no changes made".
70
70
  * @returns true if files were modified on disk (caller must regenerate diff)
71
71
  */
72
72
  export declare function autoFixLicenseHeaders(engineDir: string, diffContent: string, config: FireForgeConfig, isInteractive: boolean, dryRun?: boolean): Promise<boolean>;
@@ -83,19 +83,17 @@ export declare function autoFixLicenseHeaders(engineDir: string, diffContent: st
83
83
  export declare function findPartialOwnershipOverlap(manifest: PatchesManifest, filesAffected: string[], excludeFilenames: ReadonlySet<string>): Map<string, string[]>;
84
84
  /**
85
85
  * Gate that refuses the default export path when the new patch would
86
- * silently claim files that are already tracked by other non-superseded
87
- * patches. `findAllPatchesForFiles` already catches the full-coverage
88
- * supersede case — this helper fills the gap for partial overlap, which
89
- * was the eval finding #12 scenario (two patches both claiming
90
- * `browser/themes/shared/jar.inc.mn` after a second export with
91
- * `--before`).
86
+ * silently claim files already tracked by other non-superseded patches.
87
+ * `findAllPatchesForFiles` catches the full-coverage supersede case; this
88
+ * fills the gap for PARTIAL overlap — two patches both claiming e.g.
89
+ * `browser/themes/shared/jar.inc.mn` after a second export with `--before`.
92
90
  *
93
91
  * Proceeds silently when there is no overlap, or when the caller passed
94
92
  * `--allow-overlap`. In interactive mode the caller is prompted to
95
93
  * acknowledge the overlap (the proper fix path is `re-export --files` to
96
94
  * repartition ownership, so the prompt surfaces that pointer). In
97
- * non-interactive mode the function throws — better to fail fast than
98
- * let the queue fall out of sync with verify.
95
+ * non-interactive mode it throws — better to fail fast than let the queue
96
+ * fall out of sync with verify.
99
97
  */
100
98
  export declare function guardOwnershipOverlap(args: {
101
99
  patchesDir: string;
@@ -107,13 +105,12 @@ export declare function guardOwnershipOverlap(args: {
107
105
  }): Promise<boolean>;
108
106
  /**
109
107
  * Runs the two pre-commit gates shared by `export` and `export-all` in
110
- * order: the supersede confirmation, then the cross-patch ownership
111
- * overlap guard. The overlap guard receives the filenames of the patches
112
- * the export would fully supersede so it does not flag a file claimed by
113
- * a patch that is about to be removed (pre-0.16.0 `export` only caught
114
- * FULL-coverage supersedes, so a second export targeting a shared file
115
- * like `browser/themes/shared/jar.inc.mn` created a queue where two
116
- * patches both claimed the file and `verify` failed immediately).
108
+ * order: the supersede confirmation, then the cross-patch ownership overlap
109
+ * guard. The overlap guard receives the filenames of the patches the export
110
+ * would fully supersede so it does not flag a file claimed by a patch that
111
+ * is about to be removed. Catching only FULL-coverage supersedes leaves a
112
+ * second export targeting a shared file creating a queue where two patches
113
+ * both claim it and `verify` fails immediately.
117
114
  *
118
115
  * @param args - Gate inputs shared by both export commands
119
116
  * @param args.patchesDir - Absolute path of the patches directory
@@ -122,8 +119,8 @@ export declare function guardOwnershipOverlap(args: {
122
119
  * @param args.allowOverlap - The command's `--allow-overlap` flag
123
120
  * @param args.isInteractive - Whether prompting the operator is possible
124
121
  * @param args.s - Active spinner, stopped before any prompt
125
- * @returns `true` when both gates passed; `false` when the operator
126
- * declined (the caller returns without committing)
122
+ * @returns `true` when both gates passed; `false` when the operator declined
123
+ * (the caller returns without committing)
127
124
  */
128
125
  export declare function runSupersedeAndOverlapGates(args: {
129
126
  patchesDir: string;
@@ -10,36 +10,36 @@ import { getPatchPolicyCategories, isCategoryAllowedByConfig } from '../core/pat
10
10
  import { GeneralError, InvalidArgumentError } from '../errors/base.js';
11
11
  import { pathExists, readText } from '../utils/fs.js';
12
12
  import { cancel, info, isCancel, warn } from '../utils/logger.js';
13
- import { normalizePatchDisplayName, PATCH_CATEGORIES, validatePatchName, } from '../utils/validation.js';
13
+ import { describePatchNameProblem, normalizePatchDisplayName, PATCH_CATEGORIES, } from '../utils/validation.js';
14
14
  /**
15
- * Runs the full patch lint pipeline and reports results.
16
- * Warnings are always displayed. Errors block the export unless skipLint is true.
15
+ * Runs the full patch lint pipeline and reports results. Warnings are always
16
+ * displayed. Errors block the export unless skipLint is true.
17
17
  *
18
18
  * @param engineDir - Engine root directory
19
19
  * @param filesAffected - Files touched by the patch
20
20
  * @param diffContent - Raw unified diff string
21
21
  * @param config - Project configuration
22
22
  * @param skipLint - If true, downgrade errors to warnings
23
- * @param patchQueueCtx - Optional cross-patch context for ownership resolution
23
+ * @param patchQueueCtx - Optional cross-patch context for ownership
24
+ * resolution
24
25
  * @param ignoreChecks - Optional per-patch set of `check` IDs to suppress
25
26
  * (threaded from `PatchMetadata.lintIgnore`). Surgical alternative to
26
27
  * `--skip-lint` when exactly one advisory rule does not apply to a
27
- * specific patch — e.g. `large-patch-lines` on a cohesive branding
28
- * bundle that genuinely cannot be split.
28
+ * specific patch — e.g. `large-patch-lines` on a cohesive branding bundle
29
+ * that genuinely cannot be split.
29
30
  * @param patchTier - Optional explicit tier override (threaded from
30
- * `PatchMetadata.tier`). Forces the branding-tier thresholds when
31
- * set, independent of the auto-detect allowlist. When the branding
32
- * tier is applied (either via this opt-in or the auto-detect), a
33
- * single `info()` line surfaces the choice so the tier decision is
34
- * visible rather than silent.
31
+ * `PatchMetadata.tier`). Forces the branding-tier thresholds when set,
32
+ * independent of the auto-detect allowlist. When the branding tier is
33
+ * applied, a single `info()` line surfaces the choice so the tier decision
34
+ * is visible rather than silent.
35
35
  */
36
36
  export async function runPatchLint(engineDir, filesAffected, diffContent, config, skipLint, patchQueueCtx, ignoreChecks, patchTier) {
37
- // Compute the tier decision independently of the lint pipeline so the
38
- // decision can be surfaced even when the rule body emitted no issues
39
- // (e.g. a branding patch under the soft threshold still benefits from
40
- // operators knowing which tier governed the run). The same helper is
41
- // reused inside `lintPatchSize`, so the surfaced tier and the tier
42
- // that actually drove the thresholds never drift.
37
+ // Compute the tier decision independently of the lint pipeline so it can
38
+ // be surfaced even when the rule body emitted no issues — a branding patch
39
+ // under the soft threshold still benefits from operators knowing which
40
+ // tier governed the run. The same helper is reused inside `lintPatchSize`,
41
+ // so the surfaced tier and the tier that actually drove the thresholds
42
+ // never drift.
43
43
  const tierDecision = resolvePatchSizeTier(filesAffected, patchTier);
44
44
  if (tierDecision.tier === 'branding') {
45
45
  info(tierDecision.source === 'explicit'
@@ -54,7 +54,12 @@ export async function runPatchLint(engineDir, filesAffected, diffContent, config
54
54
  const checkJsReportScope = patchQueueCtx
55
55
  ? resolvePatchOwnedSysMjs(detectNewFilesInDiff(diffContent))
56
56
  : undefined;
57
- const issues = await lintExportedPatch(engineDir, filesAffected, diffContent, config, patchQueueCtx, ignoreChecks, patchTier, checkJsReportScope ? { checkJsReportScope } : undefined);
57
+ const issues = await lintExportedPatch(engineDir, filesAffected, diffContent, config, {
58
+ ...(patchQueueCtx ? { patchQueueCtx } : {}),
59
+ ...(ignoreChecks ? { ignoreChecks } : {}),
60
+ ...(patchTier ? { patchTier } : {}),
61
+ ...(checkJsReportScope ? { checkJsReportScope } : {}),
62
+ });
58
63
  reportPatchLintOutcome(issues, skipLint);
59
64
  }
60
65
  /**
@@ -115,7 +120,7 @@ export async function promptExportPatchMetadata(options, isInteractive, commandN
115
120
  // both name shapes.
116
121
  let patchName = options.name?.trim().replace(/\.patch$/i, '');
117
122
  if (patchName) {
118
- const validationError = validatePatchName(patchName);
123
+ const validationError = describePatchNameProblem(patchName);
119
124
  if (validationError) {
120
125
  throw new InvalidArgumentError(validationError, '--name');
121
126
  }
@@ -127,7 +132,7 @@ export async function promptExportPatchMetadata(options, isInteractive, commandN
127
132
  const nameResult = await text({
128
133
  message: 'Enter a name for this patch:',
129
134
  placeholder: commandName === 'export' ? 'my-change' : 'my-changes',
130
- validate: (value) => validatePatchName((value ?? '').trim()),
135
+ validate: (value) => describePatchNameProblem((value ?? '').trim()),
131
136
  });
132
137
  if (isCancel(nameResult)) {
133
138
  cancel('Export cancelled');
@@ -217,18 +222,18 @@ export async function confirmSupersedePatches(patchesDir, filesAffected, superse
217
222
  /**
218
223
  * Detects new files missing license headers and offers to add them.
219
224
  *
220
- * In interactive mode the user is prompted before any files are modified.
221
- * In non-interactive mode the function is a no-op — the existing lint error
222
- * will block the export instead.
225
+ * In interactive mode the user is prompted before any files are modified. In
226
+ * non-interactive mode the function is a no-op — the existing lint error
227
+ * blocks the export instead.
223
228
  *
224
229
  * @param engineDir - Absolute path to engine directory
225
230
  * @param diffContent - Current unified diff
226
231
  * @param config - Project configuration
227
232
  * @param isInteractive - Whether interactive prompts are available
228
233
  * @param dryRun - When true, only REPORT missing headers, never prompt or
229
- * write. Dry-run must stay read-only: before this flag existed, an
230
- * interactive `export --dry-run` prompted (default Yes) and wrote license
231
- * headers into engine/ files, then closed with "no changes made".
234
+ * write. Dry-run must stay read-only: without this flag an interactive
235
+ * `export --dry-run` prompts (default Yes), writes license headers into
236
+ * engine/ files, and then closes with "no changes made".
232
237
  * @returns true if files were modified on disk (caller must regenerate diff)
233
238
  */
234
239
  export async function autoFixLicenseHeaders(engineDir, diffContent, config, isInteractive, dryRun = false) {
@@ -324,19 +329,17 @@ export function findPartialOwnershipOverlap(manifest, filesAffected, excludeFile
324
329
  }
325
330
  /**
326
331
  * Gate that refuses the default export path when the new patch would
327
- * silently claim files that are already tracked by other non-superseded
328
- * patches. `findAllPatchesForFiles` already catches the full-coverage
329
- * supersede case — this helper fills the gap for partial overlap, which
330
- * was the eval finding #12 scenario (two patches both claiming
331
- * `browser/themes/shared/jar.inc.mn` after a second export with
332
- * `--before`).
332
+ * silently claim files already tracked by other non-superseded patches.
333
+ * `findAllPatchesForFiles` catches the full-coverage supersede case; this
334
+ * fills the gap for PARTIAL overlap — two patches both claiming e.g.
335
+ * `browser/themes/shared/jar.inc.mn` after a second export with `--before`.
333
336
  *
334
337
  * Proceeds silently when there is no overlap, or when the caller passed
335
338
  * `--allow-overlap`. In interactive mode the caller is prompted to
336
339
  * acknowledge the overlap (the proper fix path is `re-export --files` to
337
340
  * repartition ownership, so the prompt surfaces that pointer). In
338
- * non-interactive mode the function throws — better to fail fast than
339
- * let the queue fall out of sync with verify.
341
+ * non-interactive mode it throws — better to fail fast than let the queue
342
+ * fall out of sync with verify.
340
343
  */
341
344
  export async function guardOwnershipOverlap(args) {
342
345
  const { patchesDir, filesAffected, supersedingFilenames, allowOverlap, isInteractive, s } = args;
@@ -373,13 +376,12 @@ export async function guardOwnershipOverlap(args) {
373
376
  }
374
377
  /**
375
378
  * Runs the two pre-commit gates shared by `export` and `export-all` in
376
- * order: the supersede confirmation, then the cross-patch ownership
377
- * overlap guard. The overlap guard receives the filenames of the patches
378
- * the export would fully supersede so it does not flag a file claimed by
379
- * a patch that is about to be removed (pre-0.16.0 `export` only caught
380
- * FULL-coverage supersedes, so a second export targeting a shared file
381
- * like `browser/themes/shared/jar.inc.mn` created a queue where two
382
- * patches both claimed the file and `verify` failed immediately).
379
+ * order: the supersede confirmation, then the cross-patch ownership overlap
380
+ * guard. The overlap guard receives the filenames of the patches the export
381
+ * would fully supersede so it does not flag a file claimed by a patch that
382
+ * is about to be removed. Catching only FULL-coverage supersedes leaves a
383
+ * second export targeting a shared file creating a queue where two patches
384
+ * both claim it and `verify` fails immediately.
383
385
  *
384
386
  * @param args - Gate inputs shared by both export commands
385
387
  * @param args.patchesDir - Absolute path of the patches directory
@@ -388,8 +390,8 @@ export async function guardOwnershipOverlap(args) {
388
390
  * @param args.allowOverlap - The command's `--allow-overlap` flag
389
391
  * @param args.isInteractive - Whether prompting the operator is possible
390
392
  * @param args.s - Active spinner, stopped before any prompt
391
- * @returns `true` when both gates passed; `false` when the operator
392
- * declined (the caller returns without committing)
393
+ * @returns `true` when both gates passed; `false` when the operator declined
394
+ * (the caller returns without committing)
393
395
  */
394
396
  export async function runSupersedeAndOverlapGates(args) {
395
397
  const { patchesDir, filesAffected, supersede, allowOverlap, isInteractive, s } = args;
@@ -3,11 +3,12 @@ import { stat } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { Command, Option } from 'commander';
5
5
  import { getProjectPaths, loadConfig } from '../core/config.js';
6
- import { appendHistory } from '../core/destructive.js';
6
+ import { appendHistory, stdioIsInteractive } from '../core/destructive.js';
7
+ import { assertEngineGitReady } from '../core/engine-precondition.js';
7
8
  import { withEngineSessionLock } from '../core/engine-session-lock.js';
8
9
  import { collectFurnaceManagedPrefixes } from '../core/furnace-config.js';
9
10
  import { enforceFreshFurnaceSources } from '../core/furnace-stale-export.js';
10
- import { getStatusWithCodes, isGitRepository } from '../core/git.js';
11
+ import { getStatusWithCodes } from '../core/git.js';
11
12
  import { generateBinaryFilePatch, generateFullFilePatch } from '../core/git-diff.js';
12
13
  import { isBinaryFile } from '../core/git-file-ops.js';
13
14
  import { getModifiedFilesInDir, getUntrackedFiles, getUntrackedFilesInDir, } from '../core/git-status.js';
@@ -33,11 +34,10 @@ async function collectExportFiles(paths, files) {
33
34
  let untrackedFiles;
34
35
  // Accept both repo-root-relative (`engine/browser/...`) and engine-relative
35
36
  // (`browser/...`) paths for every input, matching `register`/`test`/`lint`.
36
- // Previously, an `engine/`-prefixed path fell through to
37
- // `File "engine/..." has no changes to export.` because the status lookup
38
- // sees paths relative to `paths.engine` and the explicit prefix double-
39
- // rooted the candidate. `stripEnginePrefix` makes that user-facing form
40
- // a no-op for the lookup pipeline.
37
+ // Without the strip, an `engine/`-prefixed path falls through to
38
+ // `File "engine/..." has no changes to export.`: the status lookup sees
39
+ // paths relative to `paths.engine`, so the explicit prefix double-roots the
40
+ // candidate.
41
41
  for (const rawInputPath of files) {
42
42
  const inputPath = stripEnginePrefix(rawInputPath);
43
43
  const fullInputPath = join(paths.engine, inputPath);
@@ -88,13 +88,14 @@ async function collectExportFiles(paths, files) {
88
88
  return { files: [...collectedFiles].sort(), fromDirectory };
89
89
  }
90
90
  /**
91
- * Auto-excludes directory-derived files already owned by OTHER patches
92
- * (0.34.0 field report): a directory export used to plan files owned by
93
- * earlier patches into the new patch, and the duplicate-new-file-creation
94
- * refusal surfaced only at placement lint, suggesting --force-unsafe —
95
- * the wrong tool for "leave that file with its owner". Explicitly named
96
- * files are never excluded (the overlap gates still confront the operator
97
- * with those). Prints one notice per exclusion.
91
+ * Auto-excludes directory-derived files already owned by OTHER patches.
92
+ *
93
+ * A directory export otherwise plans files owned by earlier patches into the
94
+ * new patch, and the duplicate-new-file-creation refusal surfaces only at
95
+ * placement lint, suggesting `--force-unsafe` — the wrong tool for "leave
96
+ * that file with its owner". Explicitly named files are never excluded (the
97
+ * overlap gates still confront the operator with those). Prints one notice
98
+ * per exclusion.
98
99
  */
99
100
  async function excludeFilesOwnedByOtherPatches(patchesDir, collected) {
100
101
  if (collected.fromDirectory.size === 0)
@@ -169,14 +170,7 @@ async function prepareExport(projectRoot, files, options) {
169
170
  throw new InvalidArgumentError('--order, --before, and --after are mutually exclusive.', 'export placement');
170
171
  }
171
172
  const paths = getProjectPaths(projectRoot);
172
- // Check if engine exists
173
- if (!(await pathExists(paths.engine))) {
174
- throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
175
- }
176
- // Check if it's a git repository
177
- if (!(await isGitRepository(paths.engine))) {
178
- throw new GeneralError('Engine directory is not a git repository. Run "fireforge download" to initialize.');
179
- }
173
+ await assertEngineGitReady(paths.engine);
180
174
  const collected = await collectExportFiles(paths, files);
181
175
  let allFiles = await excludeFilesOwnedByOtherPatches(paths.patches, collected);
182
176
  const ownershipExclusions = collected.files.length - allFiles.length;
@@ -202,10 +196,10 @@ async function prepareExport(projectRoot, files, options) {
202
196
  throw new GeneralError(`Paths "${pathList}" have no changes to export.\n\n` +
203
197
  'Run "fireforge status" to see modified files.');
204
198
  }
205
- // Stale-furnace-source gate (0.37.0 item 4): the export captures deployed
206
- // engine copies; refuse (or warn under --allow-stale-furnace) when a
207
- // covered component's source changed since the last furnace apply. Runs
208
- // after --exclude-furnace so excluded furnace files never trigger it.
199
+ // Stale-furnace-source gate: the export captures deployed engine copies,
200
+ // so refuse (or warn under --allow-stale-furnace) when a covered
201
+ // component's source changed since the last furnace apply. Runs after
202
+ // --exclude-furnace so excluded furnace files never trigger it.
209
203
  await enforceFreshFurnaceSources(projectRoot, allFiles, options.allowStaleFurnace === true, 'export');
210
204
  let diff = await generatePatchDiff(paths.engine, allFiles);
211
205
  if (!diff.trim()) {
@@ -222,7 +216,7 @@ async function prepareExport(projectRoot, files, options) {
222
216
  await ensureDir(paths.patches);
223
217
  }
224
218
  const config = await loadConfig(projectRoot);
225
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
219
+ const isInteractive = stdioIsInteractive();
226
220
  // Auto-fix missing license headers on new files (interactive only;
227
221
  // report-only under --dry-run so the preview never mutates engine/)
228
222
  const headersAdded = await autoFixLicenseHeaders(paths.engine, diff, config, isInteractive, options.dryRun ?? false);
@@ -1,11 +1,12 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { watch as fsWatch } from 'node:fs';
3
- import { getProjectPaths, loadConfig } from '../../core/config.js';
3
+ import { loadConfig } from '../../core/config.js';
4
4
  import { applyAllComponents, computeComponentChecksums } from '../../core/furnace-apply.js';
5
5
  import { logApplyResult } from '../../core/furnace-apply-output.js';
6
- import { furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, } from '../../core/furnace-config.js';
6
+ import { getFurnacePaths } from '../../core/furnace-config.js';
7
7
  import { isComponentSourceFile } from '../../core/furnace-constants.js';
8
8
  import { runFurnaceMutation } from '../../core/furnace-operation.js';
9
+ import { assertFurnaceReady } from '../../core/furnace-precondition.js';
9
10
  import { getPersistableAppliedEntry, persistSingleComponentState, shouldPersistSingleComponentState, } from '../../core/furnace-state-persist.js';
10
11
  import { countEntriesWithBlockingStepErrors } from '../../core/furnace-step-errors.js';
11
12
  import { findOverrideBaseVersionDrift, formatOverrideBaseVersionDriftError, formatOverrideBaseVersionDriftWarning, } from '../../core/furnace-version-drift.js';
@@ -74,11 +75,10 @@ function classifyWatchApplyError(err) {
74
75
  async function runWatchLoop(projectRoot) {
75
76
  const furnacePaths = getFurnacePaths(projectRoot);
76
77
  // Both categories are eligible targets. The set is fixed; only existence
77
- // varies over time — a component dir created AFTER watch started (e.g.
78
- // the user runs `furnace create` in another terminal) must be picked up
79
- // without restarting watch. The prior one-shot `pathExists` check at
80
- // startup captured only the dirs that existed then, leaving any later
81
- // creation invisible.
78
+ // varies over time — a component dir created AFTER watch started (the user
79
+ // running `furnace create` in another terminal) must be picked up without
80
+ // restarting watch. A one-shot `pathExists` check at startup captures only
81
+ // the dirs that existed then, leaving any later creation invisible.
82
82
  const candidateDirs = [furnacePaths.overridesDir, furnacePaths.customDir];
83
83
  const watchDirs = [];
84
84
  const watchers = new Map();
@@ -253,16 +253,7 @@ async function runWatchLoop(projectRoot) {
253
253
  */
254
254
  export async function furnaceApplyCommand(projectRoot, name, options = {}) {
255
255
  intro(name ? `Furnace Apply (${name})` : 'Furnace Apply');
256
- // Verify engine exists
257
- const paths = getProjectPaths(projectRoot);
258
- if (!(await pathExists(paths.engine))) {
259
- throw new FurnaceError('Engine directory not found. Run "fireforge download" first.');
260
- }
261
- // Load furnace config
262
- if (!(await furnaceConfigExists(projectRoot))) {
263
- throw new FurnaceError('No furnace.json found. Run "fireforge furnace create" or "fireforge furnace override" to get started.');
264
- }
265
- const config = await loadFurnaceConfig(projectRoot);
256
+ const { config } = await assertFurnaceReady(projectRoot);
266
257
  const overrideCount = Object.keys(config.overrides).length;
267
258
  const customCount = Object.keys(config.custom).length;
268
259
  if (overrideCount === 0 && customCount === 0) {
@@ -289,12 +280,12 @@ export async function furnaceApplyCommand(projectRoot, name, options = {}) {
289
280
  // `persistState: false` for a NAMED apply is load-bearing: the batch
290
281
  // persist path replaces `appliedChecksums` wholesale with only this
291
282
  // run's entries, and the batch loops filter to the named component —
292
- // so routing a named apply through it persisted a state file
293
- // containing ONLY that component, wiping every other component's
294
- // checksums. Orphan detection and deleted-file undeploy both key on
295
- // that state, so the wiped components' stale engine files became
296
- // invisible to apply AND to `furnace validate`. Named apply merges
297
- // per-component state below, exactly like `furnace deploy <name>`.
283
+ // so routing a named apply through it persists a state file containing
284
+ // ONLY that component, wiping every other component's checksums.
285
+ // Orphan detection and deleted-file undeploy both key on that state,
286
+ // so the wiped components' stale engine files become invisible to
287
+ // apply AND to `furnace validate`. Named apply merges per-component
288
+ // state below, exactly like `furnace deploy <name>`.
298
289
  const applyResult = await applyAllComponents(projectRoot, dryRun, {
299
290
  operationContext: ctx,
300
291
  ...(name !== undefined ? { componentName: name, persistState: false } : {}),
@@ -7,10 +7,11 @@ import { readdir } from 'node:fs/promises';
7
7
  import { join } from 'node:path';
8
8
  import { confirm } from '@clack/prompts';
9
9
  import { loadConfig } from '../../core/config.js';
10
- import { recordFurnaceRollbackFailure, runFurnaceMutation, } from '../../core/furnace-operation.js';
11
- import { createRollbackJournal, restoreRollbackJournalOrThrow, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
10
+ import { stdioIsInteractive } from '../../core/destructive.js';
11
+ import { completeJournalRollback, runFurnaceMutation, } from '../../core/furnace-operation.js';
12
+ import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
13
+ import { createRollbackJournal, snapshotDir, snapshotFile } from '../../core/furnace-rollback.js';
12
14
  import { FurnaceError } from '../../errors/furnace.js';
13
- import { toError } from '../../utils/errors.js';
14
15
  import { pathExists, readText, removeDir, removeFile, writeText } from '../../utils/fs.js';
15
16
  import { cancel, info, intro, isCancel, note, outro } from '../../utils/logger.js';
16
17
  import { buildChromeDocPlan, validateChromeDocName } from './chrome-doc.js';
@@ -87,20 +88,12 @@ async function performChromeDocRemoveMutations(args) {
87
88
  await removeEmptyDirIfPresent(join(args.engineDir, 'browser/base/content/test', `${args.binaryName}-xpcshell`), journal);
88
89
  }
89
90
  catch (error) {
90
- // This body owns its rollback end to end, so tell the lifecycle wrapper
91
- // not to restore the same journal again on the way out.
92
- args.operationContext.markRolledBack();
93
- try {
94
- await restoreRollbackJournalOrThrow(journal, `Failed to remove chrome-doc "${args.name}"`);
95
- }
96
- catch (rollbackError) {
97
- // Matches the seven sibling mutation sites: a failed rollback leaves the
98
- // engine in an unknown state, so record the repair breadcrumb and let
99
- // the rollback error win rather than discarding it.
100
- await recordFurnaceRollbackFailure(args.projectRoot, 'chrome-doc-rollback', `chrome-doc "${args.name}": ${toError(rollbackError).message}`);
101
- throw rollbackError;
102
- }
103
- throw error;
91
+ return await completeJournalRollback(args.operationContext, journal, error, {
92
+ projectRoot: args.projectRoot,
93
+ operation: 'chrome-doc-rollback',
94
+ failureMessage: `Failed to remove chrome-doc "${args.name}"`,
95
+ subject: `chrome-doc "${args.name}"`,
96
+ });
104
97
  }
105
98
  return { removedFiles, removedJarEntries, removedTestDir };
106
99
  }
@@ -110,9 +103,9 @@ export async function furnaceChromeDocRemoveCommand(projectRoot, name, options =
110
103
  validateChromeDocName(name);
111
104
  const forgeConfig = await loadConfig(projectRoot);
112
105
  const engineDir = join(projectRoot, 'engine');
113
- if (!(await pathExists(engineDir))) {
114
- throw new FurnaceError('Engine directory not found. Run "fireforge download" first before removing a chrome-doc.');
115
- }
106
+ await assertFurnaceEngineReady(projectRoot, {
107
+ engineMissingSuffix: ' before removing a chrome-doc.',
108
+ });
116
109
  const plan = await buildChromeDocPlan({
117
110
  engineDir,
118
111
  name,
@@ -125,7 +118,7 @@ export async function furnaceChromeDocRemoveCommand(projectRoot, name, options =
125
118
  outro('Dry run complete');
126
119
  return;
127
120
  }
128
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
121
+ const isInteractive = stdioIsInteractive();
129
122
  if (!options.yes && !isInteractive) {
130
123
  throw new FurnaceError(`Cannot remove chrome-doc "${name}" in non-interactive mode without --yes flag.`, name);
131
124
  }
@@ -41,23 +41,25 @@
41
41
  */
42
42
  export declare function generateChromeDocXhtml(name: string, withTitlebar: boolean, license: string): string;
43
43
  /**
44
- * browser.xhtml-like scaffold for the document that ships as the fork's
45
- * MAIN BROWSER WINDOW (0.34.0 field report): the generic dialog-shaped
46
- * `<window>` scaffold was wrong for the `tokenHostDocuments[0]` /
47
- * BROWSER_CHROME_URL target — platform C++ reads the root element BEFORE
48
- * any script runs, and expects the `browser.xhtml` shape:
44
+ * browser.xhtml-like scaffold for the document that ships as the fork's MAIN
45
+ * BROWSER WINDOW.
46
+ *
47
+ * The generic dialog-shaped `<window>` scaffold is wrong for the
48
+ * `tokenHostDocuments[0]` / BROWSER_CHROME_URL target: platform C++ reads
49
+ * the root element BEFORE any script runs, and expects the `browser.xhtml`
50
+ * shape:
49
51
  *
50
52
  * - `<html id="main-window">` root (not `<window id="<name>-window">`) —
51
53
  * upstream code from nsXULWindow sizing to session restore looks up
52
54
  * `main-window` by id;
53
55
  * - `windowtype="navigator:browser"`, `chromehidden=""` and the geometry
54
56
  * `persist` allowlist declared as ROOT ATTRIBUTES so the platform's
55
- * pre-script pass (window tracking, XULStore geometry, chrome flags)
56
- * sees them;
57
+ * pre-script pass (window tracking, XULStore geometry, chrome flags) sees
58
+ * them;
57
59
  * - the same head/bootstrap wiring (customElements.js, per-doc subscript,
58
60
  * CSS + Fluent links) and `data-furnace-chrome-doc` sentinel as the
59
- * generic scaffold, so jar.mn registration and the platform-module
60
- * guard pattern are unchanged.
61
+ * generic scaffold, so jar.mn registration and the platform-module guard
62
+ * pattern are unchanged.
61
63
  */
62
64
  export declare function generateBrowserWindowXhtml(name: string, license: string): string;
63
65
  /**
@@ -105,25 +107,23 @@ export declare function jarMnEntriesForChromeDoc(name: string): string[];
105
107
  /**
106
108
  * jar.inc.mn entry that registers the scoped CSS under `content/browser/`.
107
109
  *
108
- * The source path is `../shared/<name>-chrome.css` because `jar.inc.mn`
109
- * is included from each theme-specific manifest (`browser/themes/osx/jar.mn`,
110
- * `browser/themes/linux/jar.mn`, `browser/themes/windows/jar.mn`), and every
111
- * existing entry in those manifests resolves paths relative to the including
112
- * manifest's directory. A bare `(shared/…)` path produced
113
- * `obj-.../browser/themes/osx/shared/<name>-chrome.css` which does not exist;
114
- * `(../shared/…)` matches the upstream pattern and resolves under
115
- * `browser/themes/shared/`.
110
+ * The source path is `../shared/<name>-chrome.css` because `jar.inc.mn` is
111
+ * included from each theme-specific manifest (`browser/themes/osx/jar.mn`,
112
+ * `.../linux/`, `.../windows/`), and every existing entry in those manifests
113
+ * resolves paths relative to the INCLUDING manifest's directory. A bare
114
+ * `(shared/…)` path resolves to
115
+ * `obj-.../browser/themes/osx/shared/<name>-chrome.css`, which does not
116
+ * exist.
116
117
  */
117
118
  export declare function jarIncMnEntryForChromeDoc(name: string): string;
118
119
  /**
119
120
  * locales/jar.mn entry that registers the `.ftl` under the browser locale
120
121
  * bundle. The source path is resolved by mach-locale-jar relative to the
121
- * per-locale root (e.g. `engine/browser/locales/en-US/`), and the FTL
122
- * file is scaffolded at `browser/${name}.ftl` under that root — the `%`
123
- * prefix means "per-locale content" and the `browser/` subdirectory
124
- * matches the subdir the scaffolder writes into. Before this fix the
125
- * entry emitted `(%${name}.ftl)`, which pointed at `en-US/${name}.ftl`
126
- * and broke the first post-scaffold `fireforge build` with
122
+ * per-locale root (e.g. `engine/browser/locales/en-US/`), and the FTL file
123
+ * is scaffolded at `browser/${name}.ftl` under that root — the `%` prefix
124
+ * means "per-locale content" and the `browser/` subdirectory matches where
125
+ * the scaffolder writes. A bare `(%${name}.ftl)` points at
126
+ * `en-US/${name}.ftl` and breaks the first post-scaffold build with
127
127
  * "jar.mn: Cannot find ${name}.ftl".
128
128
  */
129
129
  export declare function localeJarMnEntryForChromeDoc(name: string): string;