@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
@@ -1,18 +1,20 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { join, resolve } from 'node:path';
3
3
  import { getProjectPaths, loadConfig } from '../core/config.js';
4
+ import { assertEngineExists } from '../core/engine-precondition.js';
4
5
  import { assertEngineGenerationUnchanged, snapshotEngineGeneration, } from '../core/engine-session-lock.js';
5
6
  import { hasBuildArtifacts, hasRunnableBundle } from '../core/mach.js';
6
7
  import { assertBuildArtifacts } from '../core/mach-build-artifacts.js';
7
8
  import { assertMarionettePortAvailable, ensureLaunchableBrowserNotRunning, ensureMarionettePortAvailable, extractForwardedMarionettePort, forwardedMachArgsIncludeMarionetteClient, shouldAutoForwardMarionettePortToMach, } from '../core/marionette-port.js';
8
9
  import { formatMarionettePreflightLine, reportMarionettePreflight, runMarionettePreflight, } from '../core/marionette-preflight.js';
10
+ import { ensureMochitestServerPortAvailable } from '../core/mochitest-server-port.js';
9
11
  import { createPostRebuildFailureContext } from '../core/test-harness-output.js';
10
12
  import { analyzeTestPathScopes, formatScopeNotice, } from '../core/test-path-scope.js';
11
13
  import { assertObjdirMatchesTreeMarker } from '../core/tree-store.js';
12
14
  import { GeneralError } from '../errors/base.js';
13
15
  import { BuildError } from '../errors/build.js';
14
16
  import { pathExists } from '../utils/fs.js';
15
- import { info, intro, notice, outro, success, verbose } from '../utils/logger.js';
17
+ import { info, intro, notice, outro, verbose } from '../utils/logger.js';
16
18
  import { stripEnginePrefix } from '../utils/paths.js';
17
19
  import { runTestBuildPhase } from './test-build-phase.js';
18
20
  import { diagnoseShardOutcome, finalizeSingleRunOutcome } from './test-diagnose.js';
@@ -36,8 +38,8 @@ async function assertTestPathsExist(engineDir, testPaths) {
36
38
  * Picks the mach dispatch target for a (non-mixed) run. A single-suite run
37
39
  * auto-routes to the suite-specific command (`mach xpcshell-test` /
38
40
  * `mach mochitest`), which degrades a broken host resource monitor to a
39
- * warning instead of crashing generic `mach test` at startup (E1). Mixed runs
40
- * are rejected before this point; a path-less "run all" or an explicit
41
+ * warning instead of crashing generic `mach test` at startup. Mixed runs are
42
+ * rejected before this point; a path-less "run all" or an explicit
41
43
  * `--generic-mach-test` opt-out stays on the generic command.
42
44
  */
43
45
  function resolveTestSuite(classification, forceGeneric) {
@@ -102,27 +104,30 @@ function logTestSelection(scopes) {
102
104
  */
103
105
  async function runDoctorPreflight(args) {
104
106
  const { engineDir, effectivePort, hasTestPaths, objDir, binaryName, launchablePath } = args;
105
- // Write the "Running marionette preflight..." banner via
106
- // `process.stdout.write` directly before `info()` so non-TTY captures
107
- // always see the banner even if clack's renderer defers output in
108
- // pipe mode. `info()` is still called so TTY users keep the normal
109
- // clack box-drawing framing.
110
- process.stdout.write('Running marionette preflight...\n');
111
- info('Running marionette preflight...');
107
+ // Non-TTY captures need the banner even if clack's renderer defers output
108
+ // in pipe mode; TTY users need the clack framing. Gated rather than
109
+ // written twice — the unconditional pair printed the same line twice on a
110
+ // terminal.
111
+ if (process.stdout.isTTY) {
112
+ info('Running marionette preflight...');
113
+ }
114
+ else {
115
+ process.stdout.write('Running marionette preflight...\n');
116
+ }
112
117
  const preflight = effectivePort !== undefined
113
118
  ? await runMarionettePreflight(engineDir, { port: effectivePort })
114
119
  : await runMarionettePreflight(engineDir);
115
- // 2026-04-24 eval Finding 7: the pre-0.18.1 code used
116
- // `success()` + `outro()` + a direct `process.stdout.write` as a
117
- // belt-and-suspenders but still reproducibly dropped the PASS summary
118
- // under non-TTY capture (observed: `tee`-wrapped eval output saw only
119
- // the intro). The fix writes the authoritative PASS/FAIL line via
120
- // `process.stdout.write` as the very first output after the probe
121
- // returns, so the captured stream has an unambiguous summary no
122
- // matter what clack does on top. The clack-rendered banner
123
- // (`info`/`warn`) is retained so TTY users keep the visual framing.
120
+ // The authoritative PASS/FAIL line is written with `process.stdout.write`
121
+ // as the first output after the probe returns, because clack's renderer
122
+ // can drop the summary under non-TTY capture.
123
+ //
124
+ // Gated on non-TTY: unconditional, it stacks with the two clack renderings
125
+ // below and the same line appears THREE times on a terminal. Captured
126
+ // streams are exactly where this branch still fires.
124
127
  const directLine = formatMarionettePreflightLine(preflight);
125
- process.stdout.write(`${directLine}\n`);
128
+ if (!process.stdout.isTTY) {
129
+ process.stdout.write(`${directLine}\n`);
130
+ }
126
131
  process.stdout.write(`Marionette preflight environment: objdir=${objDir ?? '(none)'}; binary=${binaryName}; app=${launchablePath ? `engine/${launchablePath}` : '(unknown)'}; port=${effectivePort ?? 2828}; elapsed=${preflight.durationMs}ms\n`);
127
132
  reportMarionettePreflight(preflight);
128
133
  if (!hasTestPaths) {
@@ -130,7 +135,6 @@ async function runDoctorPreflight(args) {
130
135
  emitFailVerdict('preflight');
131
136
  throw new GeneralError('Marionette preflight reported FAIL — see output above.');
132
137
  }
133
- success(directLine);
134
138
  // Doctor-only runs end here, so they carry their own verdict line
135
139
  // (reason=preflight on failure); with test paths the verdict comes
136
140
  // from the actual harness run downstream.
@@ -152,19 +156,19 @@ async function runDoctorPreflight(args) {
152
156
  * Mutates `extraArgs` in place.
153
157
  */
154
158
  function appendMarionetteForwardingArgs(extraArgs, options, forwardedPort, xpcshellOnly = false) {
155
- // Auto-forward the Marionette port to mach when `--marionette-port` is
156
- // set. `--setpref=marionette.port=<n>` configures where the browser
157
- // listener binds; `--marionette=127.0.0.1:<n>` tells the mochitest harness
158
- // client to connect there (default client is 127.0.0.1:2828). xpcshell
159
- // ignores both for browser Marionette.
159
+ // Auto-forward the Marionette port to mach when `--marionette-port` is set.
160
+ // `--setpref=marionette.port=<n>` configures where the browser listener
161
+ // binds; `--marionette=127.0.0.1:<n>` tells the mochitest harness client to
162
+ // connect there (default client is 127.0.0.1:2828). xpcshell ignores both
163
+ // for browser Marionette.
160
164
  //
161
165
  // Skip setpref forwarding when the operator already supplied an equivalent
162
- // arg via `--mach-arg` — duplicates would be confusing without changing
163
- // semantics. Skip when mach args explicitly request `--flavor=xpcshell`
164
- // (or `xpcshell-tests`): the preflight still honours `--marionette-port`,
165
- // but mach does not use the marionette.port pref on that harness. Any
166
- // other arg shape still forwards so toolkit widget paths and mixed suites
167
- // stay aligned with the probe without duplicate `--mach-arg` flags.
166
+ // arg via `--mach-arg` — duplicates would confuse without changing
167
+ // semantics. Skip when mach args explicitly request `--flavor=xpcshell` (or
168
+ // `xpcshell-tests`): the preflight still honours `--marionette-port`, but
169
+ // mach does not use the marionette.port pref on that harness. Any other arg
170
+ // shape still forwards so toolkit widget paths and mixed suites stay
171
+ // aligned with the probe without duplicate `--mach-arg` flags.
168
172
  //
169
173
  // Skip auto `--marionette=...` when `--mach-arg` already includes a client
170
174
  // `--marionette=...` (or two-token `--marionette host:port`).
@@ -172,9 +176,8 @@ function appendMarionetteForwardingArgs(extraArgs, options, forwardedPort, xpcsh
172
176
  return;
173
177
  if (xpcshellOnly) {
174
178
  // Manifest classification says every requested path is xpcshell —
175
- // xpcshell ignores the browser Marionette path entirely, and the
176
- // mochitest client flags previously forwarded here made mach reject
177
- // the dispatch.
179
+ // xpcshell ignores the browser Marionette path entirely, and forwarding
180
+ // the mochitest client flags here makes mach reject the dispatch.
178
181
  info(`--marionette-port=${options.marionettePort} applied to the preflight probe only: the requested paths are xpcshell-only, and xpcshell ignores the browser Marionette port. Not forwarding --setpref=marionette.port or --marionette to mach.`);
179
182
  return;
180
183
  }
@@ -201,8 +204,8 @@ async function ensureTestMarionettePortAvailable(port, binaryName, options, skip
201
204
  // This also recognizes a fork-branded browser via binaryName.
202
205
  if (skip.xpcshellOnly && !skip.doctor) {
203
206
  // xpcshell does not bind the browser Marionette port, so a developer's
204
- // interactive browser holding 2828 must not kill an xpcshell run
205
- //. --doctor keeps the preflight: its probe launches a
207
+ // interactive browser holding 2828 must not kill an xpcshell run.
208
+ // --doctor keeps the preflight: its probe launches a
206
209
  // Marionette browser regardless of the requested harness.
207
210
  const message = 'Skipping the Marionette stale-port preflight: all requested paths are xpcshell ' +
208
211
  '(xpcshell does not bind the browser Marionette port).';
@@ -229,6 +232,15 @@ async function ensureTestBrowserEnvironment(engineDir, launchablePath, xpcshellO
229
232
  killStaleBrowser: options.killStaleMarionette === true,
230
233
  });
231
234
  }
235
+ // A zombie mochitest httpd squatting the server port makes a fresh
236
+ // browser connect to a server that cannot serve this run's manifest,
237
+ // which surfaces as a 370s "Ran 0 checks" stall naming nothing. xpcshell
238
+ // does not use the httpd, so an xpcshell-only run is never blocked by it.
239
+ if (!xpcshellOnly) {
240
+ await ensureMochitestServerPortAvailable(undefined, {
241
+ killStaleServer: options.killStaleMarionette === true,
242
+ });
243
+ }
232
244
  const forwardedPort = options.machArg
233
245
  ? extractForwardedMarionettePort(options.machArg)
234
246
  : undefined;
@@ -292,9 +304,7 @@ async function verifyEngineGenerationOrEmitInconclusive(engineDir, before) {
292
304
  async function runTestCommandBody(projectRoot, testPaths, options = {}) {
293
305
  const paths = getProjectPaths(projectRoot);
294
306
  // Check if engine exists
295
- if (!(await pathExists(paths.engine))) {
296
- throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
297
- }
307
+ await assertEngineExists(paths.engine);
298
308
  assertPathlessTestMode(testPaths, options);
299
309
  const buildCheck = await hasBuildArtifacts(paths.engine);
300
310
  assertBuildArtifacts(paths.engine, buildCheck, TEST_BUILD_PREFLIGHT);
@@ -307,14 +317,14 @@ async function runTestCommandBody(projectRoot, testPaths, options = {}) {
307
317
  const projectConfig = await loadConfig(projectRoot);
308
318
  const canaryPath = resolveCanaryPath(options, projectConfig);
309
319
  assertTestModeCombinations(testPaths, options, canaryPath);
310
- // `hasBuildArtifacts` only confirms `obj-*/dist/` exists; a partial
311
- // build (linker failed, packaging step interrupted, etc.) can satisfy
312
- // that check without ever writing the launchable binary the marionette
313
- // preflight needs to spawn. `fireforge run` already uses
314
- // `hasRunnableBundle` to fail fast with a precise message; mirror that
315
- // here so `test --doctor` against an incomplete build surfaces the
316
- // missing-bundle path instead of a cryptic `Browser process exited
317
- // during spawn (exit code 1, signal none). stderr tail: (empty)`.
320
+ // `hasBuildArtifacts` only confirms `obj-*/dist/` exists; a partial build
321
+ // (linker failed, packaging step interrupted) can satisfy that check
322
+ // without ever writing the launchable binary the marionette preflight
323
+ // needs to spawn. `fireforge run` already uses `hasRunnableBundle` to fail
324
+ // fast with a precise message; mirroring it here makes `test --doctor`
325
+ // against an incomplete build surface the missing-bundle path instead of a
326
+ // cryptic `Browser process exited during spawn (exit code 1, signal none).
327
+ // stderr tail: (empty)`.
318
328
  const launchablePath = await resolveLaunchablePathForTests(paths.engine, projectConfig.binaryName, buildCheck.objDir);
319
329
  const harnessRetries = options.harnessRetries ?? DEFAULT_HARNESS_RETRIES;
320
330
  // Normalized engine-relative request paths, hoisted above the build/stale
@@ -338,9 +348,9 @@ async function runTestCommandBody(projectRoot, testPaths, options = {}) {
338
348
  // 3. fall back to `DEFAULT_MARIONETTE_PORT` semantics inside the probes
339
349
  // (passed as `undefined`).
340
350
  // Without (2), an operator working around a stale listener via the
341
- // documented `--mach-arg --marionette-port=NNNN` workaround would still
342
- // hit the wrapper preflight refusing on 2828 before the forwarded arg
343
- // ever reached mach.
351
+ // documented `--mach-arg --marionette-port=NNNN` route still hits the
352
+ // wrapper preflight refusing on 2828 before the forwarded arg reaches
353
+ // mach.
344
354
  const { forwardedPort, effectivePort } = await ensureTestBrowserEnvironment(paths.engine, launchablePath, xpcshellOnly, projectConfig.binaryName, options);
345
355
  if (options.doctor) {
346
356
  const doctorOutcome = await runDoctorPreflight({
@@ -371,7 +381,7 @@ async function runTestCommandBody(projectRoot, testPaths, options = {}) {
371
381
  }
372
382
  // --mach-arg is a verbatim passthrough for upstream mach/xpcshell/mochitest
373
383
  // flags FireForge does not model directly (see the xpcshell appdir hint
374
- // above for the motivating case). Appended AFTER --headless so mach sees
384
+ // above for why). Appended AFTER --headless so mach sees
375
385
  // the FireForge-managed flags first and the escape-valve ones last, which
376
386
  // keeps the override precedence predictable.
377
387
  if (forwardedMachArgs.length > 0) {
@@ -380,16 +390,14 @@ async function runTestCommandBody(projectRoot, testPaths, options = {}) {
380
390
  appendMarionetteForwardingArgs(extraArgs, options, forwardedPort, xpcshellOnly);
381
391
  // Directory arguments mean EXACTLY that directory: mozbuild's test
382
392
  // resolver matches paths by string prefix, so a bare directory arg
383
- // silently swept in prefix-named siblings (152.0b7 → 153.0b8 drill:
384
- // `…/test/hominis` also ran `…/test/hominis-tiles`, 1224 tests instead
385
- // of ~200, with no indication the scope widened). Each directory
386
- // argument dispatches as its enumerated explicit test-file list — a
387
- // file list cannot prefix-match a sibling (0.35.0's trailing-`/`
388
- // normalization turned out to be cosmetic on Firefox 153's mach; field
389
- // verification showed the sibling still ran while the echo claimed it
390
- // was excluded). Any prefix-siblings are echoed so the narrowed scope
391
- // is visible. Classification above intentionally used the raw
392
- // argument forms; only the mach dispatch needs the exact-match shape.
393
+ // silently sweeps in prefix-named siblings — `…/test/hominis` also running
394
+ // `…/test/hominis-tiles`, with no indication the scope widened. Each
395
+ // directory argument therefore dispatches as its enumerated explicit
396
+ // test-file list, which cannot prefix-match a sibling; a trailing-`/`
397
+ // normalization does not work, as mach still sweeps the sibling in. Any
398
+ // prefix-siblings are echoed so the narrowed scope is visible.
399
+ // Classification above intentionally used the raw argument forms; only the
400
+ // mach dispatch needs the exact-match shape.
393
401
  const scopes = await analyzeTestPathScopes(paths.engine, normalizedPaths);
394
402
  const dispatchGroups = scopes.map((scope) => ({
395
403
  label: scope.requestedPath,
@@ -419,17 +427,16 @@ async function runTestCommandBody(projectRoot, testPaths, options = {}) {
419
427
  const postRebuildContext = options.build
420
428
  ? createPostRebuildFailureContext('fireforge test --build', normalizedPaths)
421
429
  : undefined;
422
- // Multi-argument requests shard into sequential harness runs by default
423
- // (field report C3): one shared mochitest profile across files bleeds
424
- // pref/media-query state into later files. Sharding is per path
425
- // ARGUMENT — a directory argument keeps its enumerated files together
426
- // in one invocation, preserving the one-browser-instance semantics of
427
- // a directory run. --no-shard restores the combined invocation. The
428
- // default must not be SILENT (drill finding: a two-file cross-file
429
- // pollution repro "passed" because the headed comparison run was
430
- // sharded without saying so, briefly misattributing a suite-context
431
- // bug to an upstream headless regression), so a one-line notice states
432
- // what sharding does and does not exercise.
430
+ // Multi-argument requests shard into sequential harness runs by default:
431
+ // one shared mochitest profile across files bleeds pref/media-query state
432
+ // into later files. Sharding is per path ARGUMENT — a directory argument
433
+ // keeps its enumerated files together in one invocation, preserving the
434
+ // one-browser-instance semantics of a directory run. `--no-shard` restores
435
+ // the combined invocation. The default must not be SILENT: a cross-file
436
+ // pollution repro otherwise "passes" because the comparison run was
437
+ // sharded without saying so, misattributing a suite-context bug upstream.
438
+ // Hence the one-line notice stating what sharding does and does not
439
+ // exercise.
433
440
  if (canaryPath === undefined && dispatchGroups.length > 1 && options.shard !== false) {
434
441
  notice(`Sharding: running ${dispatchGroups.length} test path arguments in isolated browser instances ` +
435
442
  '(one mach invocation per argument; a directory argument keeps its files in one instance). ' +
@@ -468,9 +475,9 @@ async function runTestCommandBody(projectRoot, testPaths, options = {}) {
468
475
  finalizeSingleRunOutcome(outcome, normalizedPaths, projectConfig.binaryName, postRebuildContext, options.headless === true);
469
476
  }
470
477
  /**
471
- * Builds the perf-sample env contract for the harness run (field report
472
- * C4): `--perf-samples <path>` exports `<BINARYNAME>_PERF_SAMPLE_JSON`
473
- * naming the artifact file a budget checker consumes after the run.
478
+ * Builds the perf-sample env contract for the harness run:
479
+ * `--perf-samples <path>` exports `<BINARYNAME>_PERF_SAMPLE_JSON` naming the
480
+ * artifact file a budget checker consumes after the run.
474
481
  */
475
482
  function buildPerfSampleEnv(projectRoot, binaryName, perfSamples) {
476
483
  if (!perfSamples)
@@ -34,10 +34,9 @@ export async function tokenCoverageCommand(projectRoot) {
34
34
  .map((f) => f.file);
35
35
  // Also scan CSS files deployed by Furnace custom components. Deployed
36
36
  // files can be committed (and therefore absent from `git status`) while
37
- // still being the primary surface where token adoption matters. Before
38
- // 0.16.0, coverage only looked at modified files, which silently
39
- // undercounted projects where Furnace writes many component-CSS files
40
- // into the engine and they are already tracked.
37
+ // still being the primary surface where token adoption matters; looking
38
+ // only at modified files silently undercounts projects where Furnace
39
+ // writes many component-CSS files into the engine.
41
40
  const furnaceCssFiles = await collectFurnaceCustomCssFiles(projectRoot, paths.engine, tokensCssPath);
42
41
  // De-dupe so a file that is both a custom deploy target AND modified is
43
42
  // scanned exactly once.
@@ -2,7 +2,7 @@
2
2
  import { Command, Option } from 'commander';
3
3
  import { loadConfig } from '../core/config.js';
4
4
  import { furnaceConfigExists, loadFurnaceConfig } from '../core/furnace-config.js';
5
- import { addToken, getTokensCssPath, validateTokenAdd, } from '../core/token-manager.js';
5
+ import { addToken, getTokensCssPath, isTokenMode, TOKEN_MODES, validateTokenAdd, } from '../core/token-manager.js';
6
6
  import { InvalidArgumentError } from '../errors/base.js';
7
7
  import { FurnaceError } from '../errors/furnace.js';
8
8
  import { toError } from '../utils/errors.js';
@@ -45,13 +45,12 @@ async function normalizeTokenNameForProject(projectRoot, rawTokenName) {
45
45
  */
46
46
  export async function tokenAddCommand(projectRoot, tokenName, value, options) {
47
47
  intro('Token Add');
48
- // Finding #15: a fresh project without furnace.json failed deep inside
49
- // the token-manager's `assertTokenCategoryExists` with "Token CSS file
50
- // not found: browser/themes/shared/<binary>-tokens.css" — technically
51
- // correct, but the operator's actual next step is to initialize
52
- // Furnace (which scaffolds the tokens CSS file among other things).
53
- // Catching the uninitialized case here gives the right guidance up-
54
- // front before the generic "file not found" error fires.
48
+ // A fresh project without furnace.json otherwise fails deep inside the
49
+ // token-manager's `assertTokenCategoryExists` with "Token CSS file not
50
+ // found: browser/themes/shared/<binary>-tokens.css" — technically correct,
51
+ // but the operator's actual next step is to initialize Furnace, which
52
+ // scaffolds the tokens CSS file. Catching the uninitialized case here
53
+ // gives the right guidance before the generic "file not found" error.
55
54
  if (!(await furnaceConfigExists(projectRoot))) {
56
55
  throw new FurnaceError('Token management requires Furnace to be initialized. ' +
57
56
  'Tokens live in the Furnace-managed tokens CSS file, which `fireforge furnace init` scaffolds alongside the rest of the Furnace workspace.\n\n' +
@@ -60,10 +59,10 @@ export async function tokenAddCommand(projectRoot, tokenName, value, options) {
60
59
  // Normalize token name using the configured Furnace token prefix when the
61
60
  // user supplied a bare token name like "canvas-gap".
62
61
  tokenName = await normalizeTokenNameForProject(projectRoot, tokenName);
63
- // Validate mode
64
- const validModes = ['auto', 'static', 'override'];
65
- if (!validModes.includes(options.mode)) {
66
- throw new InvalidArgumentError(`Invalid mode "${options.mode}". Must be one of: ${validModes.join(', ')}`, 'mode');
62
+ // Validate mode. The guard IS the runtime proof, so the three `as
63
+ // TokenMode` casts this replaced are gone with it.
64
+ if (!isTokenMode(options.mode)) {
65
+ throw new InvalidArgumentError(`Invalid mode "${options.mode}". Must be one of: ${TOKEN_MODES.join(', ')}`, 'mode');
67
66
  }
68
67
  if (options.dryRun) {
69
68
  await validateTokenAdd(projectRoot, {
@@ -131,12 +130,11 @@ export function registerToken(program, { getProjectRoot, withErrorHandling }) {
131
130
  const token = program
132
131
  .command('token')
133
132
  .description('Design token management')
134
- // Match `fireforge furnace`'s no-args contract: print the group's help and
135
- // exit 0. Without this default action, commander routes `fireforge token`
136
- // (no subcommand) through its own help-then-exit-1 path, so scripts that
137
- // probe the CLI surface see a misleading non-zero exit for a purely
138
- // informational invocation. The action prints the exact same help commander
139
- // would otherwise print, but returns successfully.
133
+ // Match `fireforge furnace`'s no-args contract: print the group's help
134
+ // and exit 0. Without this default action, commander routes
135
+ // `fireforge token` (no subcommand) through its own help-then-exit-1
136
+ // path, so scripts probing the CLI surface see a misleading non-zero
137
+ // exit for a purely informational invocation.
140
138
  .action(() => {
141
139
  token.outputHelp();
142
140
  });
@@ -2,21 +2,23 @@
2
2
  /**
3
3
  * `fireforge tree` — copy-on-write verification clones.
4
4
  *
5
- * Trees enable concurrent READ-MOSTLY verification (lint, typecheck,
6
- * status, verify, doctor, export dry-runs) beside a busy primary tree.
7
- * Exports and every other mutation stay strictly serial in the primary —
8
- * a tree's patches/components are snapshots with no merge model, and the
9
- * tree guard (cli.ts preAction + core/tree-guard.ts) refuses mutating
10
- * commands inside a tree. `create --with-objdir` additionally clones the
11
- * primary's built obj-* directory, rewrites its mozinfo.json to the
12
- * tree, and runs `mach configure` inside the tree so the remaining
13
- * configure output (config.status, backend.mk, Makefile,
14
- * config/autoconf.mk — the verified set) stops naming the primary
15
- * (fail-closed — mach objdirs embed absolute source paths, and an
16
- * unrelocated clone would silently operate against the primary), which
17
- * is what admits build-less `fireforge test` inside that tree.
5
+ * Trees enable concurrent READ-MOSTLY verification (lint, typecheck, status,
6
+ * verify, doctor, export dry-runs) beside a busy primary tree. Exports and
7
+ * every other mutation stay strictly serial in the primary — a tree's
8
+ * patches/components are snapshots with no merge model, and the tree guard
9
+ * (cli.ts preAction + core/tree-guard.ts) refuses mutating commands inside a
10
+ * tree.
11
+ *
12
+ * `create --with-objdir` additionally clones the primary's built obj-*
13
+ * directory, rewrites its mozinfo.json to the tree, and runs `mach configure`
14
+ * inside the tree so the remaining configure output (config.status,
15
+ * backend.mk, Makefile, config/autoconf.mk) stops naming the primary. That is
16
+ * fail-closed by design — mach objdirs embed absolute source paths, and an
17
+ * unrelocated clone would silently operate against the primary — and it is
18
+ * what admits build-less `fireforge test` inside that tree.
18
19
  */
19
20
  import { spawn } from 'node:child_process';
21
+ import { mkdir, rm } from 'node:fs/promises';
20
22
  import { join } from 'node:path';
21
23
  import { Command } from 'commander';
22
24
  import { withEngineSessionLock } from '../core/engine-session-lock.js';
@@ -24,11 +26,13 @@ import { hasBuildArtifacts, runMach, withBuildLock } from '../core/mach.js';
24
26
  import { assertBuildArtifacts, findObjdirRelocationViolation, } from '../core/mach-build-artifacts.js';
25
27
  import { detectCowSupport } from '../core/tree-cow.js';
26
28
  import { cloneTree, listTrees, removeTree } from '../core/tree-store.js';
27
- import { assertValidTreeName, computePrimaryFingerprint, getTreeMarkerPath, getTreesDir, readTreeMarkerState, withTreeLifecycleLock, } from '../core/tree-store.js';
28
- import { GeneralError } from '../errors/base.js';
29
+ import { assertValidTreeName, computePrimaryFingerprint, getTreeMarkerPath, getTreesDir, readTreeMarker, withTreeLifecycleLock, } from '../core/tree-store.js';
30
+ import { GeneralError, InvalidArgumentError } from '../errors/base.js';
29
31
  import { BuildError } from '../errors/build.js';
32
+ import { toError } from '../utils/errors.js';
30
33
  import { pathExists } from '../utils/fs.js';
31
34
  import { info, intro, outro, setMachineOutputMode, setStdoutSealed, success, warn, } from '../utils/logger.js';
35
+ import { emitMachineError, MACHINE_OUTPUT_SCHEMA_VERSION } from '../utils/machine-output.js';
32
36
  import { addWaitLockOption, resolveWaitLockSeconds } from '../utils/options.js';
33
37
  function assertPosix() {
34
38
  if (process.platform === 'win32') {
@@ -100,7 +104,7 @@ export async function treeCreateCommand(projectRoot, name, options = {}) {
100
104
  // A marker we cannot parse still means "something claims this is a tree", so
101
105
  // it must block nesting too — otherwise a corrupt marker is a licence to
102
106
  // clone a snapshot into itself.
103
- const markerState = await readTreeMarkerState(projectRoot);
107
+ const markerState = await readTreeMarker(projectRoot);
104
108
  if (markerState.kind !== 'absent') {
105
109
  throw new GeneralError(markerState.kind === 'valid'
106
110
  ? 'Verification trees cannot be nested: run tree create in the primary tree.'
@@ -113,7 +117,6 @@ export async function treeCreateCommand(projectRoot, name, options = {}) {
113
117
  if (await pathExists(treeRoot)) {
114
118
  throw new GeneralError(`A tree named "${name}" already exists. Remove it first (fireforge tree remove ${name}) — refresh is remove + create.`);
115
119
  }
116
- const { mkdir } = await import('node:fs/promises');
117
120
  await mkdir(treesDir, { recursive: true });
118
121
  const capability = await detectCowSupport(treesDir);
119
122
  if (capability === 'none' && options.forceCopy !== true) {
@@ -162,7 +165,6 @@ export async function treeCreateCommand(projectRoot, name, options = {}) {
162
165
  : await clone();
163
166
  }
164
167
  catch (error) {
165
- const { rm } = await import('node:fs/promises');
166
168
  await rm(treeRoot, { recursive: true, force: true });
167
169
  throw error;
168
170
  }
@@ -184,9 +186,21 @@ export async function treeListCommand(projectRoot, options = {}) {
184
186
  // any diagnostic — including a listTrees failure rendered later by
185
187
  // withErrorHandling, which also resets the mode — routes to stderr.
186
188
  setMachineOutputMode(true);
187
- const trees = await listTrees(projectRoot);
188
- process.stdout.write(`${JSON.stringify({ schemaVersion: 1, trees }, null, 2)}\n`);
189
- setMachineOutputMode(false);
189
+ try {
190
+ const trees = await listTrees(projectRoot);
191
+ process.stdout.write(`${JSON.stringify({ schemaVersion: MACHINE_OUTPUT_SCHEMA_VERSION, trees }, null, 2)}\n`);
192
+ // Payload written: stdout is now spoken for. See docs/machine-output.md.
193
+ setStdoutSealed(true);
194
+ }
195
+ catch (error) {
196
+ // Same failure envelope `status --json` emits. Without it a scripted
197
+ // consumer got a parseable refusal from one command and a bare
198
+ // non-zero exit from the other. See docs/machine-output.md.
199
+ emitMachineError('tree-list-failed', toError(error).message);
200
+ }
201
+ finally {
202
+ setMachineOutputMode(false);
203
+ }
190
204
  return;
191
205
  }
192
206
  const trees = await listTrees(projectRoot);
@@ -206,7 +220,7 @@ export async function treeRemoveCommand(projectRoot, name, options = {}) {
206
220
  assertPosix();
207
221
  intro('FireForge Tree Remove');
208
222
  if (options.all !== true && name === undefined) {
209
- throw new GeneralError('Pass a tree name, or --all to remove every tree.');
223
+ throw new InvalidArgumentError('Pass a tree name, or --all to remove every tree.', '--all');
210
224
  }
211
225
  await withTreeLifecycleLock(projectRoot, async () => {
212
226
  const targets = options.all === true ? (await listTrees(projectRoot)).map((t) => t.name) : [name ?? ''];
@@ -226,7 +240,7 @@ async function treeExecCommand(projectRoot, name, args) {
226
240
  assertPosix();
227
241
  assertValidTreeName(name);
228
242
  const treeRoot = join(getTreesDir(projectRoot), name);
229
- const state = await readTreeMarkerState(treeRoot);
243
+ const state = await readTreeMarker(treeRoot);
230
244
  if (state.kind === 'corrupt') {
231
245
  // Collapsing corrupt into "no such tree" misdiagnoses: the directory IS
232
246
  // there, claiming to be a tree, and "no tree named X" points the operator
@@ -237,6 +251,9 @@ async function treeExecCommand(projectRoot, name, args) {
237
251
  if (state.kind === 'absent') {
238
252
  throw new GeneralError(`No verification tree named "${name}". List trees with: fireforge tree list`);
239
253
  }
254
+ if (state.kind === 'unsupported') {
255
+ throw new GeneralError(`Cannot inspect tree "${name}": ${state.reason}`);
256
+ }
240
257
  const marker = state.marker;
241
258
  const current = await computePrimaryFingerprint(projectRoot);
242
259
  if (marker.engineHead !== current.engineHead) {
@@ -263,14 +280,13 @@ async function treeExecCommand(projectRoot, name, args) {
263
280
  resolvePromise(code ?? 1);
264
281
  });
265
282
  }).finally(() => {
266
- // With `stdio: 'inherit'` the child owned stdout — including
267
- // any FIREFORGE-VERDICT line it emitted as its LAST stdout write. From
268
- // here on the parent must not write stdout again, or its own refusal
269
- // text (the GeneralError below rendered by withErrorHandling) would
270
- // print AFTER the child's verdict and break the "verdict is the run's
271
- // last stdout line" contract at the tree-exec boundary. Sealing routes
272
- // the parent's remaining output to stderr; withErrorHandling's finally
273
- // clears the seal.
283
+ // With `stdio: 'inherit'` the child owned stdout — including any
284
+ // FIREFORGE-VERDICT line it emitted as its LAST stdout write. From here
285
+ // on the parent must not write stdout again, or its own refusal text
286
+ // (the GeneralError below, rendered by withErrorHandling) prints AFTER
287
+ // the child's verdict and breaks the "verdict is the run's last stdout
288
+ // line" contract at the tree-exec boundary. Sealing routes the parent's
289
+ // remaining output to stderr; withErrorHandling's finally clears it.
274
290
  setStdoutSealed(true);
275
291
  });
276
292
  if (exitCode !== 0) {
@@ -1,18 +1,16 @@
1
1
  /**
2
- * `fireforge typecheck` — whole-project TypeScript type checking
3
- * driven by user-supplied jsconfig.json paths.
2
+ * `fireforge typecheck` — whole-project TypeScript type checking driven by
3
+ * user-supplied jsconfig.json paths.
4
4
  *
5
- * Distinct from `patchLint.checkJs`: that pass is patch-hygiene
6
- * (scoped to patch-owned `.sys.mjs`, run automatically by
7
- * `fireforge lint`); this command is CI-grade — it runs whole
8
- * projects with the user's own compiler options and is intended as
9
- * a CI gate. The two share their Firefox-globals shim and the same
10
- * suppressed-diagnostic set so a file that lints clean cannot fail
11
- * typecheck for a reason the operator could not have inferred from
12
- * the docs.
5
+ * Distinct from `patchLint.checkJs`: that pass is patch-hygiene (scoped to
6
+ * patch-owned `.sys.mjs`, run automatically by `fireforge lint`); this
7
+ * command is CI-grade — it runs whole projects with the user's own compiler
8
+ * options. The two share their Firefox-globals shim and the same
9
+ * suppressed-diagnostic set so a file that lints clean cannot fail typecheck
10
+ * for a reason the operator could not have inferred from the docs.
13
11
  *
14
- * Exits non-zero on any error-severity diagnostic. Warnings print
15
- * but do not fail. Designed for CI use.
12
+ * Exits non-zero on any error-severity diagnostic. Warnings print but do not
13
+ * fail.
16
14
  */
17
15
  import { Command } from 'commander';
18
16
  import type { CommandContext } from '../types/cli.js';