@hominis/fireforge 0.35.0 → 0.36.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 (355) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +34 -9
  3. package/dist/bin/fireforge.js +21 -6
  4. package/dist/src/cli.js +22 -5
  5. package/dist/src/commands/bootstrap-checks.js +0 -1
  6. package/dist/src/commands/bootstrap.d.ts +2 -1
  7. package/dist/src/commands/bootstrap.js +16 -7
  8. package/dist/src/commands/build.js +4 -3
  9. package/dist/src/commands/config.js +30 -10
  10. package/dist/src/commands/discard.js +18 -3
  11. package/dist/src/commands/doctor/post-rebase-audit.js +0 -1
  12. package/dist/src/commands/doctor-check-core.js +0 -1
  13. package/dist/src/commands/doctor-external-toolchains.d.ts +3 -0
  14. package/dist/src/commands/doctor-external-toolchains.js +54 -0
  15. package/dist/src/commands/doctor-furnace-jar.js +0 -1
  16. package/dist/src/commands/doctor-furnace-manifest-sync.js +0 -1
  17. package/dist/src/commands/doctor-furnace.js +2 -2
  18. package/dist/src/commands/doctor-working-tree.js +0 -1
  19. package/dist/src/commands/doctor.js +4 -1
  20. package/dist/src/commands/download.js +28 -13
  21. package/dist/src/commands/export-all.js +5 -3
  22. package/dist/src/commands/export-flow.js +12 -7
  23. package/dist/src/commands/export-placement-gate.js +0 -1
  24. package/dist/src/commands/export-placement-policy.js +0 -1
  25. package/dist/src/commands/export-shared.d.ts +5 -1
  26. package/dist/src/commands/export-shared.js +18 -6
  27. package/dist/src/commands/export.js +15 -10
  28. package/dist/src/commands/furnace/apply.js +22 -6
  29. package/dist/src/commands/furnace/chrome-doc-remove.js +0 -1
  30. package/dist/src/commands/furnace/chrome-doc-templates.js +0 -1
  31. package/dist/src/commands/furnace/chrome-doc-tests.js +0 -1
  32. package/dist/src/commands/furnace/chrome-doc.js +0 -1
  33. package/dist/src/commands/furnace/create-browser-test.js +0 -1
  34. package/dist/src/commands/furnace/create-dry-run.js +0 -1
  35. package/dist/src/commands/furnace/create-features.js +0 -1
  36. package/dist/src/commands/furnace/create-mochikit.js +0 -1
  37. package/dist/src/commands/furnace/create-readback.js +0 -1
  38. package/dist/src/commands/furnace/create-templates.js +0 -1
  39. package/dist/src/commands/furnace/create-validation.js +0 -1
  40. package/dist/src/commands/furnace/create-xpcshell.js +0 -1
  41. package/dist/src/commands/furnace/create.js +0 -1
  42. package/dist/src/commands/furnace/deploy.js +7 -83
  43. package/dist/src/commands/furnace/diff.js +0 -1
  44. package/dist/src/commands/furnace/index.js +59 -25
  45. package/dist/src/commands/furnace/init.js +0 -1
  46. package/dist/src/commands/furnace/list.js +0 -1
  47. package/dist/src/commands/furnace/override.js +0 -1
  48. package/dist/src/commands/furnace/preview.js +3 -3
  49. package/dist/src/commands/furnace/refresh.js +0 -1
  50. package/dist/src/commands/furnace/remove-state.d.ts +15 -0
  51. package/dist/src/commands/furnace/remove-state.js +47 -1
  52. package/dist/src/commands/furnace/remove.js +3 -9
  53. package/dist/src/commands/furnace/rename-browser-test.js +1 -5
  54. package/dist/src/commands/furnace/rename-helpers.d.ts +6 -0
  55. package/dist/src/commands/furnace/rename-helpers.js +30 -1
  56. package/dist/src/commands/furnace/rename-xpcshell.js +7 -6
  57. package/dist/src/commands/furnace/rename.js +15 -36
  58. package/dist/src/commands/furnace/scan.js +0 -1
  59. package/dist/src/commands/furnace/status.js +0 -1
  60. package/dist/src/commands/furnace/sync.js +76 -4
  61. package/dist/src/commands/furnace/validate.js +0 -1
  62. package/dist/src/commands/furnace/validation-output.js +0 -1
  63. package/dist/src/commands/import.js +35 -9
  64. package/dist/src/commands/lint-per-patch.js +19 -7
  65. package/dist/src/commands/lint.js +13 -3
  66. package/dist/src/commands/manifest.js +1 -2
  67. package/dist/src/commands/package.js +2 -1
  68. package/dist/src/commands/patch/compact.js +1 -1
  69. package/dist/src/commands/patch/delete.js +1 -1
  70. package/dist/src/commands/patch/index.js +1 -1
  71. package/dist/src/commands/patch/lint-ignore.js +21 -6
  72. package/dist/src/commands/patch/move-files.js +1 -1
  73. package/dist/src/commands/patch/patch-context.js +0 -1
  74. package/dist/src/commands/patch/rename.js +4 -4
  75. package/dist/src/commands/patch/reorder.js +3 -4
  76. package/dist/src/commands/patch/split-plan.js +2 -1
  77. package/dist/src/commands/patch/split.js +4 -4
  78. package/dist/src/commands/patch/staged-dependency.js +1 -1
  79. package/dist/src/commands/patch/tier.js +18 -5
  80. package/dist/src/commands/re-export-bulk-scan.js +0 -1
  81. package/dist/src/commands/re-export-files.js +1 -1
  82. package/dist/src/commands/re-export-options.js +0 -1
  83. package/dist/src/commands/re-export-scan.js +0 -1
  84. package/dist/src/commands/re-export.js +6 -4
  85. package/dist/src/commands/rebase/abort.js +2 -9
  86. package/dist/src/commands/rebase/confirm.js +0 -1
  87. package/dist/src/commands/rebase/conflict-summary.js +0 -1
  88. package/dist/src/commands/rebase/continue.js +0 -1
  89. package/dist/src/commands/rebase/index.d.ts +1 -1
  90. package/dist/src/commands/rebase/index.js +10 -15
  91. package/dist/src/commands/rebase/patch-loop.js +38 -7
  92. package/dist/src/commands/rebase/summary.js +2 -3
  93. package/dist/src/commands/rebase.js +0 -1
  94. package/dist/src/commands/register.js +1 -1
  95. package/dist/src/commands/reset.js +16 -9
  96. package/dist/src/commands/resolve.js +2 -2
  97. package/dist/src/commands/run.js +4 -4
  98. package/dist/src/commands/setup-support.js +0 -1
  99. package/dist/src/commands/setup.js +1 -2
  100. package/dist/src/commands/source.js +1 -2
  101. package/dist/src/commands/status-output.js +1 -12
  102. package/dist/src/commands/status.js +147 -133
  103. package/dist/src/commands/test-appdir.js +0 -1
  104. package/dist/src/commands/test-diagnose.js +18 -2
  105. package/dist/src/commands/test-modes.d.ts +15 -0
  106. package/dist/src/commands/test-modes.js +61 -0
  107. package/dist/src/commands/test-register.d.ts +4 -0
  108. package/dist/src/commands/test-register.js +64 -0
  109. package/dist/src/commands/test-run.d.ts +23 -7
  110. package/dist/src/commands/test-run.js +15 -14
  111. package/dist/src/commands/test.d.ts +0 -4
  112. package/dist/src/commands/test.js +85 -86
  113. package/dist/src/commands/token-coverage.js +0 -1
  114. package/dist/src/commands/token.js +1 -2
  115. package/dist/src/commands/typecheck.js +1 -1
  116. package/dist/src/commands/verify.js +3 -9
  117. package/dist/src/commands/watch.js +5 -1
  118. package/dist/src/commands/wire.js +1 -1
  119. package/dist/src/core/ast-utils.js +0 -1
  120. package/dist/src/core/brand-validation.js +0 -1
  121. package/dist/src/core/branding.js +0 -1
  122. package/dist/src/core/browser-wire.js +0 -1
  123. package/dist/src/core/build-audit-platform.js +0 -1
  124. package/dist/src/core/build-audit-registration.js +0 -1
  125. package/dist/src/core/build-audit-resolve.js +0 -1
  126. package/dist/src/core/build-audit-transforms.js +0 -1
  127. package/dist/src/core/build-audit.js +0 -1
  128. package/dist/src/core/build-baseline-types.js +0 -1
  129. package/dist/src/core/build-baseline.js +0 -1
  130. package/dist/src/core/build-prepare.js +2 -2
  131. package/dist/src/core/config-mutate.js +0 -1
  132. package/dist/src/core/config-paths.d.ts +2 -2
  133. package/dist/src/core/config-paths.js +6 -1
  134. package/dist/src/core/config-state.js +0 -1
  135. package/dist/src/core/config-validate-patch-policy.js +0 -1
  136. package/dist/src/core/config-validate-test-toolchains.d.ts +8 -0
  137. package/dist/src/core/config-validate-test-toolchains.js +105 -0
  138. package/dist/src/core/config-validate.js +3 -1
  139. package/dist/src/core/config.js +0 -1
  140. package/dist/src/core/destructive.js +0 -1
  141. package/dist/src/core/diff-hunks.js +13 -7
  142. package/dist/src/core/engine-changes.js +0 -1
  143. package/dist/src/core/engine-session-lock.d.ts +12 -0
  144. package/dist/src/core/engine-session-lock.js +45 -0
  145. package/dist/src/core/file-lock.d.ts +8 -0
  146. package/dist/src/core/file-lock.js +157 -48
  147. package/dist/src/core/firefox-archive.d.ts +4 -0
  148. package/dist/src/core/firefox-archive.js +6 -1
  149. package/dist/src/core/firefox-cache.js +60 -4
  150. package/dist/src/core/firefox-download.js +0 -1
  151. package/dist/src/core/firefox-extract.js +75 -16
  152. package/dist/src/core/firefox-ignorefile.js +0 -1
  153. package/dist/src/core/firefox.d.ts +12 -0
  154. package/dist/src/core/firefox.js +64 -2
  155. package/dist/src/core/furnace-apply-dry-run.js +0 -1
  156. package/dist/src/core/furnace-apply-ftl.d.ts +7 -3
  157. package/dist/src/core/furnace-apply-ftl.js +10 -5
  158. package/dist/src/core/furnace-apply-helpers.js +0 -1
  159. package/dist/src/core/furnace-apply-output.js +2 -2
  160. package/dist/src/core/furnace-apply.d.ts +7 -1
  161. package/dist/src/core/furnace-apply.js +47 -13
  162. package/dist/src/core/furnace-checksum-utils.js +0 -1
  163. package/dist/src/core/furnace-config-array-utils.js +0 -1
  164. package/dist/src/core/furnace-config-custom.js +0 -1
  165. package/dist/src/core/furnace-config-order.js +0 -1
  166. package/dist/src/core/furnace-config-tokens.js +0 -1
  167. package/dist/src/core/furnace-config-validate.js +0 -1
  168. package/dist/src/core/furnace-config.d.ts +11 -0
  169. package/dist/src/core/furnace-config.js +37 -1
  170. package/dist/src/core/furnace-constants.js +0 -1
  171. package/dist/src/core/furnace-css-fragments.js +0 -1
  172. package/dist/src/core/furnace-graph-utils.js +0 -1
  173. package/dist/src/core/furnace-jsconfig.d.ts +2 -3
  174. package/dist/src/core/furnace-jsconfig.js +25 -18
  175. package/dist/src/core/furnace-marker.js +0 -1
  176. package/dist/src/core/furnace-operation.js +0 -1
  177. package/dist/src/core/furnace-refresh.js +0 -1
  178. package/dist/src/core/furnace-registration-ast.d.ts +14 -0
  179. package/dist/src/core/furnace-registration-ast.js +1 -2
  180. package/dist/src/core/furnace-registration-remove.js +0 -1
  181. package/dist/src/core/furnace-registration-validate.js +1 -5
  182. package/dist/src/core/furnace-registration.js +0 -1
  183. package/dist/src/core/furnace-rollback.js +0 -1
  184. package/dist/src/core/furnace-scanner.js +0 -1
  185. package/dist/src/core/furnace-staleness.js +0 -1
  186. package/dist/src/core/furnace-state-persist.d.ts +72 -0
  187. package/dist/src/core/furnace-state-persist.js +118 -0
  188. package/dist/src/core/furnace-step-errors.d.ts +23 -0
  189. package/dist/src/core/furnace-step-errors.js +25 -0
  190. package/dist/src/core/furnace-stories.js +0 -1
  191. package/dist/src/core/furnace-validate-accessibility.js +0 -1
  192. package/dist/src/core/furnace-validate-checks.js +0 -1
  193. package/dist/src/core/furnace-validate-compatibility.js +0 -1
  194. package/dist/src/core/furnace-validate-helpers.js +0 -1
  195. package/dist/src/core/furnace-validate-registration.js +7 -7
  196. package/dist/src/core/furnace-validate-structure.d.ts +8 -0
  197. package/dist/src/core/furnace-validate-structure.js +11 -2
  198. package/dist/src/core/furnace-validate.js +0 -1
  199. package/dist/src/core/furnace-version-drift.d.ts +6 -3
  200. package/dist/src/core/furnace-version-drift.js +6 -4
  201. package/dist/src/core/git-base.js +0 -1
  202. package/dist/src/core/git-diff.js +0 -1
  203. package/dist/src/core/git-file-ops.js +0 -1
  204. package/dist/src/core/git-status.d.ts +13 -0
  205. package/dist/src/core/git-status.js +32 -2
  206. package/dist/src/core/git.d.ts +10 -1
  207. package/dist/src/core/git.js +63 -6
  208. package/dist/src/core/license-headers.js +0 -1
  209. package/dist/src/core/lint-cache.js +0 -1
  210. package/dist/src/core/lint-projection.js +0 -1
  211. package/dist/src/core/mach-build-artifacts.js +0 -1
  212. package/dist/src/core/mach-error-hints.js +0 -1
  213. package/dist/src/core/mach-mozconfig.js +0 -1
  214. package/dist/src/core/mach-python.js +0 -1
  215. package/dist/src/core/mach-resource-shim.js +0 -1
  216. package/dist/src/core/mach.js +0 -1
  217. package/dist/src/core/manifest-helpers.js +0 -1
  218. package/dist/src/core/manifest-register.js +0 -1
  219. package/dist/src/core/manifest-rules.js +0 -1
  220. package/dist/src/core/manifest-tokenizers.js +0 -1
  221. package/dist/src/core/marionette-port.d.ts +8 -0
  222. package/dist/src/core/marionette-port.js +34 -3
  223. package/dist/src/core/marionette-preflight.js +0 -1
  224. package/dist/src/core/markdown-table.js +0 -1
  225. package/dist/src/core/ownership-table.js +2 -9
  226. package/dist/src/core/parser-fallback.js +0 -1
  227. package/dist/src/core/patch-apply-fuzz.d.ts +27 -9
  228. package/dist/src/core/patch-apply-fuzz.js +71 -34
  229. package/dist/src/core/patch-apply.d.ts +30 -0
  230. package/dist/src/core/patch-apply.js +101 -18
  231. package/dist/src/core/patch-artifact-normalize.d.ts +18 -5
  232. package/dist/src/core/patch-artifact-normalize.js +19 -15
  233. package/dist/src/core/patch-export-coverage.js +0 -1
  234. package/dist/src/core/patch-export-metadata.js +0 -1
  235. package/dist/src/core/patch-export-update.js +2 -3
  236. package/dist/src/core/patch-export.d.ts +2 -6
  237. package/dist/src/core/patch-export.js +24 -48
  238. package/dist/src/core/patch-files.js +0 -1
  239. package/dist/src/core/patch-identifier-suggest.js +0 -1
  240. package/dist/src/core/patch-lint-checkjs.js +0 -1
  241. package/dist/src/core/patch-lint-chrome-jsdoc.js +0 -1
  242. package/dist/src/core/patch-lint-cross.js +0 -1
  243. package/dist/src/core/patch-lint-css.js +0 -1
  244. package/dist/src/core/patch-lint-diff-tag.js +0 -1
  245. package/dist/src/core/patch-lint-diff.d.ts +2 -1
  246. package/dist/src/core/patch-lint-diff.js +17 -32
  247. package/dist/src/core/patch-lint-imports.js +0 -1
  248. package/dist/src/core/patch-lint-jsdoc.js +0 -1
  249. package/dist/src/core/patch-lint-observer.js +0 -1
  250. package/dist/src/core/patch-lint-ownership.js +0 -1
  251. package/dist/src/core/patch-lint-reexports.js +0 -1
  252. package/dist/src/core/patch-lint.d.ts +37 -2
  253. package/dist/src/core/patch-lint.js +47 -34
  254. package/dist/src/core/patch-lock.js +0 -1
  255. package/dist/src/core/patch-manifest-consistency.js +9 -1
  256. package/dist/src/core/patch-manifest-io.d.ts +16 -0
  257. package/dist/src/core/patch-manifest-io.js +30 -5
  258. package/dist/src/core/patch-manifest-query.js +18 -10
  259. package/dist/src/core/patch-manifest-resolve.js +0 -1
  260. package/dist/src/core/patch-manifest-validate.js +0 -1
  261. package/dist/src/core/patch-manifest.d.ts +1 -1
  262. package/dist/src/core/patch-manifest.js +1 -2
  263. package/dist/src/core/patch-parse.d.ts +59 -0
  264. package/dist/src/core/patch-parse.js +209 -92
  265. package/dist/src/core/patch-policy.js +0 -1
  266. package/dist/src/core/patch-registration-refs.js +16 -26
  267. package/dist/src/core/patch-source-metadata.js +0 -1
  268. package/dist/src/core/patch-transform.d.ts +4 -0
  269. package/dist/src/core/patch-transform.js +30 -38
  270. package/dist/src/core/rebase-session.js +0 -1
  271. package/dist/src/core/register-browser-content.js +0 -1
  272. package/dist/src/core/register-module.js +0 -1
  273. package/dist/src/core/register-result.js +0 -1
  274. package/dist/src/core/register-scaffold.js +0 -1
  275. package/dist/src/core/register-shared-css.js +0 -1
  276. package/dist/src/core/register-test-manifest.js +0 -1
  277. package/dist/src/core/register-xpcshell-test.js +0 -1
  278. package/dist/src/core/shared-ftl.js +0 -1
  279. package/dist/src/core/signal-critical.js +0 -1
  280. package/dist/src/core/smoke-patterns.js +0 -1
  281. package/dist/src/core/state-file.js +0 -1
  282. package/dist/src/core/status-classify.d.ts +16 -0
  283. package/dist/src/core/status-classify.js +24 -16
  284. package/dist/src/core/test-harness-crash.d.ts +64 -7
  285. package/dist/src/core/test-harness-crash.js +196 -15
  286. package/dist/src/core/test-harness-output.js +0 -1
  287. package/dist/src/core/test-path-scope.d.ts +21 -13
  288. package/dist/src/core/test-path-scope.js +43 -28
  289. package/dist/src/core/test-stale-check.js +0 -1
  290. package/dist/src/core/test-stale-symlink.js +0 -1
  291. package/dist/src/core/test-xpcshell-retry.js +0 -1
  292. package/dist/src/core/token-coverage.js +0 -1
  293. package/dist/src/core/token-dark-mode.js +0 -1
  294. package/dist/src/core/token-docs.js +0 -1
  295. package/dist/src/core/token-manager.js +0 -1
  296. package/dist/src/core/token-scaffold.js +0 -1
  297. package/dist/src/core/token-variant.js +0 -1
  298. package/dist/src/core/toolchain-preflight.d.ts +31 -10
  299. package/dist/src/core/toolchain-preflight.js +97 -36
  300. package/dist/src/core/typecheck-shim.js +13 -1
  301. package/dist/src/core/typecheck.js +0 -1
  302. package/dist/src/core/wire-destroy.js +0 -1
  303. package/dist/src/core/wire-dom-fragment.js +0 -1
  304. package/dist/src/core/wire-init.js +0 -1
  305. package/dist/src/core/wire-subscript.js +0 -1
  306. package/dist/src/core/wire-targets.js +0 -1
  307. package/dist/src/core/wire-utils.js +0 -1
  308. package/dist/src/core/xpcshell-appdir.js +1 -4
  309. package/dist/src/errors/base.d.ts +29 -0
  310. package/dist/src/errors/base.js +35 -1
  311. package/dist/src/errors/build.js +0 -1
  312. package/dist/src/errors/codes.d.ts +6 -0
  313. package/dist/src/errors/codes.js +6 -1
  314. package/dist/src/errors/config.js +0 -1
  315. package/dist/src/errors/download.js +0 -1
  316. package/dist/src/errors/furnace.d.ts +11 -1
  317. package/dist/src/errors/furnace.js +6 -5
  318. package/dist/src/errors/git.js +0 -1
  319. package/dist/src/errors/patch.d.ts +17 -0
  320. package/dist/src/errors/patch.js +29 -1
  321. package/dist/src/errors/rebase.js +0 -1
  322. package/dist/src/errors/run.js +1 -1
  323. package/dist/src/index.js +0 -1
  324. package/dist/src/types/cli.js +0 -1
  325. package/dist/src/types/commands/index.js +0 -1
  326. package/dist/src/types/commands/options.d.ts +13 -5
  327. package/dist/src/types/commands/options.js +0 -1
  328. package/dist/src/types/commands/patches.d.ts +8 -0
  329. package/dist/src/types/commands/patches.js +0 -1
  330. package/dist/src/types/commands/project.d.ts +11 -2
  331. package/dist/src/types/commands/project.js +0 -1
  332. package/dist/src/types/config.d.ts +32 -0
  333. package/dist/src/types/config.js +0 -1
  334. package/dist/src/types/furnace.d.ts +10 -0
  335. package/dist/src/types/furnace.js +0 -1
  336. package/dist/src/types/index.js +0 -1
  337. package/dist/src/types/typecheck.js +0 -1
  338. package/dist/src/utils/elapsed.js +0 -1
  339. package/dist/src/utils/errors.js +0 -1
  340. package/dist/src/utils/fs.js +37 -5
  341. package/dist/src/utils/logger.d.ts +7 -0
  342. package/dist/src/utils/logger.js +85 -3
  343. package/dist/src/utils/options.d.ts +15 -0
  344. package/dist/src/utils/options.js +25 -1
  345. package/dist/src/utils/package-root.js +11 -4
  346. package/dist/src/utils/parse.js +0 -1
  347. package/dist/src/utils/paths.js +0 -1
  348. package/dist/src/utils/platform.js +0 -1
  349. package/dist/src/utils/process.d.ts +33 -4
  350. package/dist/src/utils/process.js +183 -28
  351. package/dist/src/utils/regex.d.ts +13 -3
  352. package/dist/src/utils/regex.js +53 -4
  353. package/dist/src/utils/validation.d.ts +0 -12
  354. package/dist/src/utils/validation.js +13 -26
  355. package/package.json +8 -7
@@ -167,4 +167,3 @@ export function findDarkMediaCloseIndex(lines) {
167
167
  return -1;
168
168
  return findBlockCloseIndex(stripped, darkMediaLine);
169
169
  }
170
- //# sourceMappingURL=token-dark-mode.js.map
@@ -98,4 +98,3 @@ export async function addTokenToDocs(engineDir, options, annotation) {
98
98
  await writeText(filePath, lines.join('\n'));
99
99
  return { docsAdded, unmappedAdded, countUpdated };
100
100
  }
101
- //# sourceMappingURL=token-docs.js.map
@@ -455,4 +455,3 @@ async function addTokenToCSS(engineDir, options, tokensCssPath) {
455
455
  await writeText(filePath, content);
456
456
  return { added: true, categoryCreated };
457
457
  }
458
- //# sourceMappingURL=token-manager.js.map
@@ -71,4 +71,3 @@ ${categoryBlocks}
71
71
  }
72
72
  `;
73
73
  }
74
- //# sourceMappingURL=token-scaffold.js.map
@@ -138,4 +138,3 @@ export function insertVariantDeclaration(lines, variant, declLine) {
138
138
  }
139
139
  lines.splice(findVariantBlockInsertionPoint(lines), 0, '', `:root${variant} {`, declLine, '}');
140
140
  }
141
- //# sourceMappingURL=token-variant.js.map
@@ -14,22 +14,38 @@
14
14
  * the downloaded major differs from the previously downloaded one.
15
15
  * 2. {@link runToolchainPreflight} — a cheap pre-build probe comparing
16
16
  * the minimums the tree itself declares against the host binaries
17
- * `mach configure` will resolve. Deliberately FAIL-SOFT: it reports
18
- * a mismatch only when a minimum was positively parsed AND the host
19
- * binary was found AND its version is definitively lower. Any
20
- * uncertainty (file moved upstream, unparseable output, binary not
21
- * on PATH) skips silently the mach-error-hints translator still
22
- * catches the real configure failure downstream.
17
+ * `mach configure` will resolve, probing configure's own candidate
18
+ * order: env override first, then the `~/.mozbuild` state-directory
19
+ * copy bootstrap installs, then PATH (0.35.0 probed env-or-PATH
20
+ * only, blocking builds whose current tool lived in the state dir
21
+ * behind a stale PATH copy). Deliberately FAIL-SOFT: it reports a
22
+ * mismatch only when a minimum was positively parsed AND at least
23
+ * one candidate resolved AND every resolved candidate is
24
+ * definitively lower. Any uncertainty (file moved upstream,
25
+ * unparseable output, no candidate found) skips silently — the
26
+ * mach-error-hints translator still catches the real configure
27
+ * failure downstream.
23
28
  */
24
29
  /** Tools the preflight knows how to probe. */
25
30
  export type ToolchainTool = 'cbindgen' | 'rustc';
31
+ /** One probed candidate binary for a tool. */
32
+ export interface ToolchainCandidate {
33
+ /** Binary path (or bare name for the PATH candidate). */
34
+ binary: string;
35
+ /** Parsed version of the candidate. */
36
+ version: string;
37
+ }
26
38
  /** One definitive host-vs-tree mismatch found by the preflight. */
27
39
  export interface ToolchainMismatch {
28
40
  tool: ToolchainTool;
29
- hostVersion: string;
30
41
  minimumVersion: string;
31
42
  /** Engine-relative file the minimum was parsed from. */
32
43
  declaredIn: string;
44
+ /**
45
+ * Every candidate that resolved — all of them below the minimum
46
+ * (mach-resolution order: env override, mozbuild state dir, PATH).
47
+ */
48
+ candidates: ToolchainCandidate[];
33
49
  }
34
50
  /**
35
51
  * Returns the one-line notice to print after a download that hopped the
@@ -49,15 +65,20 @@ export declare function formatMajorVersionHopNotice(previousVersion: string | un
49
65
  export declare function readDeclaredToolchainMinimums(engineDir: string): Promise<Partial<Record<ToolchainTool, string>>>;
50
66
  /**
51
67
  * Compares the tree-declared toolchain minimums against the host binaries
52
- * `mach configure` will resolve. Returns only DEFINITIVE mismatches; every
53
- * uncertain probe passes silently (see module header for the rationale).
68
+ * `mach configure` will resolve, in configure's own candidate order (env
69
+ * override, mozbuild state dir, PATH). A tool fails ONLY when at least
70
+ * one candidate resolved and none of them meets the minimum; any single
71
+ * passing candidate passes the tool (configure will find it), and a tool
72
+ * with no resolvable candidate skips silently (see module header for the
73
+ * fail-soft rationale).
54
74
  *
55
75
  * @param engineDir - Path to the engine directory
56
76
  */
57
77
  export declare function runToolchainPreflight(engineDir: string): Promise<ToolchainMismatch[]>;
58
78
  /**
59
79
  * Formats the fail-fast message for definitive preflight mismatches,
60
- * naming `fireforge bootstrap` as the remedy (mach's own configure error
80
+ * listing every candidate probed (in mach's resolution order) and naming
81
+ * `fireforge bootstrap` as the remedy (mach's own configure error
61
82
  * suggests `./mach bootstrap`, which is the wrong entry point for a
62
83
  * FireForge-managed repo).
63
84
  */
@@ -15,17 +15,31 @@
15
15
  * the downloaded major differs from the previously downloaded one.
16
16
  * 2. {@link runToolchainPreflight} — a cheap pre-build probe comparing
17
17
  * the minimums the tree itself declares against the host binaries
18
- * `mach configure` will resolve. Deliberately FAIL-SOFT: it reports
19
- * a mismatch only when a minimum was positively parsed AND the host
20
- * binary was found AND its version is definitively lower. Any
21
- * uncertainty (file moved upstream, unparseable output, binary not
22
- * on PATH) skips silently the mach-error-hints translator still
23
- * catches the real configure failure downstream.
18
+ * `mach configure` will resolve, probing configure's own candidate
19
+ * order: env override first, then the `~/.mozbuild` state-directory
20
+ * copy bootstrap installs, then PATH (0.35.0 probed env-or-PATH
21
+ * only, blocking builds whose current tool lived in the state dir
22
+ * behind a stale PATH copy). Deliberately FAIL-SOFT: it reports a
23
+ * mismatch only when a minimum was positively parsed AND at least
24
+ * one candidate resolved AND every resolved candidate is
25
+ * definitively lower. Any uncertainty (file moved upstream,
26
+ * unparseable output, no candidate found) skips silently — the
27
+ * mach-error-hints translator still catches the real configure
28
+ * failure downstream.
24
29
  */
25
30
  import { execFile } from 'node:child_process';
31
+ import { homedir } from 'node:os';
26
32
  import { join } from 'node:path';
27
33
  import { pathExists, readText } from '../utils/fs.js';
28
34
  import { verbose } from '../utils/logger.js';
35
+ /**
36
+ * Resolves the mach state directory the same way mach itself does:
37
+ * `$MOZBUILD_STATE_PATH`, else `~/.mozbuild` (same resolution as
38
+ * `mach-resource-shim.ts`).
39
+ */
40
+ function mozbuildStateDir() {
41
+ return process.env['MOZBUILD_STATE_PATH'] ?? join(homedir(), '.mozbuild');
42
+ }
29
43
  /** Where each tool's minimum is declared inside the Firefox tree. */
30
44
  const MINIMUM_DECLARATIONS = {
31
45
  // build/moz.configure/bindgen.configure:
@@ -43,13 +57,30 @@ const MINIMUM_DECLARATIONS = {
43
57
  pattern: /MINIMUM_RUST_VERSION\s*=\s*["']([\d.]+)["']/,
44
58
  },
45
59
  };
46
- /** How each tool's host binary is resolved and its version parsed. */
60
+ /**
61
+ * How each tool's host binary is resolved and its version parsed.
62
+ *
63
+ * `stateDirRelPaths` are the mach-state-directory locations `fireforge
64
+ * bootstrap` (via mozboot) installs the tool to. mach's configure tries
65
+ * the state directory BEFORE the PATH candidates (bindgen.configure's
66
+ * toolchain search path lists `~/.mozbuild/cbindgen/cbindgen` first — a
67
+ * configure log shows `trying cbindgen: ~/.mozbuild/cbindgen/cbindgen`
68
+ * ahead of PATH), so the probe must too. 0.35.0 probed only env-or-PATH,
69
+ * which failed a build configure would have accepted whenever an old
70
+ * `~/.cargo/bin/cbindgen` shadowed a current bootstrap-installed copy
71
+ * (field report: PATH 0.29.1 vs mozbuild 0.29.4, minimum 0.29.4). Rust
72
+ * has no state-dir install — rustup owns it — so its list is empty.
73
+ */
47
74
  const HOST_PROBES = {
48
75
  // `mach configure` honours the CBINDGEN env option (bindgen.configure's
49
76
  // `option(env="CBINDGEN", ...)`), so the probe must too — otherwise the
50
77
  // preflight could veto a build configure would have accepted.
51
- cbindgen: { envVar: 'CBINDGEN', versionPattern: /cbindgen\s+(\d+(?:\.\d+)*)/ },
52
- rustc: { envVar: 'RUSTC', versionPattern: /rustc\s+(\d+(?:\.\d+)*)/ },
78
+ cbindgen: {
79
+ envVar: 'CBINDGEN',
80
+ versionPattern: /cbindgen\s+(\d+(?:\.\d+)*)/,
81
+ stateDirRelPaths: ['cbindgen/cbindgen'],
82
+ },
83
+ rustc: { envVar: 'RUSTC', versionPattern: /rustc\s+(\d+(?:\.\d+)*)/, stateDirRelPaths: [] },
53
84
  };
54
85
  /**
55
86
  * Parses a dotted version string into numeric components. Trailing
@@ -126,29 +157,54 @@ export async function readDeclaredToolchainMinimums(engineDir) {
126
157
  return minimums;
127
158
  }
128
159
  /** Runs `<binary> --version` and parses the leading version number. */
129
- async function probeHostToolVersion(tool) {
130
- const probe = HOST_PROBES[tool];
131
- const binary = process.env[probe.envVar] ?? tool;
160
+ async function probeBinaryVersion(binary, versionPattern) {
132
161
  const output = await new Promise((resolvePromise) => {
133
162
  execFile(binary, ['--version'], { timeout: 10_000 }, (err, stdout) => {
134
163
  resolvePromise(err ? undefined : stdout);
135
164
  });
136
165
  });
137
- if (output === undefined) {
138
- verbose(`Toolchain preflight: "${binary} --version" failed or not found; skipping ${tool}.`);
166
+ if (output === undefined)
139
167
  return undefined;
140
- }
141
- const match = probe.versionPattern.exec(output);
168
+ const match = versionPattern.exec(output);
142
169
  if (!match?.[1]) {
143
- verbose(`Toolchain preflight: could not parse ${tool} version from "${output.trim()}".`);
170
+ verbose(`Toolchain preflight: could not parse a version from "${output.trim()}" (${binary}).`);
144
171
  return undefined;
145
172
  }
146
173
  return match[1];
147
174
  }
175
+ /**
176
+ * Probes every candidate binary for a tool in mach's resolution order:
177
+ * the env override (which, when set, is the ONLY candidate — configure
178
+ * uses it even when a better binary exists elsewhere), then the mozbuild
179
+ * state-directory copy bootstrap installs, then PATH. Candidates that
180
+ * fail to run or to parse are dropped silently.
181
+ */
182
+ async function probeHostToolCandidates(tool) {
183
+ const probe = HOST_PROBES[tool];
184
+ const envOverride = process.env[probe.envVar];
185
+ const binaries = envOverride
186
+ ? [envOverride]
187
+ : [...probe.stateDirRelPaths.map((rel) => join(mozbuildStateDir(), rel)), tool];
188
+ const candidates = [];
189
+ for (const binary of binaries) {
190
+ const version = await probeBinaryVersion(binary, probe.versionPattern);
191
+ if (version !== undefined) {
192
+ candidates.push({ binary, version });
193
+ }
194
+ else {
195
+ verbose(`Toolchain preflight: "${binary} --version" failed or not found; skipping.`);
196
+ }
197
+ }
198
+ return candidates;
199
+ }
148
200
  /**
149
201
  * Compares the tree-declared toolchain minimums against the host binaries
150
- * `mach configure` will resolve. Returns only DEFINITIVE mismatches; every
151
- * uncertain probe passes silently (see module header for the rationale).
202
+ * `mach configure` will resolve, in configure's own candidate order (env
203
+ * override, mozbuild state dir, PATH). A tool fails ONLY when at least
204
+ * one candidate resolved and none of them meets the minimum; any single
205
+ * passing candidate passes the tool (configure will find it), and a tool
206
+ * with no resolvable candidate skips silently (see module header for the
207
+ * fail-soft rationale).
152
208
  *
153
209
  * @param engineDir - Path to the engine directory
154
210
  */
@@ -159,37 +215,42 @@ export async function runToolchainPreflight(engineDir) {
159
215
  const minimum = parseVersionComponents(minimumVersion);
160
216
  if (!minimum)
161
217
  continue;
162
- const hostVersion = await probeHostToolVersion(tool);
163
- if (hostVersion === undefined)
218
+ const candidates = await probeHostToolCandidates(tool);
219
+ const parsed = candidates.filter((c) => parseVersionComponents(c.version) !== undefined);
220
+ if (parsed.length === 0)
164
221
  continue;
165
- const host = parseVersionComponents(hostVersion);
166
- if (!host)
222
+ const satisfying = parsed.find((c) => {
223
+ const components = parseVersionComponents(c.version);
224
+ return components !== undefined && !isVersionLower(components, minimum);
225
+ });
226
+ if (satisfying) {
227
+ verbose(`Toolchain preflight: ${tool} ${satisfying.version} (${satisfying.binary}) satisfies minimum ${minimumVersion}.`);
167
228
  continue;
168
- if (isVersionLower(host, minimum)) {
169
- mismatches.push({
170
- tool,
171
- hostVersion,
172
- minimumVersion,
173
- declaredIn: MINIMUM_DECLARATIONS[tool].relPath,
174
- });
175
- }
176
- else {
177
- verbose(`Toolchain preflight: ${tool} ${hostVersion} satisfies minimum ${minimumVersion}.`);
178
229
  }
230
+ mismatches.push({
231
+ tool,
232
+ minimumVersion,
233
+ declaredIn: MINIMUM_DECLARATIONS[tool].relPath,
234
+ candidates: parsed,
235
+ });
179
236
  }
180
237
  return mismatches;
181
238
  }
182
239
  /**
183
240
  * Formats the fail-fast message for definitive preflight mismatches,
184
- * naming `fireforge bootstrap` as the remedy (mach's own configure error
241
+ * listing every candidate probed (in mach's resolution order) and naming
242
+ * `fireforge bootstrap` as the remedy (mach's own configure error
185
243
  * suggests `./mach bootstrap`, which is the wrong entry point for a
186
244
  * FireForge-managed repo).
187
245
  */
188
246
  export function formatToolchainMismatchMessage(mismatches) {
189
- const lines = mismatches.map((m) => ` - ${m.tool} ${m.hostVersion} is older than the minimum ${m.minimumVersion} declared by this Firefox source (engine/${m.declaredIn})`);
247
+ const lines = mismatches.map((m) => {
248
+ const probed = m.candidates.map((c) => `${c.version} (${c.binary})`).join(', ');
249
+ return (` - ${m.tool}: no resolvable candidate meets the minimum ${m.minimumVersion} declared by ` +
250
+ `this Firefox source (engine/${m.declaredIn}); probed in mach's resolution order: ${probed}`);
251
+ });
190
252
  return ('Toolchain preflight found host tools older than what this Firefox source requires:\n' +
191
253
  `${lines.join('\n')}\n\n` +
192
254
  'This typically happens after "fireforge download --force" moved the engine to a new ' +
193
255
  'Firefox major version. Run "fireforge bootstrap" to update the toolchain, then retry the build.');
194
256
  }
195
- //# sourceMappingURL=toolchain-preflight.js.map
@@ -60,6 +60,7 @@ interface ChromeUtilsShim {
60
60
  defineESModuleGetters(target: any, modules: Record<string, string>): void;
61
61
  importESModule(specifier: string): any;
62
62
  import(specifier: string): any;
63
+ registerWindowActor(name: string, options: Record<string, any>): void;
63
64
  defineModuleGetter(target: any, name: string, specifier: string): void;
64
65
  defineLazyGetter(target: any, name: string, getter: () => any): void;
65
66
  generateQI(interfaces: any[]): Function;
@@ -85,6 +86,18 @@ declare var gBrowser: any;
85
86
  declare var gURLBar: any;
86
87
  declare var gNavigatorBundle: any;
87
88
  declare var AppConstants: any;
89
+ declare class JSWindowActorChild {
90
+ readonly browsingContext: any;
91
+ readonly contentWindow: any;
92
+ readonly document: any;
93
+ sendAsyncMessage(name: string, data?: any, transfers?: any[]): void;
94
+ sendQuery(name: string, data?: any, transfers?: any[]): Promise<any>;
95
+ }
96
+ declare class JSWindowActorParent {
97
+ readonly browsingContext: any;
98
+ sendAsyncMessage(name: string, data?: any, transfers?: any[]): void;
99
+ sendQuery(name: string, data?: any, transfers?: any[]): Promise<any>;
100
+ }
88
101
  // Fluent localization — a stable chrome global. Members stay loose (any),
89
102
  // but the constructor shape is declared so "new Localization([...])" and
90
103
  // "new Localization([...], true)" typecheck without a local cast. Both
@@ -213,4 +226,3 @@ export async function composeShimSource(projectRoot, extraShimPath) {
213
226
  extraShimAppended: true,
214
227
  };
215
228
  }
216
- //# sourceMappingURL=typecheck-shim.js.map
@@ -327,4 +327,3 @@ export function relativeForDisplay(projectRoot, absoluteFile) {
327
327
  return absoluteFile;
328
328
  return rel;
329
329
  }
330
- //# sourceMappingURL=typecheck.js.map
@@ -121,4 +121,3 @@ export async function addDestroyToBrowserInit(engineDir, expression, marker = DE
121
121
  await writeText(filePath, value);
122
122
  return true;
123
123
  }
124
- //# sourceMappingURL=wire-destroy.js.map
@@ -181,4 +181,3 @@ export async function addDomFragment(engineDir, domFilePath, targetPath = DEFAUL
181
181
  await writeText(targetAbsPath, value);
182
182
  return true;
183
183
  }
184
- //# sourceMappingURL=wire-dom-fragment.js.map
@@ -231,4 +231,3 @@ export async function addInitToBrowserInit(engineDir, expression, after, marker
231
231
  await writeText(filePath, value);
232
232
  return true;
233
233
  }
234
- //# sourceMappingURL=wire-init.js.map
@@ -144,4 +144,3 @@ export async function addSubscriptToBrowserMain(engineDir, name, marker = DEFAUL
144
144
  await writeText(filePath, value);
145
145
  return true;
146
146
  }
147
- //# sourceMappingURL=wire-subscript.js.map
@@ -6,4 +6,3 @@ export { addDestroyToBrowserInit } from './wire-destroy.js';
6
6
  export { addDomFragment } from './wire-dom-fragment.js';
7
7
  export { addInitToBrowserInit } from './wire-init.js';
8
8
  export { addSubscriptToBrowserMain } from './wire-subscript.js';
9
- //# sourceMappingURL=wire-targets.js.map
@@ -349,4 +349,3 @@ export function findMethodBody(ast, methodName) {
349
349
  });
350
350
  return found;
351
351
  }
352
- //# sourceMappingURL=wire-utils.js.map
@@ -41,6 +41,7 @@
41
41
  import { readdir, stat } from 'node:fs/promises';
42
42
  import { dirname, join, resolve, sep } from 'node:path';
43
43
  import { pathExists, readJson, readText } from '../utils/fs.js';
44
+ import { escapeRegex } from '../utils/regex.js';
44
45
  import { isObject, isString } from '../utils/validation.js';
45
46
  /**
46
47
  * `[DEFAULT]` section parser shaped to the narrow case we need: pull a
@@ -88,9 +89,6 @@ export function parseAppdirFromToml(tomlText, key) {
88
89
  }
89
90
  return undefined;
90
91
  }
91
- function escapeRegex(input) {
92
- return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
93
- }
94
92
  function stripQuotes(raw) {
95
93
  if (raw.length === 0)
96
94
  return undefined;
@@ -305,4 +303,3 @@ export function operatorAlreadySetAppPath(extraArgs) {
305
303
  }
306
304
  return false;
307
305
  }
308
- //# sourceMappingURL=xpcshell-appdir.js.map
@@ -54,6 +54,35 @@ export declare class InvalidArgumentError extends FireForgeError {
54
54
  constructor(message: string, argument?: string | undefined, cause?: Error);
55
55
  get userMessage(): string;
56
56
  }
57
+ /**
58
+ * Error thrown when a spawned command exceeds its `timeout` option.
59
+ *
60
+ * Every `exec*` helper in `src/utils/process.ts` rejects with this type
61
+ * (instead of Node's bare `AbortError: The operation was aborted`) when the
62
+ * caller-supplied timeout fires. The bare AbortError bit an operator during
63
+ * the 2026-04-24 eval (see {@link GitIndexingTimeoutError} in errors/git.ts):
64
+ * an 854 s git indexing pass died with no command name, no elapsed time, and
65
+ * no hint that a timeout — not git — was responsible. The git path gained a
66
+ * site-local typed error then; this class extends the same courtesy to every
67
+ * other `timeout` caller.
68
+ */
69
+ export declare class ExecTimeoutError extends FireForgeError {
70
+ /** Executable that was spawned (argv[0]). */
71
+ readonly command: string;
72
+ /** Arguments the executable was spawned with. */
73
+ readonly args: readonly string[];
74
+ /** Timeout that elapsed, in milliseconds. */
75
+ readonly timeoutMs: number;
76
+ readonly code: 1;
77
+ constructor(
78
+ /** Executable that was spawned (argv[0]). */
79
+ command: string,
80
+ /** Arguments the executable was spawned with. */
81
+ args: readonly string[],
82
+ /** Timeout that elapsed, in milliseconds. */
83
+ timeoutMs: number, cause?: unknown);
84
+ get userMessage(): string;
85
+ }
57
86
  /**
58
87
  * Error thrown when the user cancels an interactive prompt.
59
88
  */
@@ -78,6 +78,41 @@ export class InvalidArgumentError extends FireForgeError {
78
78
  return msg;
79
79
  }
80
80
  }
81
+ /**
82
+ * Error thrown when a spawned command exceeds its `timeout` option.
83
+ *
84
+ * Every `exec*` helper in `src/utils/process.ts` rejects with this type
85
+ * (instead of Node's bare `AbortError: The operation was aborted`) when the
86
+ * caller-supplied timeout fires. The bare AbortError bit an operator during
87
+ * the 2026-04-24 eval (see {@link GitIndexingTimeoutError} in errors/git.ts):
88
+ * an 854 s git indexing pass died with no command name, no elapsed time, and
89
+ * no hint that a timeout — not git — was responsible. The git path gained a
90
+ * site-local typed error then; this class extends the same courtesy to every
91
+ * other `timeout` caller.
92
+ */
93
+ export class ExecTimeoutError extends FireForgeError {
94
+ command;
95
+ args;
96
+ timeoutMs;
97
+ code = ExitCode.GENERAL_ERROR;
98
+ constructor(
99
+ /** Executable that was spawned (argv[0]). */
100
+ command,
101
+ /** Arguments the executable was spawned with. */
102
+ args,
103
+ /** Timeout that elapsed, in milliseconds. */
104
+ timeoutMs, cause) {
105
+ super(`Command timed out after ${Math.round(timeoutMs / 1000)}s: ${[command, ...args].join(' ')}`, cause);
106
+ this.command = command;
107
+ this.args = args;
108
+ this.timeoutMs = timeoutMs;
109
+ }
110
+ get userMessage() {
111
+ return (`${this.message}\n\n` +
112
+ 'The command was killed because it exceeded its time budget, not because it failed on its own. ' +
113
+ 'If the host is just slow or loaded, re-running may succeed.');
114
+ }
115
+ }
81
116
  /**
82
117
  * Error thrown when the user cancels an interactive prompt.
83
118
  */
@@ -108,4 +143,3 @@ export class CommandError extends Error {
108
143
  this.name = 'CommandError';
109
144
  }
110
145
  }
111
- //# sourceMappingURL=base.js.map
@@ -111,4 +111,3 @@ export class AmbiguousBuildArtifactsError extends BuildError {
111
111
  ' 3. Run the command again');
112
112
  }
113
113
  }
114
- //# sourceMappingURL=build.js.map
@@ -38,5 +38,11 @@ export declare const ExitCode: {
38
38
  * missing profile, etc.).
39
39
  */
40
40
  readonly SMOKE_LAUNCH_FAILURE: 13;
41
+ /**
42
+ * The user cancelled an interactive prompt. 130 = 128 + SIGINT, the
43
+ * conventional "interrupted by the user" code — scripts and CI can tell
44
+ * a deliberate cancellation apart from a real failure (GENERAL_ERROR).
45
+ */
46
+ readonly USER_CANCELLED: 130;
41
47
  };
42
48
  export type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];
@@ -39,5 +39,10 @@ export const ExitCode = {
39
39
  * missing profile, etc.).
40
40
  */
41
41
  SMOKE_LAUNCH_FAILURE: 13,
42
+ /**
43
+ * The user cancelled an interactive prompt. 130 = 128 + SIGINT, the
44
+ * conventional "interrupted by the user" code — scripts and CI can tell
45
+ * a deliberate cancellation apart from a real failure (GENERAL_ERROR).
46
+ */
47
+ USER_CANCELLED: 130,
42
48
  };
43
- //# sourceMappingURL=codes.js.map
@@ -58,4 +58,3 @@ export class InvalidFieldError extends ConfigError {
58
58
  this.actualValue = actualValue;
59
59
  }
60
60
  }
61
- //# sourceMappingURL=config.js.map
@@ -129,4 +129,3 @@ export class PartialEngineExistsError extends DownloadError {
129
129
  ' 3. Re-run with --verbose for the full underlying error and stack trace');
130
130
  }
131
131
  }
132
- //# sourceMappingURL=download.js.map
@@ -5,6 +5,16 @@ import { FireForgeError } from './base.js';
5
5
  export declare class FurnaceError extends FireForgeError {
6
6
  readonly component?: string | undefined;
7
7
  readonly code: 9;
8
- constructor(message: string, component?: string | undefined, cause?: Error);
8
+ constructor(message: string, component?: string | undefined, cause?: Error, options?: {
9
+ /**
10
+ * Suppresses the "run furnace validate" advice line. Set by callers
11
+ * whose message already tells the user to run validate (or that ARE
12
+ * the validate command) — an explicit flag, because the previous
13
+ * `message.includes('furnace validate')` sniffing dropped the advice
14
+ * from any error that merely MENTIONED the command.
15
+ */
16
+ omitValidateAdvice?: boolean;
17
+ });
18
+ private readonly omitValidateAdvice;
9
19
  get userMessage(): string;
10
20
  }
@@ -7,22 +7,23 @@ import { ExitCode } from './codes.js';
7
7
  export class FurnaceError extends FireForgeError {
8
8
  component;
9
9
  code = ExitCode.FURNACE_ERROR;
10
- constructor(message, component, cause) {
10
+ constructor(message, component, cause, options) {
11
11
  super(message, cause);
12
12
  this.component = component;
13
+ this.omitValidateAdvice =
14
+ options?.omitValidateAdvice ?? message.includes('Run "fireforge furnace validate"');
13
15
  }
16
+ omitValidateAdvice;
14
17
  get userMessage() {
15
18
  let msg = this.component
16
19
  ? `Furnace Error (${this.component}): ${this.message}`
17
20
  : `Furnace Error: ${this.message}`;
18
21
  msg += '\n\nTo fix this:\n';
19
22
  msg += ' 1. Check the error message above for specifics\n';
20
- // Avoid circular advice when the error is thrown during validation itself.
21
- if (!this.message.includes('furnace validate')) {
23
+ if (!this.omitValidateAdvice) {
22
24
  msg += ' 2. Run "fireforge furnace validate" to diagnose issues\n';
23
25
  }
24
- msg += ` ${this.message.includes('furnace validate') ? '2' : '3'}. Use "fireforge doctor --repair-furnace" if state is inconsistent`;
26
+ msg += ` ${this.omitValidateAdvice ? '2' : '3'}. Use "fireforge doctor --repair-furnace" if state is inconsistent`;
25
27
  return msg;
26
28
  }
27
29
  }
28
- //# sourceMappingURL=furnace.js.map
@@ -135,4 +135,3 @@ export class GitIndexingTimeoutError extends GitError {
135
135
  ' 3. If the problem persists, check disk throughput and free space; Firefox source indexing on a cold SSD typically completes in 1–3 minutes.');
136
136
  }
137
137
  }
138
- //# sourceMappingURL=git.js.map
@@ -1,4 +1,21 @@
1
1
  import { FireForgeError } from './base.js';
2
+ /**
3
+ * Error thrown when `patches.json` exists but cannot be parsed or validated.
4
+ *
5
+ * This must be a HARD error on every mutating path. The historical behavior
6
+ * collapsed "corrupt" into "absent" (`loadPatchesManifest` returned null for
7
+ * both), so the next `fireforge export` would rebuild the manifest containing
8
+ * only the new patch — silently destroying every other patch's metadata
9
+ * (tiers, descriptions, lintIgnore, staged dependencies) — and a failed
10
+ * export's rollback would then delete `patches.json` outright because the
11
+ * "before" state looked absent (2026-07-05 review, finding H2).
12
+ */
13
+ export declare class PatchManifestCorruptError extends FireForgeError {
14
+ readonly manifestPath: string;
15
+ readonly code: 6;
16
+ constructor(manifestPath: string, cause?: Error);
17
+ get userMessage(): string;
18
+ }
2
19
  /**
3
20
  * Error thrown when patch operations fail.
4
21
  */
@@ -1,6 +1,35 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { FireForgeError } from './base.js';
3
3
  import { ExitCode } from './codes.js';
4
+ /**
5
+ * Error thrown when `patches.json` exists but cannot be parsed or validated.
6
+ *
7
+ * This must be a HARD error on every mutating path. The historical behavior
8
+ * collapsed "corrupt" into "absent" (`loadPatchesManifest` returned null for
9
+ * both), so the next `fireforge export` would rebuild the manifest containing
10
+ * only the new patch — silently destroying every other patch's metadata
11
+ * (tiers, descriptions, lintIgnore, staged dependencies) — and a failed
12
+ * export's rollback would then delete `patches.json` outright because the
13
+ * "before" state looked absent (2026-07-05 review, finding H2).
14
+ */
15
+ export class PatchManifestCorruptError extends FireForgeError {
16
+ manifestPath;
17
+ code = ExitCode.PATCH_ERROR;
18
+ constructor(manifestPath, cause) {
19
+ super(`patches.json exists but could not be parsed: ${cause?.message ?? 'unknown parse error'}`, cause);
20
+ this.manifestPath = manifestPath;
21
+ }
22
+ get userMessage() {
23
+ return (`Patch Manifest Error: ${this.message}\n\n` +
24
+ `Manifest: ${this.manifestPath}\n\n` +
25
+ 'FireForge refuses to modify the patch queue while patches.json is unreadable — ' +
26
+ 'rewriting it now would silently discard the metadata of every patch it lists.\n\n' +
27
+ 'To fix this:\n' +
28
+ ' 1. Open patches.json and repair the syntax error (a recent hand-edit is the usual cause)\n' +
29
+ ' 2. Or restore patches.json from version control\n' +
30
+ ' 3. Then re-run the command');
31
+ }
32
+ }
4
33
  /**
5
34
  * Error thrown when patch operations fail.
6
35
  */
@@ -23,4 +52,3 @@ export class PatchError extends FireForgeError {
23
52
  return msg;
24
53
  }
25
54
  }
26
- //# sourceMappingURL=patch.js.map