@hominis/fireforge 0.43.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (470) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/README.md +10 -1
  3. package/dist/bin/fireforge.d.ts +4 -9
  4. package/dist/bin/fireforge.js +42 -40
  5. package/dist/build-info.json +4 -4
  6. package/dist/src/cli.js +74 -8
  7. package/dist/src/commands/bootstrap.js +17 -20
  8. package/dist/src/commands/build.js +25 -32
  9. package/dist/src/commands/config.js +34 -41
  10. package/dist/src/commands/discard.js +8 -9
  11. package/dist/src/commands/doctor-check-core.d.ts +4 -18
  12. package/dist/src/commands/doctor-check-core.js +8 -35
  13. package/dist/src/commands/doctor-external-toolchains.js +2 -2
  14. package/dist/src/commands/doctor-furnace-config-sync.d.ts +18 -0
  15. package/dist/src/commands/{doctor-furnace-manifest-sync.js → doctor-furnace-config-sync.js} +27 -26
  16. package/dist/src/commands/doctor-furnace-jar.d.ts +7 -9
  17. package/dist/src/commands/doctor-furnace-jar.js +7 -9
  18. package/dist/src/commands/doctor-furnace.js +21 -24
  19. package/dist/src/commands/doctor-orphaned-harness.d.ts +16 -20
  20. package/dist/src/commands/doctor-orphaned-harness.js +18 -44
  21. package/dist/src/commands/doctor-post-rebase-audit.d.ts +2 -0
  22. package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
  23. package/dist/src/commands/doctor-working-tree.d.ts +10 -12
  24. package/dist/src/commands/doctor-working-tree.js +11 -18
  25. package/dist/src/commands/doctor.js +35 -53
  26. package/dist/src/commands/download.js +32 -48
  27. package/dist/src/commands/export-all.js +29 -33
  28. package/dist/src/commands/export-flow.js +6 -6
  29. package/dist/src/commands/export-placement-conflicts.js +10 -9
  30. package/dist/src/commands/export-placement-gate.js +5 -5
  31. package/dist/src/commands/export-shared.d.ts +30 -33
  32. package/dist/src/commands/export-shared.js +45 -43
  33. package/dist/src/commands/export.js +21 -27
  34. package/dist/src/commands/furnace/apply.js +14 -23
  35. package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
  36. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
  37. package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
  38. package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
  39. package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
  40. package/dist/src/commands/furnace/chrome-doc.js +31 -41
  41. package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
  42. package/dist/src/commands/furnace/create-browser-test.js +8 -8
  43. package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
  44. package/dist/src/commands/furnace/create-mochikit.js +8 -9
  45. package/dist/src/commands/furnace/create-readback.d.ts +12 -13
  46. package/dist/src/commands/furnace/create-readback.js +12 -13
  47. package/dist/src/commands/furnace/create-templates.d.ts +22 -28
  48. package/dist/src/commands/furnace/create-templates.js +35 -30
  49. package/dist/src/commands/furnace/create-xpcshell.js +2 -2
  50. package/dist/src/commands/furnace/create.js +33 -23
  51. package/dist/src/commands/furnace/deploy.js +16 -24
  52. package/dist/src/commands/furnace/diff.js +9 -9
  53. package/dist/src/commands/furnace/init.js +33 -36
  54. package/dist/src/commands/furnace/override.js +23 -25
  55. package/dist/src/commands/furnace/preview.js +47 -67
  56. package/dist/src/commands/furnace/refresh.js +12 -16
  57. package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
  58. package/dist/src/commands/furnace/remove-mutations.js +9 -16
  59. package/dist/src/commands/furnace/remove-state.js +1 -1
  60. package/dist/src/commands/furnace/remove.js +33 -38
  61. package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
  62. package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
  63. package/dist/src/commands/furnace/rename.js +59 -69
  64. package/dist/src/commands/furnace/scan.js +19 -33
  65. package/dist/src/commands/furnace/status.js +12 -9
  66. package/dist/src/commands/furnace/sync.js +9 -16
  67. package/dist/src/commands/furnace/validate.js +20 -20
  68. package/dist/src/commands/import.js +42 -35
  69. package/dist/src/commands/lint-per-patch.js +15 -10
  70. package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
  71. package/dist/src/commands/lint-per-run-checkjs.js +13 -13
  72. package/dist/src/commands/lint-report.js +6 -7
  73. package/dist/src/commands/lint.d.ts +15 -16
  74. package/dist/src/commands/lint.js +86 -102
  75. package/dist/src/commands/package.js +6 -11
  76. package/dist/src/commands/patch/compact.js +1 -1
  77. package/dist/src/commands/patch/delete.js +24 -33
  78. package/dist/src/commands/patch/index.d.ts +5 -6
  79. package/dist/src/commands/patch/index.js +10 -12
  80. package/dist/src/commands/patch/lint-ignore.js +14 -13
  81. package/dist/src/commands/patch/move-files-create.d.ts +6 -6
  82. package/dist/src/commands/patch/move-files-create.js +8 -8
  83. package/dist/src/commands/patch/move-files-into.d.ts +6 -6
  84. package/dist/src/commands/patch/move-files-into.js +8 -8
  85. package/dist/src/commands/patch/patch-context.d.ts +2 -3
  86. package/dist/src/commands/patch/patch-context.js +2 -3
  87. package/dist/src/commands/patch/rename.d.ts +10 -11
  88. package/dist/src/commands/patch/rename.js +11 -12
  89. package/dist/src/commands/patch/reorder.js +8 -9
  90. package/dist/src/commands/patch/split-plan.js +1 -1
  91. package/dist/src/commands/patch/split.d.ts +8 -10
  92. package/dist/src/commands/patch/split.js +9 -11
  93. package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
  94. package/dist/src/commands/patch/staged-dependency.js +7 -3
  95. package/dist/src/commands/patch/tier.d.ts +9 -9
  96. package/dist/src/commands/patch/tier.js +18 -15
  97. package/dist/src/commands/re-export-adjacent.js +9 -8
  98. package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
  99. package/dist/src/commands/re-export-bulk-scan.js +22 -21
  100. package/dist/src/commands/re-export-drift.js +10 -10
  101. package/dist/src/commands/re-export-files.d.ts +6 -7
  102. package/dist/src/commands/re-export-files.js +34 -44
  103. package/dist/src/commands/re-export-lint.d.ts +10 -13
  104. package/dist/src/commands/re-export-lint.js +16 -14
  105. package/dist/src/commands/re-export-options.d.ts +6 -6
  106. package/dist/src/commands/re-export-options.js +6 -6
  107. package/dist/src/commands/re-export-register.d.ts +4 -10
  108. package/dist/src/commands/re-export-register.js +5 -11
  109. package/dist/src/commands/re-export-scan.js +1 -1
  110. package/dist/src/commands/re-export-single.d.ts +3 -3
  111. package/dist/src/commands/re-export-single.js +33 -25
  112. package/dist/src/commands/re-export.js +13 -21
  113. package/dist/src/commands/rebase/abort.js +3 -3
  114. package/dist/src/commands/rebase/confirm.js +2 -1
  115. package/dist/src/commands/rebase/continue.d.ts +1 -1
  116. package/dist/src/commands/rebase/continue.js +14 -9
  117. package/dist/src/commands/rebase/index.js +11 -11
  118. package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
  119. package/dist/src/commands/rebase/patch-loop.js +60 -39
  120. package/dist/src/commands/rebase/summary.js +1 -1
  121. package/dist/src/commands/register.js +5 -7
  122. package/dist/src/commands/reset.js +2 -1
  123. package/dist/src/commands/resolve.d.ts +16 -13
  124. package/dist/src/commands/resolve.js +30 -33
  125. package/dist/src/commands/run.js +33 -35
  126. package/dist/src/commands/setup-support.js +15 -19
  127. package/dist/src/commands/setup.js +13 -9
  128. package/dist/src/commands/status-check.d.ts +6 -6
  129. package/dist/src/commands/status-json.d.ts +6 -6
  130. package/dist/src/commands/status-json.js +5 -0
  131. package/dist/src/commands/status-output.d.ts +5 -5
  132. package/dist/src/commands/status-output.js +6 -6
  133. package/dist/src/commands/status-ownership.d.ts +16 -14
  134. package/dist/src/commands/status-ownership.js +16 -14
  135. package/dist/src/commands/status.js +42 -34
  136. package/dist/src/commands/test-build-phase.js +26 -13
  137. package/dist/src/commands/test-diagnose.js +44 -44
  138. package/dist/src/commands/test-register.js +1 -0
  139. package/dist/src/commands/test-run.d.ts +14 -16
  140. package/dist/src/commands/test-run.js +21 -13
  141. package/dist/src/commands/test-stale-gate.d.ts +15 -16
  142. package/dist/src/commands/test-stale-gate.js +15 -16
  143. package/dist/src/commands/test-verdict.d.ts +20 -3
  144. package/dist/src/commands/test-verdict.js +30 -0
  145. package/dist/src/commands/test.js +83 -76
  146. package/dist/src/commands/token-coverage.js +3 -4
  147. package/dist/src/commands/token.js +16 -18
  148. package/dist/src/commands/tree.js +47 -31
  149. package/dist/src/commands/typecheck.d.ts +10 -12
  150. package/dist/src/commands/typecheck.js +24 -27
  151. package/dist/src/commands/verify.js +13 -15
  152. package/dist/src/commands/watch.js +21 -23
  153. package/dist/src/commands/wire.js +52 -62
  154. package/dist/src/core/ast-utils.d.ts +8 -7
  155. package/dist/src/core/ast-utils.js +8 -7
  156. package/dist/src/core/branding.d.ts +9 -14
  157. package/dist/src/core/branding.js +7 -11
  158. package/dist/src/core/browser-wire.d.ts +1 -1
  159. package/dist/src/core/browser-wire.js +14 -19
  160. package/dist/src/core/build-audit-platform.js +25 -33
  161. package/dist/src/core/build-audit-registration.js +17 -25
  162. package/dist/src/core/build-audit-resolve.d.ts +15 -17
  163. package/dist/src/core/build-audit-resolve.js +27 -33
  164. package/dist/src/core/build-audit-transforms.js +18 -19
  165. package/dist/src/core/build-audit.js +28 -42
  166. package/dist/src/core/build-baseline-types.d.ts +29 -35
  167. package/dist/src/core/build-baseline.d.ts +5 -4
  168. package/dist/src/core/build-baseline.js +14 -14
  169. package/dist/src/core/build-overwrite-guard.d.ts +7 -14
  170. package/dist/src/core/build-overwrite-guard.js +62 -19
  171. package/dist/src/core/build-prepare.d.ts +2 -2
  172. package/dist/src/core/build-prepare.js +8 -9
  173. package/dist/src/core/config-state.js +12 -4
  174. package/dist/src/core/config-validate.js +8 -5
  175. package/dist/src/core/config.d.ts +5 -8
  176. package/dist/src/core/config.js +7 -16
  177. package/dist/src/core/coverage-extend.d.ts +15 -13
  178. package/dist/src/core/coverage-extend.js +15 -13
  179. package/dist/src/core/destructive.d.ts +31 -9
  180. package/dist/src/core/destructive.js +29 -26
  181. package/dist/src/core/diff-hunks.js +10 -12
  182. package/dist/src/core/discard-baseline.d.ts +8 -8
  183. package/dist/src/core/discard-baseline.js +16 -15
  184. package/dist/src/core/display-state.d.ts +12 -14
  185. package/dist/src/core/display-state.js +12 -14
  186. package/dist/src/core/engine-changes.d.ts +2 -4
  187. package/dist/src/core/engine-changes.js +2 -4
  188. package/dist/src/core/engine-precondition.d.ts +28 -13
  189. package/dist/src/core/engine-precondition.js +25 -10
  190. package/dist/src/core/engine-session-lock.d.ts +47 -21
  191. package/dist/src/core/engine-session-lock.js +168 -33
  192. package/dist/src/core/export-moved-code-shape.d.ts +10 -10
  193. package/dist/src/core/export-moved-code-shape.js +10 -10
  194. package/dist/src/core/file-lock.d.ts +62 -5
  195. package/dist/src/core/file-lock.js +196 -59
  196. package/dist/src/core/firefox-cache.js +1 -3
  197. package/dist/src/core/firefox-extract.js +10 -14
  198. package/dist/src/core/firefox.d.ts +7 -5
  199. package/dist/src/core/firefox.js +4 -5
  200. package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
  201. package/dist/src/core/furnace-apply-dry-run.js +3 -2
  202. package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
  203. package/dist/src/core/furnace-apply-ftl.js +12 -12
  204. package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
  205. package/dist/src/core/furnace-apply-helpers.js +19 -16
  206. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
  207. package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
  208. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
  209. package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
  210. package/dist/src/core/furnace-apply.d.ts +5 -8
  211. package/dist/src/core/furnace-apply.js +19 -24
  212. package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
  213. package/dist/src/core/furnace-checksum-utils.js +16 -0
  214. package/dist/src/core/furnace-config.d.ts +21 -30
  215. package/dist/src/core/furnace-config.js +32 -54
  216. package/dist/src/core/furnace-constants.d.ts +8 -0
  217. package/dist/src/core/furnace-constants.js +10 -2
  218. package/dist/src/core/furnace-css-fragments.d.ts +5 -5
  219. package/dist/src/core/furnace-css-fragments.js +5 -5
  220. package/dist/src/core/furnace-jsconfig.d.ts +11 -12
  221. package/dist/src/core/furnace-jsconfig.js +11 -12
  222. package/dist/src/core/furnace-marker.d.ts +7 -8
  223. package/dist/src/core/furnace-marker.js +7 -8
  224. package/dist/src/core/furnace-operation.d.ts +31 -0
  225. package/dist/src/core/furnace-operation.js +54 -17
  226. package/dist/src/core/furnace-precondition.d.ts +61 -0
  227. package/dist/src/core/furnace-precondition.js +67 -0
  228. package/dist/src/core/furnace-registration-remove.d.ts +5 -6
  229. package/dist/src/core/furnace-registration-remove.js +6 -7
  230. package/dist/src/core/furnace-registration-validate.d.ts +13 -0
  231. package/dist/src/core/furnace-registration-validate.js +20 -1
  232. package/dist/src/core/furnace-registration.d.ts +5 -6
  233. package/dist/src/core/furnace-registration.js +10 -12
  234. package/dist/src/core/furnace-rollback.js +7 -7
  235. package/dist/src/core/furnace-scanner.d.ts +1 -0
  236. package/dist/src/core/furnace-scanner.js +1 -2
  237. package/dist/src/core/furnace-stale-export.d.ts +10 -10
  238. package/dist/src/core/furnace-stale-export.js +10 -10
  239. package/dist/src/core/furnace-state-persist.d.ts +6 -8
  240. package/dist/src/core/furnace-state-persist.js +6 -8
  241. package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
  242. package/dist/src/core/furnace-validate-accessibility.js +310 -1
  243. package/dist/src/core/furnace-validate-compatibility.js +141 -1
  244. package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
  245. package/dist/src/core/furnace-validate-helpers.js +0 -491
  246. package/dist/src/core/furnace-validate-registration.d.ts +7 -0
  247. package/dist/src/core/furnace-validate-registration.js +115 -41
  248. package/dist/src/core/furnace-validate.d.ts +16 -0
  249. package/dist/src/core/furnace-validate.js +188 -145
  250. package/dist/src/core/furnace-version-drift.d.ts +10 -11
  251. package/dist/src/core/furnace-version-drift.js +10 -11
  252. package/dist/src/core/git-base.d.ts +7 -9
  253. package/dist/src/core/git-base.js +7 -9
  254. package/dist/src/core/git-diff.d.ts +20 -9
  255. package/dist/src/core/git-diff.js +148 -63
  256. package/dist/src/core/git-file-ops.d.ts +19 -19
  257. package/dist/src/core/git-file-ops.js +19 -19
  258. package/dist/src/core/git-readonly-index.d.ts +53 -19
  259. package/dist/src/core/git-readonly-index.js +75 -31
  260. package/dist/src/core/git-status.d.ts +5 -6
  261. package/dist/src/core/git-status.js +5 -6
  262. package/dist/src/core/git.d.ts +9 -10
  263. package/dist/src/core/git.js +54 -70
  264. package/dist/src/core/license-headers.d.ts +17 -23
  265. package/dist/src/core/license-headers.js +17 -23
  266. package/dist/src/core/mach-build-artifacts.d.ts +9 -9
  267. package/dist/src/core/mach-build-artifacts.js +57 -19
  268. package/dist/src/core/mach-error-hints.js +42 -29
  269. package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
  270. package/dist/src/core/mach-known-noise-filter.js +18 -20
  271. package/dist/src/core/mach-mozconfig.d.ts +14 -0
  272. package/dist/src/core/mach-mozconfig.js +30 -0
  273. package/dist/src/core/mach-resource-shim.d.ts +43 -58
  274. package/dist/src/core/mach-resource-shim.js +50 -67
  275. package/dist/src/core/mach.d.ts +37 -41
  276. package/dist/src/core/mach.js +50 -51
  277. package/dist/src/core/marionette-port.d.ts +36 -1
  278. package/dist/src/core/marionette-port.js +96 -31
  279. package/dist/src/core/marionette-preflight.d.ts +8 -12
  280. package/dist/src/core/marionette-preflight.js +86 -52
  281. package/dist/src/core/mochitest-server-port.d.ts +42 -0
  282. package/dist/src/core/mochitest-server-port.js +119 -0
  283. package/dist/src/core/module-resolution-preflight.d.ts +9 -11
  284. package/dist/src/core/module-resolution-preflight.js +9 -11
  285. package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
  286. package/dist/src/core/moz-manifest-helpers.js +163 -0
  287. package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
  288. package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
  289. package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
  290. package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
  291. package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
  292. package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
  293. package/dist/src/core/ownership-table.d.ts +14 -16
  294. package/dist/src/core/ownership-table.js +14 -16
  295. package/dist/src/core/parser-fallback.d.ts +15 -1
  296. package/dist/src/core/parser-fallback.js +22 -3
  297. package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
  298. package/dist/src/core/patch-apply-fuzz.js +15 -18
  299. package/dist/src/core/patch-apply.d.ts +13 -34
  300. package/dist/src/core/patch-apply.js +16 -76
  301. package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
  302. package/dist/src/core/patch-artifact-normalize.js +13 -15
  303. package/dist/src/core/patch-export-metadata.d.ts +12 -2
  304. package/dist/src/core/patch-export-metadata.js +13 -4
  305. package/dist/src/core/patch-export-update.d.ts +13 -1
  306. package/dist/src/core/patch-export-update.js +25 -8
  307. package/dist/src/core/patch-export.d.ts +15 -8
  308. package/dist/src/core/patch-export.js +10 -11
  309. package/dist/src/core/patch-files.d.ts +5 -6
  310. package/dist/src/core/patch-files.js +5 -6
  311. package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
  312. package/dist/src/core/patch-identifier-suggest.js +14 -19
  313. package/dist/src/core/patch-lint-binary.d.ts +51 -0
  314. package/dist/src/core/patch-lint-binary.js +39 -0
  315. package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
  316. package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
  317. package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
  318. package/dist/src/core/patch-lint-checkjs.js +16 -16
  319. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
  320. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
  321. package/dist/src/core/patch-lint-creators.d.ts +8 -8
  322. package/dist/src/core/patch-lint-creators.js +8 -8
  323. package/dist/src/core/patch-lint-cross.d.ts +17 -20
  324. package/dist/src/core/patch-lint-cross.js +16 -16
  325. package/dist/src/core/patch-lint-css.d.ts +3 -5
  326. package/dist/src/core/patch-lint-css.js +17 -22
  327. package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
  328. package/dist/src/core/patch-lint-diff-tag.js +14 -16
  329. package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
  330. package/dist/src/core/patch-lint-jsdoc.js +26 -19
  331. package/dist/src/core/patch-lint-mozbuild.js +20 -20
  332. package/dist/src/core/patch-lint-observer.d.ts +7 -7
  333. package/dist/src/core/patch-lint-observer.js +7 -7
  334. package/dist/src/core/patch-lint.d.ts +38 -29
  335. package/dist/src/core/patch-lint.js +89 -121
  336. package/dist/src/core/patch-lock.d.ts +1 -1
  337. package/dist/src/core/patch-lock.js +45 -9
  338. package/dist/src/core/patch-manifest-consistency.d.ts +2 -1
  339. package/dist/src/core/patch-manifest-consistency.js +25 -27
  340. package/dist/src/core/patch-manifest-io.js +14 -18
  341. package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
  342. package/dist/src/core/patch-manifest-resolve.js +9 -12
  343. package/dist/src/core/patch-manifest-validate.d.ts +6 -1
  344. package/dist/src/core/patch-manifest-validate.js +25 -13
  345. package/dist/src/core/patch-parse.d.ts +24 -13
  346. package/dist/src/core/patch-parse.js +19 -14
  347. package/dist/src/core/patch-policy.d.ts +2 -2
  348. package/dist/src/core/patch-policy.js +2 -2
  349. package/dist/src/core/patch-registration-refs.d.ts +7 -8
  350. package/dist/src/core/patch-registration-refs.js +9 -9
  351. package/dist/src/core/process-cpu-time.d.ts +12 -0
  352. package/dist/src/core/process-cpu-time.js +52 -0
  353. package/dist/src/core/rebase-session.d.ts +38 -26
  354. package/dist/src/core/rebase-session.js +55 -28
  355. package/dist/src/core/register-browser-content.js +9 -74
  356. package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
  357. package/dist/src/core/register-module.js +10 -43
  358. package/dist/src/core/register-scaffold.d.ts +8 -8
  359. package/dist/src/core/register-scaffold.js +10 -10
  360. package/dist/src/core/register-shared-css.d.ts +9 -12
  361. package/dist/src/core/register-shared-css.js +17 -87
  362. package/dist/src/core/register-xpcshell-test.d.ts +6 -7
  363. package/dist/src/core/register-xpcshell-test.js +7 -8
  364. package/dist/src/core/shared-ftl.js +12 -4
  365. package/dist/src/core/state-file.d.ts +45 -0
  366. package/dist/src/core/state-file.js +34 -0
  367. package/dist/src/core/status-binary.d.ts +18 -11
  368. package/dist/src/core/status-binary.js +19 -12
  369. package/dist/src/core/status-classify.d.ts +30 -36
  370. package/dist/src/core/status-classify.js +21 -26
  371. package/dist/src/core/test-changed-prefs.d.ts +30 -0
  372. package/dist/src/core/test-changed-prefs.js +78 -0
  373. package/dist/src/core/test-harness-crash.d.ts +25 -63
  374. package/dist/src/core/test-harness-crash.js +121 -142
  375. package/dist/src/core/test-harness-signature.d.ts +18 -0
  376. package/dist/src/core/test-harness-signature.js +13 -0
  377. package/dist/src/core/test-path-scope.d.ts +12 -15
  378. package/dist/src/core/test-path-scope.js +15 -18
  379. package/dist/src/core/test-stale-check.d.ts +17 -17
  380. package/dist/src/core/test-stale-check.js +50 -43
  381. package/dist/src/core/test-stall-triage.d.ts +51 -0
  382. package/dist/src/core/test-stall-triage.js +126 -0
  383. package/dist/src/core/token-category.js +5 -7
  384. package/dist/src/core/token-coverage.js +9 -9
  385. package/dist/src/core/token-dark-mode.d.ts +9 -11
  386. package/dist/src/core/token-dark-mode.js +9 -11
  387. package/dist/src/core/token-docs.d.ts +2 -0
  388. package/dist/src/core/token-docs.js +2 -0
  389. package/dist/src/core/token-manager.d.ts +10 -2
  390. package/dist/src/core/token-manager.js +17 -8
  391. package/dist/src/core/token-scaffold.d.ts +7 -7
  392. package/dist/src/core/token-scaffold.js +7 -7
  393. package/dist/src/core/token-variant.d.ts +4 -4
  394. package/dist/src/core/token-variant.js +4 -4
  395. package/dist/src/core/toolchain-preflight.d.ts +20 -21
  396. package/dist/src/core/toolchain-preflight.js +28 -31
  397. package/dist/src/core/tree-guard.js +12 -2
  398. package/dist/src/core/tree-store.d.ts +41 -27
  399. package/dist/src/core/tree-store.js +63 -42
  400. package/dist/src/core/typecheck-shim.d.ts +23 -24
  401. package/dist/src/core/typecheck-shim.js +23 -24
  402. package/dist/src/core/typecheck.js +8 -8
  403. package/dist/src/core/wire-destroy.d.ts +3 -3
  404. package/dist/src/core/wire-destroy.js +7 -15
  405. package/dist/src/core/wire-dom-fragment.d.ts +9 -17
  406. package/dist/src/core/wire-dom-fragment.js +11 -44
  407. package/dist/src/core/wire-init.d.ts +2 -3
  408. package/dist/src/core/wire-init.js +8 -19
  409. package/dist/src/core/wire-subscript.d.ts +2 -3
  410. package/dist/src/core/wire-subscript.js +4 -13
  411. package/dist/src/core/wire-utils.d.ts +7 -8
  412. package/dist/src/core/wire-utils.js +7 -8
  413. package/dist/src/core/xpcshell-appdir.d.ts +38 -45
  414. package/dist/src/core/xpcshell-appdir.js +38 -45
  415. package/dist/src/errors/base.d.ts +37 -16
  416. package/dist/src/errors/base.js +37 -16
  417. package/dist/src/errors/build.d.ts +13 -1
  418. package/dist/src/errors/build.js +14 -2
  419. package/dist/src/errors/codes.d.ts +16 -0
  420. package/dist/src/errors/codes.js +16 -0
  421. package/dist/src/errors/furnace.d.ts +4 -4
  422. package/dist/src/errors/git.d.ts +6 -10
  423. package/dist/src/errors/git.js +6 -10
  424. package/dist/src/errors/patch.d.ts +7 -7
  425. package/dist/src/errors/patch.js +7 -7
  426. package/dist/src/errors/rebase.d.ts +4 -4
  427. package/dist/src/errors/rebase.js +4 -4
  428. package/dist/src/index.d.ts +7 -1
  429. package/dist/src/index.js +5 -0
  430. package/dist/src/types/commands/index.d.ts +2 -2
  431. package/dist/src/types/commands/options.d.ts +88 -70
  432. package/dist/src/types/commands/patches.d.ts +24 -34
  433. package/dist/src/types/commands/project.d.ts +5 -34
  434. package/dist/src/types/config.d.ts +25 -23
  435. package/dist/src/types/config.js +6 -1
  436. package/dist/src/types/furnace.d.ts +37 -13
  437. package/dist/src/types/furnace.js +19 -1
  438. package/dist/src/types/index.d.ts +3 -3
  439. package/dist/src/utils/build-info.d.ts +7 -8
  440. package/dist/src/utils/build-info.js +12 -11
  441. package/dist/src/utils/errors.d.ts +6 -8
  442. package/dist/src/utils/errors.js +6 -8
  443. package/dist/src/utils/logger.d.ts +27 -10
  444. package/dist/src/utils/logger.js +28 -16
  445. package/dist/src/utils/machine-output.d.ts +12 -0
  446. package/dist/src/utils/machine-output.js +33 -0
  447. package/dist/src/utils/options.d.ts +31 -14
  448. package/dist/src/utils/options.js +86 -30
  449. package/dist/src/utils/paths.d.ts +10 -8
  450. package/dist/src/utils/paths.js +10 -8
  451. package/dist/src/utils/platform.d.ts +0 -25
  452. package/dist/src/utils/platform.js +1 -42
  453. package/dist/src/utils/process-group.d.ts +9 -4
  454. package/dist/src/utils/process-group.js +19 -5
  455. package/dist/src/utils/process.d.ts +42 -7
  456. package/dist/src/utils/process.js +44 -17
  457. package/dist/src/utils/ps-duration.d.ts +31 -0
  458. package/dist/src/utils/ps-duration.js +70 -0
  459. package/dist/src/utils/regex.d.ts +9 -10
  460. package/dist/src/utils/regex.js +9 -10
  461. package/dist/src/utils/validation.d.ts +58 -28
  462. package/dist/src/utils/validation.js +60 -33
  463. package/package.json +2 -2
  464. package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
  465. package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
  466. package/dist/src/core/manifest-helpers.d.ts +0 -38
  467. package/dist/src/core/manifest-helpers.js +0 -120
  468. /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
  469. /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
  470. /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
@@ -70,6 +70,7 @@ export async function readBuildBaseline(projectRoot) {
70
70
  * engine HEAD SHA (or an empty string when the engine has no HEAD yet) and
71
71
  * the current binaryName. Caller is responsible for only invoking this
72
72
  * after the build exit code was zero.
73
+ *
73
74
  * @param projectRoot - Root directory of the project
74
75
  * @param engineDir - Path to the engine directory
75
76
  * @param binaryName - Current `binaryName` from fireforge.json
@@ -85,10 +86,10 @@ export async function readBuildBaseline(projectRoot) {
85
86
  * @param staticComponentsHandling - `'auto'` (default) refreshes the
86
87
  * static-components anchor whenever the coverage claim is `'full'` or
87
88
  * absent. `'refresh'` records it even for a scoped coverage claim whose
88
- * implementation escalated to a full build. `'carry-forward'` always keeps the previous anchor: needed by
89
- * `--extend-coverage`, whose union can EVALUATE to `'full'`
90
- * while the build that produced it was still a scoped `mach build
91
- * faster` that did not rebake the compiled table.
89
+ * implementation escalated to a full build. `'carry-forward'` always
90
+ * keeps the previous anchor: needed by `--extend-coverage`, whose union
91
+ * can EVALUATE to `'full'` while the build that produced it was still a
92
+ * scoped `mach build faster` that did not rebake the compiled table.
92
93
  */
93
94
  export async function writeBuildBaseline(projectRoot, engineDir, binaryName, testPackagingCoverage, previousBaseline, recordedBy, staticComponentsHandling = 'auto') {
94
95
  let engineHeadSha = '';
@@ -128,17 +129,16 @@ export async function writeBuildBaseline(projectRoot, engineDir, binaryName, tes
128
129
  await writeJson(getBuildBaselinePath(projectRoot), baseline);
129
130
  }
130
131
  /**
131
- * Reads the current engine workdir and computes a SHA-256 fingerprint
132
- * for every packageable path that is either modified against HEAD or
133
- * untracked. The stale-build preflight (`checkStaleBuildForTest`)
134
- * compares the live fingerprint for each packageable-dirty file to
135
- * the baseline's entry — paths where the hash matches are "the build
136
- * already saw this exact content", paths where it differs (or that
137
- * are new since the baseline) are genuinely stale.
132
+ * Reads the current engine workdir and computes a SHA-256 fingerprint for
133
+ * every packageable path that is either modified against HEAD or untracked.
134
+ * The stale-build preflight (`checkStaleBuildForTest`) compares the live
135
+ * fingerprint for each packageable-dirty file to the baseline's entry: a
136
+ * matching hash means the build already saw this exact content, a differing
137
+ * or new one means the file is genuinely stale.
138
138
  *
139
- * Returns `undefined` on any git failure so a broken probe never
140
- * corrupts the on-disk baseline with `{}`; the stale-check then falls
141
- * back to the pre-0.16.0 "path-only" behavior on the next test run.
139
+ * Returns `undefined` on any git failure so a broken probe never corrupts
140
+ * the on-disk baseline with `{}`; the stale-check then falls back to a
141
+ * path-only comparison.
142
142
  */
143
143
  async function collectPackageableFingerprints(engineDir) {
144
144
  return collectDirtyFingerprints(engineDir, isPackageablePath, 'packageable fingerprint');
@@ -1,20 +1,13 @@
1
1
  /**
2
2
  * Loud guard against silently reverting UNEXPORTED engine drift.
3
3
  *
4
- * `build-prepare` rewrites engine files from FireForge-owned sources
5
- * before every build: the branding tree, every Furnace-managed component,
6
- * and `mozconfig`. On a multi-session checkout that is a silent
7
- * destructive write. The recorded incident: one session edited a file in
8
- * `engine/` while another held the engine lock; the lock-holder's build
9
- * rewrote the file back to its patch baseline; the editing session's later
10
- * `re-export --wait-lock` then captured a HYBRID file — and every gate
11
- * passed on the hybrid capture. Only a hand-run grep of the patch body
12
- * caught it.
13
- *
14
- * The `setupBranding` half of this class was closed in `31fdf744` (the
15
- * branding writer preserves unmanaged lines). The general shape — any
16
- * build-prepare overwrite destroying local drift that no patch records —
17
- * stayed silent, and silence is what made the hybrid capture possible.
4
+ * `build-prepare` rewrites engine files from FireForge-owned sources before
5
+ * every build: the branding tree, every Furnace-managed component, and
6
+ * `mozconfig`. On a multi-session checkout that is a silent destructive
7
+ * write — one session edits a file in `engine/` while another holds the
8
+ * engine lock, the lock-holder's build rewrites it back to its patch
9
+ * baseline, and the editing session's later `re-export --wait-lock`
10
+ * captures a HYBRID file that every gate then passes.
18
11
  *
19
12
  * This guard runs BEFORE the writes, classifies the engine's dirty files
20
13
  * with the same classifier `fireforge status` uses, and reports the ones
@@ -2,20 +2,13 @@
2
2
  /**
3
3
  * Loud guard against silently reverting UNEXPORTED engine drift.
4
4
  *
5
- * `build-prepare` rewrites engine files from FireForge-owned sources
6
- * before every build: the branding tree, every Furnace-managed component,
7
- * and `mozconfig`. On a multi-session checkout that is a silent
8
- * destructive write. The recorded incident: one session edited a file in
9
- * `engine/` while another held the engine lock; the lock-holder's build
10
- * rewrote the file back to its patch baseline; the editing session's later
11
- * `re-export --wait-lock` then captured a HYBRID file — and every gate
12
- * passed on the hybrid capture. Only a hand-run grep of the patch body
13
- * caught it.
14
- *
15
- * The `setupBranding` half of this class was closed in `31fdf744` (the
16
- * branding writer preserves unmanaged lines). The general shape — any
17
- * build-prepare overwrite destroying local drift that no patch records —
18
- * stayed silent, and silence is what made the hybrid capture possible.
5
+ * `build-prepare` rewrites engine files from FireForge-owned sources before
6
+ * every build: the branding tree, every Furnace-managed component, and
7
+ * `mozconfig`. On a multi-session checkout that is a silent destructive
8
+ * write — one session edits a file in `engine/` while another holds the
9
+ * engine lock, the lock-holder's build rewrites it back to its patch
10
+ * baseline, and the editing session's later `re-export --wait-lock`
11
+ * captures a HYBRID file that every gate then passes.
19
12
  *
20
13
  * This guard runs BEFORE the writes, classifies the engine's dirty files
21
14
  * with the same classifier `fireforge status` uses, and reports the ones
@@ -27,7 +20,7 @@ import { toError } from '../utils/errors.js';
27
20
  import { verbose } from '../utils/logger.js';
28
21
  import { getProjectPaths } from './config.js';
29
22
  import { collectFurnaceManagedPrefixes } from './furnace-config.js';
30
- import { getWorkingTreeStatus } from './git-status.js';
23
+ import { expandUntrackedDirectoryEntries, getWorkingTreeStatus } from './git-status.js';
31
24
  import { classifyFiles } from './status-classify.js';
32
25
  /**
33
26
  * Classifications whose on-disk content is explained by NEITHER a patch
@@ -39,6 +32,14 @@ import { classifyFiles } from './status-classify.js';
39
32
  * preserves unmanaged lines rather than overwriting them.
40
33
  */
41
34
  const AT_RISK_CLASSIFICATIONS = new Set(['unmanaged', 'patch-owned-drift', 'conflict']);
35
+ /**
36
+ * Every path prefix build-prepare rewrites from a FireForge-owned source:
37
+ * the branding tree plus every Furnace-managed prefix. (`mozconfig` is a
38
+ * single file, not a prefix, and is tested separately.)
39
+ */
40
+ function buildPrepareOwnedPrefixes(binaryName, furnacePrefixes) {
41
+ return [`browser/branding/${binaryName}/`, ...furnacePrefixes];
42
+ }
42
43
  /**
43
44
  * True when `file` sits under a path build-prepare rewrites from a
44
45
  * FireForge-owned source: the branding tree, a Furnace-managed prefix, or
@@ -47,14 +48,56 @@ const AT_RISK_CLASSIFICATIONS = new Set(['unmanaged', 'patch-owned-drift', 'conf
47
48
  function isOverwrittenByBuildPrepare(file, binaryName, furnacePrefixes) {
48
49
  if (file === 'mozconfig')
49
50
  return true;
50
- if (file.startsWith(`browser/branding/${binaryName}/`))
51
- return true;
52
- for (const prefix of furnacePrefixes) {
51
+ for (const prefix of buildPrepareOwnedPrefixes(binaryName, furnacePrefixes)) {
53
52
  if (file === prefix || file.startsWith(prefix))
54
53
  return true;
55
54
  }
56
55
  return false;
57
56
  }
57
+ /**
58
+ * Narrows the status entries to the ones build-prepare can overwrite,
59
+ * expanding collapsed untracked directories on the way.
60
+ *
61
+ * Git reports a WHOLLY untracked directory as a single `?? dir/` entry
62
+ * rather than listing the files under it. A directory entry has no content
63
+ * to compare, so `classifyFiles` matches it against neither a patch body nor
64
+ * the pristine baseline and buckets it `unmanaged` — an at-risk
65
+ * classification. Expanding here is what keeps this guard and
66
+ * `status --unmanaged`, which expands, agreeing by construction.
67
+ *
68
+ * Expansion is scoped to build-prepare-owned prefixes on both sides of the
69
+ * walk: a collapsed directory that merely CONTAINS an owned prefix
70
+ * (`components/` when `components/custom/` is Furnace-managed) is rewritten
71
+ * to the owned prefixes beneath it, so the enumeration never walks an
72
+ * unrelated subtree and never trips the per-directory expansion cap.
73
+ */
74
+ async function collectOverwrittenEntries(engineDir, status, binaryName, furnacePrefixes) {
75
+ const ownedPrefixes = buildPrepareOwnedPrefixes(binaryName, furnacePrefixes);
76
+ const candidates = [];
77
+ for (const entry of status) {
78
+ if (isOverwrittenByBuildPrepare(entry.file, binaryName, furnacePrefixes)) {
79
+ candidates.push(entry);
80
+ continue;
81
+ }
82
+ if (!entry.isUntracked || !entry.file.endsWith('/'))
83
+ continue;
84
+ // A collapsed untracked ancestor of an owned prefix: walk only the
85
+ // owned subtrees under it, never the whole directory.
86
+ for (const prefix of ownedPrefixes) {
87
+ if (prefix.startsWith(entry.file)) {
88
+ candidates.push({ ...entry, file: prefix });
89
+ }
90
+ }
91
+ }
92
+ if (candidates.length === 0)
93
+ return [];
94
+ const expanded = await expandUntrackedDirectoryEntries(engineDir, candidates);
95
+ // Re-test after expansion: an ancestor rewrite can only widen, and a
96
+ // directory that survived expansion (nothing untracked under it any
97
+ // more) carries no content to classify.
98
+ return expanded.filter((entry) => !entry.file.endsWith('/') &&
99
+ isOverwrittenByBuildPrepare(entry.file, binaryName, furnacePrefixes));
100
+ }
58
101
  /**
59
102
  * Finds engine files that build-prepare is about to overwrite and whose
60
103
  * current content is recorded nowhere.
@@ -73,7 +116,7 @@ export async function findUnexportedDriftAtRisk(projectRoot, config) {
73
116
  if (status.length === 0)
74
117
  return [];
75
118
  const furnacePrefixes = await collectFurnaceManagedPrefixes(projectRoot);
76
- const candidates = status.filter((entry) => isOverwrittenByBuildPrepare(entry.file, config.binaryName, furnacePrefixes));
119
+ const candidates = await collectOverwrittenEntries(paths.engine, status, config.binaryName, furnacePrefixes);
77
120
  if (candidates.length === 0)
78
121
  return [];
79
122
  const classified = await classifyFiles(candidates, paths.engine, paths.patches, config.binaryName, furnacePrefixes);
@@ -37,8 +37,8 @@ export declare function requiresFullBuildForIncrementalTest(path: string): boole
37
37
  * Describes an exit code in the shell's 128+N signal convention. Truncated
38
38
  * configure/build logs with a signal-shaped exit (e.g. 144 = 128+16, SIGURG
39
39
  * on macOS) are environmental interruptions, not compiler failures — naming
40
- * that in the failure text saves the operator a fruitless log hunt
41
- *. Returns undefined for codes <= 128 (regular failures) and
40
+ * that in the failure text saves the operator a fruitless log hunt.
41
+ * Returns undefined for codes <= 128 (regular failures) and
42
42
  * for codes past the conventional signal range, so callers append nothing.
43
43
  */
44
44
  export declare function describeSignalShapedExit(exitCode: number): string | undefined;
@@ -44,8 +44,8 @@ function extractMachConfigureError(result) {
44
44
  * Describes an exit code in the shell's 128+N signal convention. Truncated
45
45
  * configure/build logs with a signal-shaped exit (e.g. 144 = 128+16, SIGURG
46
46
  * on macOS) are environmental interruptions, not compiler failures — naming
47
- * that in the failure text saves the operator a fruitless log hunt
48
- *. Returns undefined for codes <= 128 (regular failures) and
47
+ * that in the failure text saves the operator a fruitless log hunt.
48
+ * Returns undefined for codes <= 128 (regular failures) and
49
49
  * for codes past the conventional signal range, so callers append nothing.
50
50
  */
51
51
  export function describeSignalShapedExit(exitCode) {
@@ -94,10 +94,10 @@ export function isBackendInvalidatingFile(path) {
94
94
  *
95
95
  * Every write `prepareBuildEnvironment` performs — branding, Furnace
96
96
  * components, `mozconfig` — rewrites engine files from a FireForge-owned
97
- * source. Content recorded in neither a patch body nor the pristine
98
- * baseline is destroyed by that, and on a multi-session checkout the
99
- * destruction is what lets a later re-export capture a half-reverted
100
- * hybrid that every gate then passes.
97
+ * source. Content recorded in neither a patch body nor the pristine baseline
98
+ * is destroyed by that, and on a multi-session checkout the destruction is
99
+ * what lets a later re-export capture a half-reverted hybrid that every gate
100
+ * then passes.
101
101
  *
102
102
  * @param projectRoot - Project root
103
103
  * @param config - Loaded FireForge config
@@ -190,9 +190,8 @@ export async function prepareBuildEnvironment(projectRoot, paths, config, option
190
190
  // project license through so `buildConfigureScriptContent` /
191
191
  // `buildBrandPropertiesContent` / `buildBrandFtlContent` stamp the
192
192
  // generated files with a matching SPDX header — otherwise `patch-lint`
193
- // flags them with `missing-license-header` on every subsequent export
194
- // when the project is not MPL-2.0 (the eval finding: a 0BSD-licensed
195
- // fork's first export failed `lint` on its own generated branding).
193
+ // flags them with `missing-license-header` on every subsequent export when
194
+ // the project is not MPL-2.0.
196
195
  const brandingConfig = {
197
196
  name: config.name,
198
197
  vendor: config.vendor,
@@ -3,10 +3,14 @@
3
3
  * Project state file management (.fireforge/state.json).
4
4
  */
5
5
  import { ConfigError } from '../errors/config.js';
6
+ import { BUILD_MODES } from '../types/config.js';
6
7
  import { toError } from '../utils/errors.js';
7
8
  import { pathExists, readJson, writeJson } from '../utils/fs.js';
8
9
  import { warn } from '../utils/logger.js';
9
10
  import { isObject, isString } from '../utils/validation.js';
11
+ import { makeEnumGuard } from '../utils/validation.js';
12
+ /** Narrows an arbitrary string to a {@link BuildMode}. */
13
+ const isBuildMode = makeEnumGuard(BUILD_MODES);
10
14
  import { getProjectPaths } from './config-paths.js';
11
15
  import { quarantineStateFile, withStateFileLock } from './state-file.js';
12
16
  function sanitizeProjectState(data) {
@@ -36,11 +40,15 @@ function sanitizeProjectState(data) {
36
40
  issues.push(`field "${key}" must be a string`);
37
41
  continue;
38
42
  }
39
- if (key === 'buildMode' && !['dev', 'debug', 'release'].includes(value)) {
40
- issues.push('field "buildMode" must be one of: dev, debug, release');
41
- continue;
42
- }
43
43
  if (key === 'buildMode') {
44
+ // Validate and assign in ONE block so the predicate's narrowing reaches
45
+ // the assignment. Split across two `key === 'buildMode'` guards,
46
+ // TypeScript cannot correlate them and the assignment needed a cast —
47
+ // which is what let the untyped allowlist drift from the union.
48
+ if (!isBuildMode(value)) {
49
+ issues.push(`field "buildMode" must be one of: ${BUILD_MODES.join(', ')}`);
50
+ continue;
51
+ }
44
52
  state.buildMode = value;
45
53
  }
46
54
  else {
@@ -6,7 +6,7 @@ import { ConfigError } from '../errors/config.js';
6
6
  import { verbose } from '../utils/logger.js';
7
7
  import { parseObject } from '../utils/parse.js';
8
8
  import { isContainedRelativePath, isExplicitAbsolutePath } from '../utils/paths.js';
9
- import { isObject, isValidAppId, isValidFirefoxCandidate, isValidFirefoxVersion, isValidProjectLicense, PROJECT_LICENSES, validateFirefoxProductVersionCompatibility, } from '../utils/validation.js';
9
+ import { describeProductVersionIncompatibility, FIREFOX_PRODUCTS, isObject, isValidAppId, isValidFirefoxCandidate, isValidFirefoxProduct, isValidFirefoxVersion, isValidProjectLicense, PROJECT_LICENSES, } from '../utils/validation.js';
10
10
  import { SUPPORTED_CONFIG_ROOT_KEYS } from './config-paths.js';
11
11
  import { parsePatchPolicyBlock } from './config-validate-patch-policy.js';
12
12
  import { parseExternalToolchainsBlock, parseTestBlock } from './config-validate-test-toolchains.js';
@@ -68,12 +68,15 @@ function parseFirefoxBlock(rec) {
68
68
  throw new ConfigError('Config field "firefox.version" must be a valid Firefox version (e.g., "145.0")');
69
69
  }
70
70
  const firefoxProduct = requireConfigString(firefoxRec, 'product', 'firefox.product');
71
- const validProducts = ['firefox', 'firefox-esr', 'firefox-beta', 'firefox-devedition'];
72
- if (!validProducts.includes(firefoxProduct)) {
73
- throw new ConfigError(`Config field "firefox.product" must be one of: ${validProducts.join(', ')}`);
71
+ // Uses the shared guard rather than a fourth copy of the member list. This
72
+ // is the fireforge.json READ path: a product added to the union but not to
73
+ // the copy that used to live here would have been silently rejected from
74
+ // every project config.
75
+ if (!isValidFirefoxProduct(firefoxProduct)) {
76
+ throw new ConfigError(`Config field "firefox.product" must be one of: ${FIREFOX_PRODUCTS.join(', ')}`);
74
77
  }
75
78
  // Cross-field validation: product and version must be compatible
76
- const compatError = validateFirefoxProductVersionCompatibility(firefoxVersion, firefoxProduct);
79
+ const compatError = describeProductVersionIncompatibility(firefoxVersion, firefoxProduct);
77
80
  if (compatError) {
78
81
  throw new ConfigError(compatError);
79
82
  }
@@ -66,13 +66,10 @@ export declare function writeConfigDocument(root: string, config: FireForgeConfi
66
66
  /**
67
67
  * Runs an operation while holding a sidecar lock on `fireforge.json`.
68
68
  *
69
- * Motivating case (2026-04-21 eval): two concurrent `fireforge config
70
- * <key> <value>` invocations each ran load → mutate → writeJson against
71
- * the same on-disk fireforge.json. The second rename landed after the
72
- * first, silently dropping the first writer's key — both commands exited
73
- * `0`, but only one change survived. This helper turns the same
74
- * read-modify-write sequence into a serialised operation so a concurrent
75
- * writer now waits for the lock rather than racing on the document.
69
+ * Two concurrent `fireforge config <key> <value>` invocations each run
70
+ * load → mutate → writeJson against the same document; without this lock the
71
+ * second rename lands after the first and silently drops the first writer's
72
+ * key, with both commands exiting 0.
76
73
  *
77
74
  * Reads (`loadConfig`, `loadRawConfigDocument`) stay lock-free: writers
78
75
  * always use `writeJson`'s atomic temp-file + rename, so a reader observes
@@ -81,7 +78,7 @@ export declare function writeConfigDocument(root: string, config: FireForgeConfi
81
78
  *
82
79
  * The lock is a sidecar directory `${config}.fireforge-config.lock`, and
83
80
  * `withFileLock` handles stale-lock recovery (PID-alive probe, age-based
84
- * fallback) — a crashed writer does not permanently block future writes.
81
+ * fallback), so a crashed writer does not permanently block future writes.
85
82
  *
86
83
  * @param root - Root directory of the project
87
84
  * @param operation - Async function to run while holding the lock
@@ -26,14 +26,8 @@ export { validateConfig } from './config-validate.js';
26
26
  * Config-file existence probe.
27
27
  *
28
28
  * Uses {@link pathExistsStrict} deliberately: a permission error probing
29
- * `fireforge.json` must propagate rather than read as "no config here", which
30
- * is what plain `pathExists` would report.
31
- *
32
- * The `fsUtils as typeof fsUtils & { pathExistsStrict?: … }` cast this
33
- * replaced re-declared a real, non-optional export as optional so a partial
34
- * `vi.mock` of `utils/fs.js` would fall back to `pathExists` — production code
35
- * shaped around a test double, and shaped around the *wrong* function, since
36
- * the two differ precisely on whether EACCES propagates.
29
+ * `fireforge.json` must propagate rather than read as "no config here",
30
+ * which is what plain `pathExists` would report.
37
31
  */
38
32
  async function configPathExists(path) {
39
33
  return fsUtils.pathExistsStrict(path);
@@ -134,13 +128,10 @@ export async function writeConfigDocument(root, config) {
134
128
  /**
135
129
  * Runs an operation while holding a sidecar lock on `fireforge.json`.
136
130
  *
137
- * Motivating case (2026-04-21 eval): two concurrent `fireforge config
138
- * <key> <value>` invocations each ran load → mutate → writeJson against
139
- * the same on-disk fireforge.json. The second rename landed after the
140
- * first, silently dropping the first writer's key — both commands exited
141
- * `0`, but only one change survived. This helper turns the same
142
- * read-modify-write sequence into a serialised operation so a concurrent
143
- * writer now waits for the lock rather than racing on the document.
131
+ * Two concurrent `fireforge config <key> <value>` invocations each run
132
+ * load → mutate → writeJson against the same document; without this lock the
133
+ * second rename lands after the first and silently drops the first writer's
134
+ * key, with both commands exiting 0.
144
135
  *
145
136
  * Reads (`loadConfig`, `loadRawConfigDocument`) stay lock-free: writers
146
137
  * always use `writeJson`'s atomic temp-file + rename, so a reader observes
@@ -149,7 +140,7 @@ export async function writeConfigDocument(root, config) {
149
140
  *
150
141
  * The lock is a sidecar directory `${config}.fireforge-config.lock`, and
151
142
  * `withFileLock` handles stale-lock recovery (PID-alive probe, age-based
152
- * fallback) — a crashed writer does not permanently block future writes.
143
+ * fallback), so a crashed writer does not permanently block future writes.
153
144
  *
154
145
  * @param root - Root directory of the project
155
146
  * @param operation - Async function to run while holding the lock
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `test --build/--build-only --extend-coverage`: unions the
3
- * requested paths into the recorded `testPackagingCoverage` instead of
4
- * replacing it, under an anchor guard.
2
+ * `test --build/--build-only --extend-coverage`: unions the requested paths
3
+ * into the recorded `testPackagingCoverage` instead of replacing it, under
4
+ * an anchor guard.
5
5
  *
6
- * Coverage REPLACES by default for a documented reason
7
- * (`build-baseline-types.ts`): every baseline write refreshes
6
+ * Coverage REPLACES by default for a reason documented in
7
+ * `build-baseline-types.ts`: every baseline write refreshes
8
8
  * `packageableFingerprints` for ALL dirty packageable paths, so a blind
9
9
  * union would whitewash an earlier scope's edited fixtures while
10
10
  * `obj-*`/`_tests/` still holds that scope's stale staging. Extending is
@@ -14,9 +14,10 @@
14
14
  *
15
15
  * 1. engine HEAD is unchanged — every committed edit is excluded;
16
16
  * 2. every path the previous baseline fingerprinted still hashes to the
17
- * recorded digest — the earlier scope's staging inputs are byte-identical.
18
- * Files that became dirty SINCE that build are fine: the current
19
- * whole-tree `mach build faster` repackages `dist/` and vouches for them;
17
+ * recorded digest, so the earlier scope's staging inputs are
18
+ * byte-identical. Files that became dirty SINCE that build are fine:
19
+ * the current whole-tree `mach build faster` repackages `dist/` and
20
+ * vouches for them;
20
21
  * 3. the generated `engine/mozconfig` is unchanged. Engine HEAD does not
21
22
  * cover this — the mozconfig is regenerated from project-side
22
23
  * `configs/*.mozconfig` templates plus `fireforge.json` on every build,
@@ -29,8 +30,8 @@
29
30
  * fixtures under previously covered paths (plain test files, manifests
30
31
  * `isPackageablePath` rejects) are invisible to check 2. Editing one
31
32
  * uncommitted between two builds leaves the earlier scope's `_tests/`
32
- * staging stale while extend still vouches for it. Plain REPLACE has no
33
- * such window, because the earlier scope simply stops being covered.
33
+ * staging stale while extend still vouches for it. Plain REPLACE has no such
34
+ * window, because the earlier scope simply stops being covered.
34
35
  */
35
36
  import { type BuildBaseline, type TestPackagingCoverage } from './build-baseline-types.js';
36
37
  /** Why `--extend-coverage` could not union onto the recorded claim. */
@@ -62,9 +63,10 @@ export declare function checkExtendCoverageAnchor(engineDir: string, previousBas
62
63
  */
63
64
  export declare function checkExtendMozconfigAnchor(engineDir: string, previousBaseline: BuildBaseline | undefined): Promise<ExtendAnchorResult>;
64
65
  /**
65
- * Unions the newly built paths onto the recorded claim. A `'full'` claim
66
- * (or a pre-0.37.0 absent one, which means full historically) stays
67
- * `'full'` — it already covers everything.
66
+ * Unions the newly built paths onto the recorded claim. A `'full'` claim, or
67
+ * an absent one (which means full), stays `'full'` — it already covers
68
+ * everything.
69
+ *
68
70
  * @param previous - Coverage recorded by the previous build
69
71
  * @param requested - Normalized request paths this build packaged
70
72
  */
@@ -1,11 +1,11 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
- * `test --build/--build-only --extend-coverage`: unions the
4
- * requested paths into the recorded `testPackagingCoverage` instead of
5
- * replacing it, under an anchor guard.
3
+ * `test --build/--build-only --extend-coverage`: unions the requested paths
4
+ * into the recorded `testPackagingCoverage` instead of replacing it, under
5
+ * an anchor guard.
6
6
  *
7
- * Coverage REPLACES by default for a documented reason
8
- * (`build-baseline-types.ts`): every baseline write refreshes
7
+ * Coverage REPLACES by default for a reason documented in
8
+ * `build-baseline-types.ts`: every baseline write refreshes
9
9
  * `packageableFingerprints` for ALL dirty packageable paths, so a blind
10
10
  * union would whitewash an earlier scope's edited fixtures while
11
11
  * `obj-*`/`_tests/` still holds that scope's stale staging. Extending is
@@ -15,9 +15,10 @@
15
15
  *
16
16
  * 1. engine HEAD is unchanged — every committed edit is excluded;
17
17
  * 2. every path the previous baseline fingerprinted still hashes to the
18
- * recorded digest — the earlier scope's staging inputs are byte-identical.
19
- * Files that became dirty SINCE that build are fine: the current
20
- * whole-tree `mach build faster` repackages `dist/` and vouches for them;
18
+ * recorded digest, so the earlier scope's staging inputs are
19
+ * byte-identical. Files that became dirty SINCE that build are fine:
20
+ * the current whole-tree `mach build faster` repackages `dist/` and
21
+ * vouches for them;
21
22
  * 3. the generated `engine/mozconfig` is unchanged. Engine HEAD does not
22
23
  * cover this — the mozconfig is regenerated from project-side
23
24
  * `configs/*.mozconfig` templates plus `fireforge.json` on every build,
@@ -30,8 +31,8 @@
30
31
  * fixtures under previously covered paths (plain test files, manifests
31
32
  * `isPackageablePath` rejects) are invisible to check 2. Editing one
32
33
  * uncommitted between two builds leaves the earlier scope's `_tests/`
33
- * staging stale while extend still vouches for it. Plain REPLACE has no
34
- * such window, because the earlier scope simply stops being covered.
34
+ * staging stale while extend still vouches for it. Plain REPLACE has no such
35
+ * window, because the earlier scope simply stops being covered.
35
36
  */
36
37
  import { createHash } from 'node:crypto';
37
38
  import { readFile } from 'node:fs/promises';
@@ -106,9 +107,10 @@ export async function checkExtendMozconfigAnchor(engineDir, previousBaseline) {
106
107
  return { ok: true };
107
108
  }
108
109
  /**
109
- * Unions the newly built paths onto the recorded claim. A `'full'` claim
110
- * (or a pre-0.37.0 absent one, which means full historically) stays
111
- * `'full'` — it already covers everything.
110
+ * Unions the newly built paths onto the recorded claim. A `'full'` claim, or
111
+ * an absent one (which means full), stays `'full'` — it already covers
112
+ * everything.
113
+ *
112
114
  * @param previous - Coverage recorded by the previous build
113
115
  * @param requested - Normalized request paths this build packaged
114
116
  */
@@ -49,8 +49,16 @@ export interface DestructiveOpInput {
49
49
  /** Structural conflicts that should block the operation unless `unsafeOverride`. */
50
50
  conflicts?: ConflictReport | null;
51
51
  }
52
- /** Outcome of {@link confirmDestructive}. */
53
- export type DestructiveOpResult = 'proceed' | 'dry-run' | 'cancelled';
52
+ /**
53
+ * Outcome of the destructive-operation contract.
54
+ *
55
+ * `'declined'` means the operator answered "no" — a successful run that
56
+ * chose not to proceed, so callers return normally and the process exits 0.
57
+ * An INTERRUPT (Esc / Ctrl+C) is not represented here: it throws
58
+ * {@link CancellationError} and exits 130. Collapsing the two would make an
59
+ * interrupted prompt indistinguishable from a declined one.
60
+ */
61
+ export type DestructiveOpResult = 'proceed' | 'dry-run' | 'declined';
54
62
  /**
55
63
  * Inputs to {@link appendHistory}. Entries are appended as one JSON record
56
64
  * per line. Callers should only append after a mutation succeeds so
@@ -68,6 +76,21 @@ export interface HistoryEntry {
68
76
  /** Result: `'ok'` on success; other strings describe failure. */
69
77
  result: string;
70
78
  }
79
+ /**
80
+ * True when BOTH standard handles are real TTYs, i.e. an interactive prompt
81
+ * can actually be answered.
82
+ *
83
+ * Named for the condition rather than for confirmation specifically: many
84
+ * call sites gate a `text()` / `multiselect()` input prompt or a mode
85
+ * switch, not a destructive confirmation.
86
+ *
87
+ * Node types `isTTY` as `boolean`, but at runtime it is `true | undefined`
88
+ * (absent when the handle is not a TTY). Unusual harnesses (vitest stdio
89
+ * capture, CI spawners, mocked pipes) can also leave it unset on only one
90
+ * handle, so every falsy variant — false, undefined, a null-patched mock —
91
+ * must route to the non-interactive path.
92
+ */
93
+ export declare function stdioIsInteractive(): boolean;
71
94
  /**
72
95
  * UP-FRONT non-interactive refusal for a command that will end at
73
96
  * {@link confirmDestructive}.
@@ -78,8 +101,7 @@ export interface HistoryEntry {
78
101
  * --create` that is minutes of work before a scripted run learns it needed
79
102
  * `--yes`, and the failure reads as an obscure late error rather than a
80
103
  * usage problem. Calling this at command entry turns it into a usage
81
- * refusal that names the flag, in the same shape as the 0.41.0
82
- * `move-files --create` / `--description` refusal.
104
+ * refusal that names the flag.
83
105
  *
84
106
  * A `--yes` or `--dry-run` run never prompts, so neither is refused.
85
107
  *
@@ -95,14 +117,14 @@ export declare function assertConfirmationAvailable(operation: string, options:
95
117
  * Executes the destructive-operation contract: summary → conflict refusal →
96
118
  * dry-run / force / prompt / non-TTY refusal.
97
119
  *
98
- * Returns the decision for the caller to act on; callers must not execute the
99
- * mutation when the result is `'dry-run'` or `'cancelled'`, and must call
100
- * {@link appendHistory} only after the mutation succeeds (never on dry-run or
101
- * cancellation).
120
+ * Returns the decision for the caller to act on; callers must not execute
121
+ * the mutation when the result is `'dry-run'` or `'declined'`, and must call
122
+ * {@link appendHistory} only after the mutation succeeds (never on dry-run
123
+ * or decline).
102
124
  *
103
125
  * @param input - Operation description, flags, and optional conflict report
104
126
  * @returns `'proceed'` to execute, `'dry-run'` to skip execution, or
105
- * `'cancelled'` when the user declined the prompt
127
+ * `'declined'` when the user declined the prompt
106
128
  */
107
129
  export declare function confirmDestructive(input: DestructiveOpInput): Promise<DestructiveOpResult>;
108
130
  /**