@hominis/fireforge 0.44.4 → 0.46.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 (378) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/dist/bin/fireforge.js +6 -0
  3. package/dist/build-info.json +4 -4
  4. package/dist/src/cli-usage-error.d.ts +46 -0
  5. package/dist/src/cli-usage-error.js +81 -0
  6. package/dist/src/cli.d.ts +5 -1
  7. package/dist/src/cli.js +20 -21
  8. package/dist/src/commands/bootstrap.js +1 -2
  9. package/dist/src/commands/build.js +15 -6
  10. package/dist/src/commands/destructive-decision.d.ts +19 -0
  11. package/dist/src/commands/destructive-decision.js +31 -0
  12. package/dist/src/commands/doctor-check-core.d.ts +0 -6
  13. package/dist/src/commands/doctor-check-core.js +0 -8
  14. package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
  15. package/dist/src/commands/doctor-orphaned-harness.js +4 -12
  16. package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
  17. package/dist/src/commands/doctor-source-pin.d.ts +29 -0
  18. package/dist/src/commands/doctor-source-pin.js +81 -0
  19. package/dist/src/commands/doctor.d.ts +2 -0
  20. package/dist/src/commands/doctor.js +9 -8
  21. package/dist/src/commands/download.js +35 -24
  22. package/dist/src/commands/export-all.js +21 -4
  23. package/dist/src/commands/export-flow.js +15 -60
  24. package/dist/src/commands/export-placement-conflicts.js +3 -3
  25. package/dist/src/commands/export-placement-gate.js +2 -8
  26. package/dist/src/commands/export-placement-policy.js +3 -5
  27. package/dist/src/commands/export-shared.d.ts +42 -23
  28. package/dist/src/commands/export-shared.js +4 -19
  29. package/dist/src/commands/export.js +10 -1
  30. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  31. package/dist/src/commands/furnace/authoring-config.js +19 -0
  32. package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
  33. package/dist/src/commands/furnace/create-browser-test.js +3 -8
  34. package/dist/src/commands/furnace/create-dry-run.js +2 -5
  35. package/dist/src/commands/furnace/create-templates.d.ts +17 -1
  36. package/dist/src/commands/furnace/create-templates.js +2 -1
  37. package/dist/src/commands/furnace/create.js +25 -19
  38. package/dist/src/commands/furnace/deploy.js +11 -2
  39. package/dist/src/commands/furnace/init.js +2 -1
  40. package/dist/src/commands/furnace/override.js +28 -19
  41. package/dist/src/commands/furnace/refresh.js +9 -1
  42. package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
  43. package/dist/src/commands/furnace/remove-mutations.js +0 -1
  44. package/dist/src/commands/furnace/remove.js +2 -6
  45. package/dist/src/commands/furnace/rename-browser-test.js +1 -7
  46. package/dist/src/commands/furnace/rename.js +29 -29
  47. package/dist/src/commands/furnace/status.js +12 -9
  48. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  49. package/dist/src/commands/furnace/test-file-name.js +39 -0
  50. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  51. package/dist/src/commands/furnace/validation-output.js +3 -8
  52. package/dist/src/commands/import.js +2 -9
  53. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  54. package/dist/src/commands/lint-per-patch.js +19 -4
  55. package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
  56. package/dist/src/commands/lint.d.ts +2 -1
  57. package/dist/src/commands/lint.js +1 -1
  58. package/dist/src/commands/manifest.js +1 -1
  59. package/dist/src/commands/package.js +2 -4
  60. package/dist/src/commands/patch/compact.js +14 -15
  61. package/dist/src/commands/patch/delete.js +15 -25
  62. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  63. package/dist/src/commands/patch/entry-projection.js +24 -0
  64. package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
  65. package/dist/src/commands/patch/lint-ignore.js +19 -29
  66. package/dist/src/commands/patch/move-files-create.js +24 -10
  67. package/dist/src/commands/patch/move-files-into.js +38 -37
  68. package/dist/src/commands/patch/move-files.js +4 -13
  69. package/dist/src/commands/patch/rename.js +27 -41
  70. package/dist/src/commands/patch/reorder.js +25 -38
  71. package/dist/src/commands/patch/split-plan.d.ts +4 -5
  72. package/dist/src/commands/patch/split-plan.js +4 -11
  73. package/dist/src/commands/patch/split.js +45 -43
  74. package/dist/src/commands/patch/staged-dependency.js +16 -22
  75. package/dist/src/commands/patch/tier.js +15 -25
  76. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  77. package/dist/src/commands/patch-rename-map.js +36 -0
  78. package/dist/src/commands/pending-resolution.d.ts +15 -0
  79. package/dist/src/commands/pending-resolution.js +25 -0
  80. package/dist/src/commands/re-export-adjacent.d.ts +32 -2
  81. package/dist/src/commands/re-export-adjacent.js +51 -10
  82. package/dist/src/commands/re-export-bulk-scan.js +6 -10
  83. package/dist/src/commands/re-export-drift.d.ts +8 -0
  84. package/dist/src/commands/re-export-drift.js +65 -4
  85. package/dist/src/commands/re-export-files.d.ts +2 -3
  86. package/dist/src/commands/re-export-files.js +38 -32
  87. package/dist/src/commands/re-export-lint.d.ts +3 -4
  88. package/dist/src/commands/re-export-lint.js +9 -2
  89. package/dist/src/commands/re-export-options.js +19 -5
  90. package/dist/src/commands/re-export-register.js +5 -1
  91. package/dist/src/commands/re-export-scan.js +8 -8
  92. package/dist/src/commands/re-export-single.d.ts +22 -3
  93. package/dist/src/commands/re-export-single.js +19 -11
  94. package/dist/src/commands/re-export.js +44 -27
  95. package/dist/src/commands/rebase/abort.js +3 -8
  96. package/dist/src/commands/rebase/continue.js +12 -12
  97. package/dist/src/commands/rebase/index.js +2 -2
  98. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  99. package/dist/src/commands/rebase/patch-loop.js +2 -7
  100. package/dist/src/commands/resolve.js +18 -10
  101. package/dist/src/commands/run.d.ts +0 -13
  102. package/dist/src/commands/run.js +12 -25
  103. package/dist/src/commands/setup-support.d.ts +16 -4
  104. package/dist/src/commands/setup-support.js +16 -16
  105. package/dist/src/commands/setup.js +3 -9
  106. package/dist/src/commands/source.js +8 -1
  107. package/dist/src/commands/status-check.js +2 -2
  108. package/dist/src/commands/status-output.js +2 -2
  109. package/dist/src/commands/status.js +3 -4
  110. package/dist/src/commands/test-build-phase.d.ts +2 -2
  111. package/dist/src/commands/test-build-phase.js +33 -8
  112. package/dist/src/commands/test-diagnose.js +14 -5
  113. package/dist/src/commands/test-modes.d.ts +2 -2
  114. package/dist/src/commands/test-modes.js +0 -1
  115. package/dist/src/commands/test-register.js +6 -3
  116. package/dist/src/commands/test-run.js +29 -21
  117. package/dist/src/commands/test-stale-gate.js +8 -5
  118. package/dist/src/commands/test-verdict.d.ts +16 -2
  119. package/dist/src/commands/test-verdict.js +18 -3
  120. package/dist/src/commands/test.js +51 -7
  121. package/dist/src/commands/token-list.d.ts +26 -0
  122. package/dist/src/commands/token-list.js +153 -0
  123. package/dist/src/commands/token.js +17 -1
  124. package/dist/src/commands/tree.js +7 -2
  125. package/dist/src/commands/typecheck.js +9 -9
  126. package/dist/src/commands/wire.js +4 -3
  127. package/dist/src/core/branding.d.ts +0 -7
  128. package/dist/src/core/branding.js +3 -2
  129. package/dist/src/core/browser-wire.js +6 -3
  130. package/dist/src/core/build-audit-platform.d.ts +29 -0
  131. package/dist/src/core/build-audit-platform.js +140 -0
  132. package/dist/src/core/build-audit-registration.js +3 -2
  133. package/dist/src/core/build-audit-skip.d.ts +57 -0
  134. package/dist/src/core/build-audit-skip.js +112 -0
  135. package/dist/src/core/build-audit.d.ts +15 -2
  136. package/dist/src/core/build-audit.js +154 -12
  137. package/dist/src/core/build-baseline.d.ts +45 -28
  138. package/dist/src/core/build-baseline.js +6 -29
  139. package/dist/src/core/build-jar-escalation.d.ts +64 -0
  140. package/dist/src/core/build-jar-escalation.js +128 -0
  141. package/dist/src/core/build-overwrite-guard.js +1 -1
  142. package/dist/src/core/build-prepare.d.ts +5 -2
  143. package/dist/src/core/build-prepare.js +34 -6
  144. package/dist/src/core/config-paths.d.ts +2 -6
  145. package/dist/src/core/config-paths.js +4 -2
  146. package/dist/src/core/config-state.d.ts +0 -6
  147. package/dist/src/core/config-state.js +1 -19
  148. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  149. package/dist/src/core/config-validate-build-audit.js +80 -0
  150. package/dist/src/core/config-validate-file-size.d.ts +12 -0
  151. package/dist/src/core/config-validate-file-size.js +65 -0
  152. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  153. package/dist/src/core/config-validate-patch-policy.js +3 -3
  154. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  155. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  156. package/dist/src/core/config-validate.js +20 -3
  157. package/dist/src/core/config.d.ts +2 -2
  158. package/dist/src/core/config.js +11 -15
  159. package/dist/src/core/coverage-extend.js +4 -4
  160. package/dist/src/core/discard-baseline.js +7 -7
  161. package/dist/src/core/engine-precondition.d.ts +1 -9
  162. package/dist/src/core/engine-precondition.js +3 -8
  163. package/dist/src/core/engine-session-lock.js +13 -17
  164. package/dist/src/core/export-moved-code-shape.js +2 -2
  165. package/dist/src/core/file-lock-owner.d.ts +103 -0
  166. package/dist/src/core/file-lock-owner.js +259 -0
  167. package/dist/src/core/file-lock.d.ts +2 -7
  168. package/dist/src/core/file-lock.js +40 -91
  169. package/dist/src/core/firefox-cache.d.ts +13 -1
  170. package/dist/src/core/firefox-cache.js +59 -34
  171. package/dist/src/core/firefox-download.d.ts +15 -0
  172. package/dist/src/core/firefox-download.js +37 -21
  173. package/dist/src/core/firefox-extract.d.ts +6 -5
  174. package/dist/src/core/firefox-extract.js +36 -18
  175. package/dist/src/core/firefox.d.ts +30 -32
  176. package/dist/src/core/firefox.js +5 -36
  177. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  178. package/dist/src/core/furnace-apply-dry-run.js +1 -7
  179. package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
  180. package/dist/src/core/furnace-apply-helpers.js +8 -19
  181. package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
  182. package/dist/src/core/furnace-apply.d.ts +1 -1
  183. package/dist/src/core/furnace-apply.js +30 -10
  184. package/dist/src/core/furnace-config.d.ts +1 -1
  185. package/dist/src/core/furnace-config.js +23 -36
  186. package/dist/src/core/furnace-constants.js +3 -2
  187. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  188. package/dist/src/core/furnace-dir-entry.js +21 -0
  189. package/dist/src/core/furnace-operation.d.ts +2 -0
  190. package/dist/src/core/furnace-operation.js +3 -1
  191. package/dist/src/core/furnace-refresh.d.ts +18 -6
  192. package/dist/src/core/furnace-refresh.js +3 -6
  193. package/dist/src/core/furnace-registration-ast.d.ts +0 -1
  194. package/dist/src/core/furnace-registration-ast.js +0 -3
  195. package/dist/src/core/furnace-registration.d.ts +2 -1
  196. package/dist/src/core/furnace-registration.js +22 -29
  197. package/dist/src/core/furnace-scanner.js +3 -6
  198. package/dist/src/core/furnace-state-persist.d.ts +2 -2
  199. package/dist/src/core/furnace-validate-registration.js +5 -7
  200. package/dist/src/core/git-base.d.ts +3 -1
  201. package/dist/src/core/git-base.js +25 -3
  202. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  203. package/dist/src/core/git-diff-new-file.js +119 -0
  204. package/dist/src/core/git-diff.d.ts +9 -9
  205. package/dist/src/core/git-diff.js +88 -157
  206. package/dist/src/core/git-file-ops.d.ts +0 -7
  207. package/dist/src/core/git-file-ops.js +4 -28
  208. package/dist/src/core/git-readonly-index.d.ts +0 -2
  209. package/dist/src/core/git-readonly-index.js +0 -4
  210. package/dist/src/core/git-status.d.ts +7 -0
  211. package/dist/src/core/git-status.js +21 -9
  212. package/dist/src/core/git.d.ts +0 -6
  213. package/dist/src/core/git.js +14 -43
  214. package/dist/src/core/harness-orphans.d.ts +73 -0
  215. package/dist/src/core/harness-orphans.js +187 -0
  216. package/dist/src/core/history-log.d.ts +19 -0
  217. package/dist/src/core/history-log.js +29 -0
  218. package/dist/src/core/mach-error-hints.d.ts +0 -5
  219. package/dist/src/core/mach-error-hints.js +1 -1
  220. package/dist/src/core/mach-known-noise-filter.d.ts +25 -3
  221. package/dist/src/core/mach-known-noise-filter.js +31 -10
  222. package/dist/src/core/mach-mozconfig.js +2 -1
  223. package/dist/src/core/mach-python.d.ts +2 -0
  224. package/dist/src/core/mach-python.js +12 -7
  225. package/dist/src/core/mach-resource-shim.d.ts +13 -1
  226. package/dist/src/core/mach-resource-shim.js +112 -9
  227. package/dist/src/core/mach.d.ts +46 -53
  228. package/dist/src/core/mach.js +31 -83
  229. package/dist/src/core/marionette-port.d.ts +0 -17
  230. package/dist/src/core/marionette-port.js +12 -58
  231. package/dist/src/core/marionette-preflight.js +9 -27
  232. package/dist/src/core/mochitest-server-port.js +3 -3
  233. package/dist/src/core/moz-manifest-rules.js +37 -33
  234. package/dist/src/core/patch-apply-fuzz.js +4 -4
  235. package/dist/src/core/patch-apply.d.ts +4 -5
  236. package/dist/src/core/patch-apply.js +55 -32
  237. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  238. package/dist/src/core/patch-export-coverage.js +1 -30
  239. package/dist/src/core/patch-export-update.d.ts +18 -1
  240. package/dist/src/core/patch-export-update.js +4 -3
  241. package/dist/src/core/patch-export.d.ts +11 -21
  242. package/dist/src/core/patch-export.js +44 -66
  243. package/dist/src/core/patch-files.d.ts +0 -4
  244. package/dist/src/core/patch-files.js +0 -11
  245. package/dist/src/core/patch-lint-binary.d.ts +6 -12
  246. package/dist/src/core/patch-lint-cache.d.ts +17 -1
  247. package/dist/src/core/patch-lint-cache.js +3 -6
  248. package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
  249. package/dist/src/core/patch-lint-checkjs.js +43 -25
  250. package/dist/src/core/patch-lint-creators.d.ts +2 -6
  251. package/dist/src/core/patch-lint-cross.d.ts +16 -8
  252. package/dist/src/core/patch-lint-cross.js +49 -92
  253. package/dist/src/core/patch-lint-css.js +10 -6
  254. package/dist/src/core/patch-lint-file-size.d.ts +52 -0
  255. package/dist/src/core/patch-lint-file-size.js +53 -0
  256. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  257. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  258. package/dist/src/core/patch-lint-jsdoc.js +36 -36
  259. package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
  260. package/dist/src/core/patch-lint-prettier.d.ts +36 -0
  261. package/dist/src/core/patch-lint-prettier.js +126 -0
  262. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  263. package/dist/src/core/patch-lint-queue-types.js +12 -0
  264. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  265. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  266. package/dist/src/core/patch-lint-unmanaged-companion.d.ts +106 -0
  267. package/dist/src/core/patch-lint-unmanaged-companion.js +214 -0
  268. package/dist/src/core/patch-lint.d.ts +14 -9
  269. package/dist/src/core/patch-lint.js +27 -27
  270. package/dist/src/core/patch-lock.js +3 -3
  271. package/dist/src/core/patch-manifest-io.js +8 -7
  272. package/dist/src/core/patch-parse.d.ts +19 -8
  273. package/dist/src/core/patch-parse.js +25 -0
  274. package/dist/src/core/patch-policy.js +15 -27
  275. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  276. package/dist/src/core/patch-source-metadata.js +0 -4
  277. package/dist/src/core/patch-transform.d.ts +21 -8
  278. package/dist/src/core/patch-transform.js +38 -10
  279. package/dist/src/core/process-cpu-time.js +1 -1
  280. package/dist/src/core/rebase-session.d.ts +2 -8
  281. package/dist/src/core/rebase-session.js +5 -17
  282. package/dist/src/core/register-browser-content.js +3 -2
  283. package/dist/src/core/register-browser-test.js +2 -1
  284. package/dist/src/core/register-module.js +2 -1
  285. package/dist/src/core/register-scaffold.js +5 -4
  286. package/dist/src/core/register-shared-css.js +2 -1
  287. package/dist/src/core/run-log-redact.d.ts +38 -0
  288. package/dist/src/core/run-log-redact.js +69 -0
  289. package/dist/src/core/run-log.d.ts +13 -2
  290. package/dist/src/core/run-log.js +67 -18
  291. package/dist/src/core/signal-critical.js +2 -4
  292. package/dist/src/core/smoke-patterns.d.ts +0 -1
  293. package/dist/src/core/smoke-patterns.js +18 -11
  294. package/dist/src/core/state-file.d.ts +12 -1
  295. package/dist/src/core/state-file.js +14 -1
  296. package/dist/src/core/status-classify.js +2 -1
  297. package/dist/src/core/test-changed-prefs.js +26 -11
  298. package/dist/src/core/test-harness-crash.js +61 -48
  299. package/dist/src/core/test-harness-verdict-notes.d.ts +55 -0
  300. package/dist/src/core/test-harness-verdict-notes.js +48 -0
  301. package/dist/src/core/test-stale-check.d.ts +23 -8
  302. package/dist/src/core/test-stale-check.js +77 -4
  303. package/dist/src/core/test-stall-triage.js +37 -19
  304. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  305. package/dist/src/core/test-xpcshell-retry.js +14 -11
  306. package/dist/src/core/token-category.d.ts +33 -0
  307. package/dist/src/core/token-category.js +56 -48
  308. package/dist/src/core/token-dark-mode.js +2 -14
  309. package/dist/src/core/token-inventory.d.ts +56 -0
  310. package/dist/src/core/token-inventory.js +109 -0
  311. package/dist/src/core/token-manager.d.ts +1 -1
  312. package/dist/src/core/token-manager.js +1 -1
  313. package/dist/src/core/token-variant.js +21 -22
  314. package/dist/src/core/toolchain-preflight.js +1 -1
  315. package/dist/src/core/tree-cow.d.ts +0 -2
  316. package/dist/src/core/tree-cow.js +2 -2
  317. package/dist/src/core/tree-store.js +4 -5
  318. package/dist/src/core/typecheck.js +2 -2
  319. package/dist/src/core/wire-dom-fragment.js +6 -17
  320. package/dist/src/core/wire-utils.js +5 -17
  321. package/dist/src/core/xpcshell-appdir.js +2 -4
  322. package/dist/src/errors/base.d.ts +53 -1
  323. package/dist/src/errors/base.js +79 -11
  324. package/dist/src/errors/build.d.ts +28 -0
  325. package/dist/src/errors/build.js +51 -10
  326. package/dist/src/errors/config.d.ts +0 -14
  327. package/dist/src/errors/config.js +13 -34
  328. package/dist/src/errors/download.d.ts +13 -0
  329. package/dist/src/errors/download.js +53 -24
  330. package/dist/src/errors/furnace.js +8 -9
  331. package/dist/src/errors/git.d.ts +8 -8
  332. package/dist/src/errors/git.js +20 -30
  333. package/dist/src/errors/patch.js +8 -10
  334. package/dist/src/errors/rebase.js +7 -7
  335. package/dist/src/types/commands/options.d.ts +96 -123
  336. package/dist/src/types/commands/patches.d.ts +17 -6
  337. package/dist/src/types/config.d.ts +75 -2
  338. package/dist/src/types/index.d.ts +0 -1
  339. package/dist/src/utils/assert.d.ts +3 -15
  340. package/dist/src/utils/assert.js +1 -18
  341. package/dist/src/utils/build-info.d.ts +1 -1
  342. package/dist/src/utils/child-env.d.ts +2 -1
  343. package/dist/src/utils/elapsed.js +10 -1
  344. package/dist/src/utils/fs.d.ts +4 -2
  345. package/dist/src/utils/fs.js +11 -14
  346. package/dist/src/utils/hash.d.ts +12 -0
  347. package/dist/src/utils/hash.js +16 -0
  348. package/dist/src/utils/line-dispatch.d.ts +15 -0
  349. package/dist/src/utils/line-dispatch.js +48 -0
  350. package/dist/src/utils/options.d.ts +1 -7
  351. package/dist/src/utils/options.js +6 -7
  352. package/dist/src/utils/package-root.js +3 -12
  353. package/dist/src/utils/platform.d.ts +0 -4
  354. package/dist/src/utils/process-group.d.ts +2 -1
  355. package/dist/src/utils/process-group.js +20 -19
  356. package/dist/src/utils/process.d.ts +40 -26
  357. package/dist/src/utils/process.js +115 -166
  358. package/dist/src/utils/ps-duration.js +3 -3
  359. package/dist/src/utils/sleep.d.ts +26 -0
  360. package/dist/src/utils/sleep.js +31 -0
  361. package/dist/src/utils/validation.d.ts +2 -38
  362. package/dist/src/utils/validation.js +7 -16
  363. package/docs/configuration.md +136 -1
  364. package/docs/exit-codes.md +34 -20
  365. package/docs/lifecycle-invariants.md +18 -12
  366. package/docs/machine-output.md +31 -8
  367. package/docs/patch-workflow.md +2 -1
  368. package/docs/run-logs.md +44 -4
  369. package/docs/testing.md +70 -11
  370. package/package.json +8 -7
  371. package/dist/src/commands/rebase.d.ts +0 -4
  372. package/dist/src/commands/rebase.js +0 -5
  373. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  374. package/dist/src/core/patch-artifact-normalize.js +0 -24
  375. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  376. package/dist/src/core/patch-lint-reexports.js +0 -8
  377. package/dist/src/core/wire-targets.d.ts +0 -7
  378. package/dist/src/core/wire-targets.js +0 -8
@@ -2,10 +2,8 @@
2
2
  /**
3
3
  * Policy-aware checks for export placement plans.
4
4
  */
5
+ import { formatOrderRange, formatPatchOrder } from '../core/patch-parse.js';
5
6
  import { InvalidArgumentError } from '../errors/base.js';
6
- function reservedRangeLabel(range) {
7
- return `${String(range.from).padStart(3, '0')}-${String(range.to).padStart(3, '0')}`;
8
- }
9
7
  function findReservedRange(config, order) {
10
8
  return (config.patchPolicy?.reservedRanges?.find((range) => order >= range.from && order <= range.to) ??
11
9
  null);
@@ -42,11 +40,11 @@ export function assertPlacementAvoidsReservedRanges(plan, manifestPatches, confi
42
40
  (oldOrder !== undefined ? findReservedRange(config, oldOrder) : null);
43
41
  if (reserved === null)
44
42
  continue;
45
- const label = reservedRangeLabel(reserved);
43
+ const label = formatOrderRange(reserved);
46
44
  const freeOrder = firstFreeOrderBelowReservedRange(manifestPatches, reserved);
47
45
  if (freeOrder !== null) {
48
46
  throw new InvalidArgumentError(`Positional insert would renumber the reserved range ${label}; ` +
49
- `pass --order ${String(freeOrder).padStart(3, '0')} (first free order below the ` +
47
+ `pass --order ${formatPatchOrder(freeOrder)} (first free order below the ` +
50
48
  'reserved block) to place the new patch without renumbering reserved patches.', 'export placement');
51
49
  }
52
50
  throw new InvalidArgumentError(`Positional insert would renumber the reserved range ${label}; ` +
@@ -2,29 +2,43 @@ import type { PatchesManifest, PatchLintIssue } from '../types/commands/index.js
2
2
  import type { ExportOptions, PatchCategory } from '../types/commands/index.js';
3
3
  import type { FireForgeConfig } from '../types/config.js';
4
4
  import type { SpinnerHandle } from '../utils/logger.js';
5
+ export interface RunPatchLintOptions {
6
+ /** Engine root directory. */
7
+ engineDir: string;
8
+ /** Files touched by the patch. */
9
+ filesAffected: string[];
10
+ /** Raw unified diff string. */
11
+ diffContent: string;
12
+ /** Project configuration. */
13
+ config: FireForgeConfig;
14
+ /** If true, downgrade errors to warnings. */
15
+ skipLint?: boolean | undefined;
16
+ /** Optional cross-patch context for ownership resolution. */
17
+ patchQueueCtx?: import('../core/patch-lint-cross.js').PatchQueueContext | undefined;
18
+ /**
19
+ * Optional per-patch set of `check` IDs to suppress (threaded from
20
+ * `PatchMetadata.lintIgnore`). Surgical alternative to `--skip-lint` when
21
+ * exactly one advisory rule does not apply to a specific patch — e.g.
22
+ * `large-patch-lines` on a cohesive branding bundle that genuinely cannot
23
+ * be split.
24
+ */
25
+ ignoreChecks?: ReadonlySet<string> | undefined;
26
+ /**
27
+ * Optional explicit tier override (threaded from `PatchMetadata.tier`).
28
+ * Forces the branding-tier thresholds when set, independent of the
29
+ * auto-detect allowlist. When the branding tier is applied, a single
30
+ * `info()` line surfaces the choice so the tier decision is visible rather
31
+ * than silent.
32
+ */
33
+ patchTier?: 'branding' | undefined;
34
+ }
5
35
  /**
6
36
  * Runs the full patch lint pipeline and reports results. Warnings are always
7
37
  * displayed. Errors block the export unless skipLint is true.
8
38
  *
9
- * @param engineDir - Engine root directory
10
- * @param filesAffected - Files touched by the patch
11
- * @param diffContent - Raw unified diff string
12
- * @param config - Project configuration
13
- * @param skipLint - If true, downgrade errors to warnings
14
- * @param patchQueueCtx - Optional cross-patch context for ownership
15
- * resolution
16
- * @param ignoreChecks - Optional per-patch set of `check` IDs to suppress
17
- * (threaded from `PatchMetadata.lintIgnore`). Surgical alternative to
18
- * `--skip-lint` when exactly one advisory rule does not apply to a
19
- * specific patch — e.g. `large-patch-lines` on a cohesive branding bundle
20
- * that genuinely cannot be split.
21
- * @param patchTier - Optional explicit tier override (threaded from
22
- * `PatchMetadata.tier`). Forces the branding-tier thresholds when set,
23
- * independent of the auto-detect allowlist. When the branding tier is
24
- * applied, a single `info()` line surfaces the choice so the tier decision
25
- * is visible rather than silent.
39
+ * @param options - See {@link RunPatchLintOptions}
26
40
  */
27
- export declare function runPatchLint(engineDir: string, filesAffected: string[], diffContent: string, config: FireForgeConfig, skipLint?: boolean, patchQueueCtx?: import('../core/patch-lint-cross.js').PatchQueueContext, ignoreChecks?: ReadonlySet<string>, patchTier?: 'branding'): Promise<void>;
41
+ export declare function runPatchLint(options: RunPatchLintOptions): Promise<void>;
28
42
  /**
29
43
  * Prints a patch-lint issue list with `runPatchLint`'s exact severity
30
44
  * buckets and skip-lint semantics, throwing on errors unless `skipLint`.
@@ -32,17 +46,22 @@ export declare function runPatchLint(engineDir: string, filesAffected: string[],
32
46
  * stored issues through the identical reporter without re-linting.
33
47
  */
34
48
  export declare function reportPatchLintOutcome(issues: PatchLintIssue[], skipLint?: boolean): void;
49
+ /** Patch identity resolved for an export, from flags or interactive prompts. */
50
+ export interface ExportPatchMetadata {
51
+ /** Normalized patch name (without order prefix or extension). */
52
+ patchName: string;
53
+ /** Category the patch is filed under. */
54
+ selectedCategory: PatchCategory;
55
+ /** Human-readable patch description. */
56
+ description: string;
57
+ }
35
58
  /**
36
59
  * Resolves patch metadata interactively or from flags, with shared validation.
37
60
  * @param options - Export command options
38
61
  * @param isInteractive - Whether interactive prompts are allowed
39
62
  * @param commandName - Command name for error/help text
40
63
  */
41
- export declare function promptExportPatchMetadata(options: ExportOptions, isInteractive: boolean, commandName: 'export' | 'export-all', config?: FireForgeConfig): Promise<{
42
- patchName: string;
43
- selectedCategory: PatchCategory;
44
- description: string;
45
- } | null>;
64
+ export declare function promptExportPatchMetadata(options: ExportOptions, isInteractive: boolean, commandName: 'export' | 'export-all', config?: FireForgeConfig): Promise<ExportPatchMetadata | null>;
46
65
  /**
47
66
  * Confirms whether an export may supersede existing patches.
48
67
  * @param patchesDir - Patches directory
@@ -15,25 +15,10 @@ import { describePatchNameProblem, normalizePatchDisplayName, PATCH_CATEGORIES,
15
15
  * Runs the full patch lint pipeline and reports results. Warnings are always
16
16
  * displayed. Errors block the export unless skipLint is true.
17
17
  *
18
- * @param engineDir - Engine root directory
19
- * @param filesAffected - Files touched by the patch
20
- * @param diffContent - Raw unified diff string
21
- * @param config - Project configuration
22
- * @param skipLint - If true, downgrade errors to warnings
23
- * @param patchQueueCtx - Optional cross-patch context for ownership
24
- * resolution
25
- * @param ignoreChecks - Optional per-patch set of `check` IDs to suppress
26
- * (threaded from `PatchMetadata.lintIgnore`). Surgical alternative to
27
- * `--skip-lint` when exactly one advisory rule does not apply to a
28
- * specific patch — e.g. `large-patch-lines` on a cohesive branding bundle
29
- * that genuinely cannot be split.
30
- * @param patchTier - Optional explicit tier override (threaded from
31
- * `PatchMetadata.tier`). Forces the branding-tier thresholds when set,
32
- * independent of the auto-detect allowlist. When the branding tier is
33
- * applied, a single `info()` line surfaces the choice so the tier decision
34
- * is visible rather than silent.
18
+ * @param options - See {@link RunPatchLintOptions}
35
19
  */
36
- export async function runPatchLint(engineDir, filesAffected, diffContent, config, skipLint, patchQueueCtx, ignoreChecks, patchTier) {
20
+ export async function runPatchLint(options) {
21
+ const { engineDir, filesAffected, diffContent, config, skipLint, patchQueueCtx, ignoreChecks, patchTier, } = options;
37
22
  // Compute the tier decision independently of the lint pipeline so it can
38
23
  // be surfaced even when the rule body emitted no issues — a branding patch
39
24
  // under the soft threshold still benefits from operators knowing which
@@ -353,7 +338,7 @@ export async function guardOwnershipOverlap(args) {
353
338
  return true;
354
339
  s.stop();
355
340
  const entries = [...overlap.entries()].sort(([a], [b]) => a.localeCompare(b));
356
- warn(`This export would create cross-patch ownership overlap on ${String(entries.length)} file${entries.length === 1 ? '' : 's'}:`);
341
+ warn(`This export would create cross-patch ownership overlap on ${entries.length} file${entries.length === 1 ? '' : 's'}:`);
357
342
  for (const [file, owners] of entries) {
358
343
  warn(` - ${file} already claimed by: ${owners.join(', ')}`);
359
344
  }
@@ -258,7 +258,16 @@ export async function exportCommand(projectRoot, files, options) {
258
258
  const patchQueueCtx = (await pathExists(paths.patches))
259
259
  ? await buildPatchQueueContext(paths.patches)
260
260
  : undefined;
261
- await runPatchLint(paths.engine, filesAffected, diff, config, options.skipLint, patchQueueCtx, exportIgnoreChecks, options.tier);
261
+ await runPatchLint({
262
+ engineDir: paths.engine,
263
+ filesAffected,
264
+ diffContent: diff,
265
+ config,
266
+ skipLint: options.skipLint,
267
+ patchQueueCtx,
268
+ ignoreChecks: exportIgnoreChecks,
269
+ patchTier: options.tier,
270
+ });
262
271
  // Resolve placement (if any flag was given). Placement is mutually
263
272
  // exclusive with supersede — the semantics overlap confusingly.
264
273
  let placementPlan = null;
@@ -0,0 +1,13 @@
1
+ import type { FurnaceConfig } from '../../types/index.js';
2
+ /**
3
+ * Loads `furnace.json` for an authoring command, defaulting to an empty
4
+ * configuration when the project has none yet.
5
+ *
6
+ * `furnace create` and `furnace override` both need this: they are the two
7
+ * commands that may run before any furnace configuration exists, and both must
8
+ * treat "no file" as "nothing registered yet" rather than an error. They had a
9
+ * byte-identical private copy each.
10
+ * @param projectRoot - Project root to load from.
11
+ * @returns The parsed configuration, or a fresh default.
12
+ */
13
+ export declare function loadAuthoringFurnaceConfig(projectRoot: string): Promise<FurnaceConfig>;
@@ -0,0 +1,19 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { createDefaultFurnaceConfig, furnaceConfigExists, loadFurnaceConfig, } from '../../core/furnace-config.js';
3
+ /**
4
+ * Loads `furnace.json` for an authoring command, defaulting to an empty
5
+ * configuration when the project has none yet.
6
+ *
7
+ * `furnace create` and `furnace override` both need this: they are the two
8
+ * commands that may run before any furnace configuration exists, and both must
9
+ * treat "no file" as "nothing registered yet" rather than an error. They had a
10
+ * byte-identical private copy each.
11
+ * @param projectRoot - Project root to load from.
12
+ * @returns The parsed configuration, or a fresh default.
13
+ */
14
+ export async function loadAuthoringFurnaceConfig(projectRoot) {
15
+ if (await furnaceConfigExists(projectRoot)) {
16
+ return loadFurnaceConfig(projectRoot);
17
+ }
18
+ return createDefaultFurnaceConfig();
19
+ }
@@ -10,7 +10,7 @@ import { loadConfig } from '../../core/config.js';
10
10
  import { stdioIsInteractive } from '../../core/destructive.js';
11
11
  import { completeJournalRollback, runFurnaceMutation, } from '../../core/furnace-operation.js';
12
12
  import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
13
- import { createRollbackJournal, snapshotDir, snapshotFile } from '../../core/furnace-rollback.js';
13
+ import { createRollbackJournal, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
14
14
  import { FurnaceError } from '../../errors/furnace.js';
15
15
  import { pathExists, readText, removeDir, removeFile, writeText } from '../../utils/fs.js';
16
16
  import { cancel, info, intro, isCancel, note, outro } from '../../utils/logger.js';
@@ -16,6 +16,7 @@ import { toError } from '../../utils/errors.js';
16
16
  import { ensureDir, pathExists, readText, writeText } from '../../utils/fs.js';
17
17
  import { info, success, warn } from '../../utils/logger.js';
18
18
  import { stripEnginePrefix } from '../../utils/paths.js';
19
+ import { browserTestFileName, deriveTestStem } from './test-file-name.js';
19
20
  /**
20
21
  * Validates the `--test-dir` option against the resolved test style before
21
22
  * any writes. Mochikit lives in the upstream toolkit/content/tests/widgets
@@ -65,15 +66,9 @@ export function resolveTestDirOverride(raw) {
65
66
  * the harness will try to run.
66
67
  */
67
68
  export async function scaffoldTestFiles(componentName, license, forgeConfig, paths, journal, testDirOverride) {
68
- const strippedName = componentName.startsWith('moz-') ? componentName.slice(4) : componentName;
69
- // Avoid double-prefixing: strip binaryName prefix since the default test
70
- // dir name already uses it
71
69
  const binaryName = forgeConfig.binaryName;
72
- const withoutBinaryPrefix = strippedName.startsWith(binaryName + '-')
73
- ? strippedName.slice(binaryName.length + 1)
74
- : strippedName;
75
- const underscored = withoutBinaryPrefix.replace(/-/g, '_');
76
- const testFileName = `browser_${binaryName}_${underscored}.js`;
70
+ const underscored = deriveTestStem(componentName, binaryName);
71
+ const testFileName = browserTestFileName(componentName, binaryName);
77
72
  // --test-dir redirects the scaffold: the hardcoded
78
73
  // `.../test/<binaryName>/` target can collide with a test suite owned by a
79
74
  // different patch. The manifest-registration name is the path below
@@ -8,6 +8,7 @@
8
8
  * independently of the mutation plumbing.
9
9
  */
10
10
  import { BROWSER_TEST_SCAFFOLD_ROOT, resolveBrowserChromeTestDir, resolveXpcshellTestDir, } from '../../core/furnace-constants.js';
11
+ import { deriveTestStem } from './test-file-name.js';
11
12
  /**
12
13
  * Builds the test-section fragment of the dry-run plan for a given
13
14
  * harness choice. Kept separate from the top-level formatter so the
@@ -18,11 +19,7 @@ function formatTestSection(args) {
18
19
  const { testStyle, componentName, binaryName, testDir } = args;
19
20
  if (testStyle === 'none')
20
21
  return '';
21
- const strippedName = componentName.startsWith('moz-') ? componentName.slice(4) : componentName;
22
- const withoutBinaryPrefix = strippedName.startsWith(binaryName + '-')
23
- ? strippedName.slice(binaryName.length + 1)
24
- : strippedName;
25
- const underscored = withoutBinaryPrefix.replace(/-/g, '_');
22
+ const underscored = deriveTestStem(componentName, binaryName);
26
23
  if (testStyle === 'browser-chrome') {
27
24
  // Same resolver as scaffoldTestFiles: the registration name is the
28
25
  // path below browser/base/content/test/, which under --test-dir is
@@ -3,6 +3,22 @@
3
3
  * command entrypoint so the generator is unit-testable in isolation and the
4
4
  * command file stays under the per-file LOC budget.
5
5
  */
6
+ export interface GenerateMjsContentOptions {
7
+ /** Custom element tag name. */
8
+ name: string;
9
+ /** Generated component class name. */
10
+ className: string;
11
+ /** Human-readable component description. */
12
+ description: string;
13
+ /** Whether to emit the Fluent wiring. */
14
+ localized: boolean;
15
+ /** License header prepended to the module. */
16
+ header: string;
17
+ /** chrome:// sub-path for the generated FTL reference, when localized. */
18
+ ftlChromeSubPath: string | undefined;
19
+ /** Explicit shared FTL path, used verbatim when supplied. */
20
+ sharedFtl: string | undefined;
21
+ }
6
22
  /**
7
23
  * Generates the .mjs file content for a custom component.
8
24
  *
@@ -23,7 +39,7 @@
23
39
  * matching the locale jar.mn entry that `furnace apply` writes.
24
40
  * 3. `<name>.ftl` — fallback when no chrome sub-path was resolvable.
25
41
  */
26
- export declare function generateMjsContent(name: string, className: string, description: string, localized: boolean, header: string, ftlChromeSubPath: string | undefined, sharedFtl: string | undefined): string;
42
+ export declare function generateMjsContent(options: GenerateMjsContentOptions): string;
27
43
  /** Generates the .css file content for a custom component. */
28
44
  export declare function generateCssContent(header: string): string;
29
45
  /** Generates the .ftl file content for a custom component. */
@@ -24,7 +24,8 @@
24
24
  * matching the locale jar.mn entry that `furnace apply` writes.
25
25
  * 3. `<name>.ftl` — fallback when no chrome sub-path was resolvable.
26
26
  */
27
- export function generateMjsContent(name, className, description, localized, header, ftlChromeSubPath, sharedFtl) {
27
+ export function generateMjsContent(options) {
28
+ const { name, className, description, localized, header, ftlChromeSubPath, sharedFtl } = options;
28
29
  // A `*/` in the operator-supplied description would close the JSDoc block
29
30
  // early and leave the remainder as stray code. Escaped here rather than
30
31
  // rejected at input, because the same text is also rendered by
@@ -3,7 +3,7 @@ import { join } from 'node:path';
3
3
  import { text } from '@clack/prompts';
4
4
  import { getProjectPaths, loadConfig } from '../../core/config.js';
5
5
  import { stdioIsInteractive } from '../../core/destructive.js';
6
- import { createDefaultFurnaceConfig, furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig, } from '../../core/furnace-config.js';
6
+ import { getFurnacePaths, writeFurnaceConfig } from '../../core/furnace-config.js';
7
7
  import { resolveFtlChromeSubPath, tagNameToClassName, WIDGETS_DIR, } from '../../core/furnace-constants.js';
8
8
  import { completeJournalRollback, runFurnaceMutation, } from '../../core/furnace-operation.js';
9
9
  import { CUSTOM_ELEMENT_TAG_PATTERN, CUSTOM_ELEMENT_TAG_RULES, describeTagNameProblem, } from '../../core/furnace-registration-validate.js';
@@ -15,6 +15,7 @@ import { InvalidArgumentError } from '../../errors/base.js';
15
15
  import { FurnaceError } from '../../errors/furnace.js';
16
16
  import { ensureDir, pathExists, writeText } from '../../utils/fs.js';
17
17
  import { cancel, intro, isCancel, note, outro } from '../../utils/logger.js';
18
+ import { loadAuthoringFurnaceConfig } from './authoring-config.js';
18
19
  import { resolveValidatedTestDir, scaffoldTestFiles } from './create-browser-test.js';
19
20
  import { formatDryRunPlan, formatSuccessNote } from './create-dry-run.js';
20
21
  import { resolveCreateFeatures } from './create-features.js';
@@ -23,12 +24,6 @@ import { assertCustomEntryPersisted } from './create-readback.js';
23
24
  import { generateCssContent, generateFtlContent, generateMjsContent } from './create-templates.js';
24
25
  import { validateCreateAgainstConfig } from './create-validation.js';
25
26
  import { scaffoldXpcshellTestFiles } from './create-xpcshell.js';
26
- async function loadAuthoringFurnaceConfig(projectRoot) {
27
- if (await furnaceConfigExists(projectRoot)) {
28
- return loadFurnaceConfig(projectRoot);
29
- }
30
- return createDefaultFurnaceConfig();
31
- }
32
27
  function knownComponentSet(config) {
33
28
  return new Set([
34
29
  ...config.stock,
@@ -64,24 +59,25 @@ function validateTagName(name) {
64
59
  }
65
60
  /**
66
61
  * Writes the scaffolded component source files to disk.
67
- * @param componentDir - Destination component directory
68
- * @param componentName - Custom element tag name
69
- * @param className - Generated component class name
70
- * @param description - Human-readable component description
71
- * @param localized - Whether to include a Fluent file
72
- * @param license - Project license used for generated headers
73
- * @param ftlChromeSubPath - chrome:// sub-path for the generated FTL reference, when localized
74
- * @param sharedFtl - Explicit shared FTL path from `--shared-ftl`, when supplied
75
- * @param journal - Optional rollback journal that snapshots files before writes
62
+ * @param options - See {@link WriteComponentFilesOptions}
76
63
  * @returns Relative filenames written for the component
77
64
  */
78
- async function writeComponentFiles(componentDir, componentName, className, description, localized, license, ftlChromeSubPath, sharedFtl, journal) {
65
+ async function writeComponentFiles(options) {
66
+ const { componentDir, componentName, className, description, localized, license, ftlChromeSubPath, sharedFtl, journal, } = options;
79
67
  await ensureDir(componentDir);
80
68
  const files = [`${componentName}.mjs`, `${componentName}.css`];
81
69
  const mjsPath = join(componentDir, `${componentName}.mjs`);
82
70
  if (journal)
83
71
  await snapshotFile(journal, mjsPath);
84
- const mjsContent = generateMjsContent(componentName, className, description, localized, getLicenseHeader(license, 'js'), ftlChromeSubPath, sharedFtl);
72
+ const mjsContent = generateMjsContent({
73
+ name: componentName,
74
+ className,
75
+ description,
76
+ localized,
77
+ header: getLicenseHeader(license, 'js'),
78
+ ftlChromeSubPath,
79
+ sharedFtl,
80
+ });
85
81
  await writeText(mjsPath, mjsContent);
86
82
  const cssPath = join(componentDir, `${componentName}.css`);
87
83
  if (journal)
@@ -165,7 +161,17 @@ async function performCreateMutations(args) {
165
161
  // so signal-driven rollback can clean it up even if writeComponentFiles
166
162
  // is interrupted mid-ensureDir.
167
163
  recordCreatedDir(journal, args.componentDir);
168
- files = await writeComponentFiles(args.componentDir, args.componentName, args.className, args.description, args.localized, args.license, resolveFtlChromeSubPath(freshConfig.ftlBasePath), args.sharedFtl, journal);
164
+ files = await writeComponentFiles({
165
+ componentDir: args.componentDir,
166
+ componentName: args.componentName,
167
+ className: args.className,
168
+ description: args.description,
169
+ localized: args.localized,
170
+ license: args.license,
171
+ ftlChromeSubPath: resolveFtlChromeSubPath(freshConfig.ftlBasePath),
172
+ sharedFtl: args.sharedFtl,
173
+ journal,
174
+ });
169
175
  const customEntry = {
170
176
  description: args.description,
171
177
  targetPath: `${WIDGETS_DIR}/${args.componentName}`,
@@ -2,7 +2,7 @@
2
2
  import { loadConfig } from '../../core/config.js';
3
3
  import { applyAllComponents } from '../../core/furnace-apply.js';
4
4
  import { logApplyResult } from '../../core/furnace-apply-output.js';
5
- import { getFurnacePaths, loadFurnaceConfig } from '../../core/furnace-config.js';
5
+ import { getFurnacePaths } from '../../core/furnace-config.js';
6
6
  import { reportJsconfigPathsSync } from '../../core/furnace-jsconfig.js';
7
7
  import { runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
8
8
  import { assertFurnaceReady } from '../../core/furnace-precondition.js';
@@ -203,7 +203,16 @@ export async function furnaceDeployCommand(projectRoot, name, options = {}) {
203
203
  }
204
204
  const validateSpinner = spinner(isDryRun ? 'Validating (read-only)...' : 'Validating...');
205
205
  const failedComponents = getFailedComponentNames(result);
206
- const validation = await runDeployValidation(validateSpinner, name, config, furnacePaths, failedComponents, isDryRun, projectRoot, result.actions);
206
+ const validation = await runDeployValidation({
207
+ validateSpinner,
208
+ name,
209
+ config,
210
+ furnacePaths,
211
+ failedComponents,
212
+ isDryRun,
213
+ projectRoot,
214
+ dryRunActions: result.actions,
215
+ });
207
216
  if (validation.done)
208
217
  return;
209
218
  const { totalErrors, totalWarnings, componentCount, skippedValidationCount } = validation;
@@ -13,6 +13,7 @@ import { FurnaceError } from '../../errors/furnace.js';
13
13
  import { getNodeErrorCode, toError } from '../../utils/errors.js';
14
14
  import { ensureDir, pathExists, writeText } from '../../utils/fs.js';
15
15
  import { cancel, info, intro, isCancel, note, outro, success, warn } from '../../utils/logger.js';
16
+ import { normalizePathSlashes } from '../../utils/paths.js';
16
17
  /**
17
18
  * File extensions that are definitely FTL resources (not locale
18
19
  * directories). A value ending in one of these is almost certainly the
@@ -64,7 +65,7 @@ async function validateFtlBasePath(value, engineDir) {
64
65
  // Normalize with the POSIX rules regardless of host: `normalize` on Windows
65
66
  // emits backslashes, so a `../`-prefix test against the platform `normalize`
66
67
  // silently passes every traversal attempt there.
67
- const normalized = posix.normalize(value.replace(/\\/g, '/'));
68
+ const normalized = posix.normalize(normalizePathSlashes(value));
68
69
  if (normalized === '..' || normalized.startsWith('../')) {
69
70
  throw new FurnaceError(`ftlBasePath "${value}" must not escape the engine checkout via parent-directory segments.`);
70
71
  }
@@ -4,7 +4,7 @@ import { dirname, join } from 'node:path';
4
4
  import { select, text } from '@clack/prompts';
5
5
  import { getProjectPaths, loadConfig, loadState } from '../../core/config.js';
6
6
  import { stdioIsInteractive } from '../../core/destructive.js';
7
- import { createDefaultFurnaceConfig, furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig, } from '../../core/furnace-config.js';
7
+ import { getFurnacePaths, writeFurnaceConfig } from '../../core/furnace-config.js';
8
8
  import { resolveFtlDir } from '../../core/furnace-constants.js';
9
9
  import { completeJournalRollback, runFurnaceMutation } from '../../core/furnace-operation.js';
10
10
  import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
@@ -16,25 +16,14 @@ import { FurnaceError } from '../../errors/furnace.js';
16
16
  import { toError } from '../../utils/errors.js';
17
17
  import { copyFile, ensureDir, pathExists, writeJson } from '../../utils/fs.js';
18
18
  import { cancel, info, intro, isCancel, note, outro, warn } from '../../utils/logger.js';
19
- async function loadAuthoringFurnaceConfig(projectRoot) {
20
- if (await furnaceConfigExists(projectRoot)) {
21
- return loadFurnaceConfig(projectRoot);
22
- }
23
- return createDefaultFurnaceConfig();
24
- }
19
+ import { loadAuthoringFurnaceConfig } from './authoring-config.js';
25
20
  /**
26
21
  * Copies the source files needed for a new override into the workspace.
27
- * @param engineDir - Absolute path to the engine checkout
28
- * @param srcDir - Original component directory in the engine checkout
29
- * @param destDir - Destination override directory in the workspace
30
- * @param componentName - Custom element tag name being overridden
31
- * @param hasFTL - Whether the component ships a localized `.ftl` file
32
- * @param overrideType - Requested override mode
33
- * @param ftlDir - Engine-relative directory the shared `.ftl` deploys into
34
- * @param journal - Rollback journal every write is recorded in
22
+ * @param options - See {@link CopyOverrideFilesOptions}
35
23
  * @returns Filenames copied into the override directory
36
24
  */
37
- async function copyOverrideFiles(engineDir, srcDir, destDir, componentName, hasFTL, overrideType, ftlDir, journal) {
25
+ async function copyOverrideFiles(options) {
26
+ const { engineDir, srcDir, destDir, componentName, hasFTL, overrideType, ftlDir, journal } = options;
38
27
  await ensureDir(destDir);
39
28
  const entries = await readdir(srcDir, { withFileTypes: true });
40
29
  const copiedFiles = [];
@@ -93,7 +82,8 @@ async function copyOverrideFiles(engineDir, srcDir, destDir, componentName, hasF
93
82
  * cycles into a single surviving entry — otherwise both report success and
94
83
  * furnace.json keeps only the second writer's.
95
84
  */
96
- async function saveOverrideConfig(projectRoot, destDir, componentName, overrideType, description, details, firefoxVersion, journal, baseCommit) {
85
+ async function saveOverrideConfig(options) {
86
+ const { projectRoot, destDir, componentName, overrideType, description, details, firefoxVersion, journal, baseCommit, } = options;
97
87
  const overrideJson = {
98
88
  type: overrideType,
99
89
  description,
@@ -138,9 +128,28 @@ async function performOverrideMutations(args) {
138
128
  ctx.registerJournal(journal);
139
129
  recordCreatedDir(journal, args.destDir);
140
130
  try {
141
- const filesCopied = await copyOverrideFiles(args.engineDir, args.srcDir, args.destDir, args.componentName, args.details.hasFTL, args.overrideType, args.ftlDir, journal);
131
+ const filesCopied = await copyOverrideFiles({
132
+ engineDir: args.engineDir,
133
+ srcDir: args.srcDir,
134
+ destDir: args.destDir,
135
+ componentName: args.componentName,
136
+ hasFTL: args.details.hasFTL,
137
+ overrideType: args.overrideType,
138
+ ftlDir: args.ftlDir,
139
+ journal,
140
+ });
142
141
  await snapshotFile(journal, args.furnacePaths.furnaceConfig);
143
- await saveOverrideConfig(args.projectRoot, args.destDir, args.componentName, args.overrideType, args.description, args.details, args.firefoxVersion, journal, args.baseCommit);
142
+ await saveOverrideConfig({
143
+ projectRoot: args.projectRoot,
144
+ destDir: args.destDir,
145
+ componentName: args.componentName,
146
+ overrideType: args.overrideType,
147
+ description: args.description,
148
+ details: args.details,
149
+ firefoxVersion: args.firefoxVersion,
150
+ journal,
151
+ baseCommit: args.baseCommit,
152
+ });
144
153
  return filesCopied;
145
154
  }
146
155
  catch (error) {
@@ -126,7 +126,15 @@ async function refreshSingleOverride(projectRoot, name, options = {}) {
126
126
  for (const entry of overrideFiles) {
127
127
  const overridePath = join(overrideDir, entry.name);
128
128
  const engineRelPath = getOverrideEngineTargetPath(paths.engine, overrideConfig, entry.name, ftlDir).slice(paths.engine.length + 1);
129
- const result = await refreshOverrideFile(paths.engine, overridePath, engineRelPath, baseCommit, entry.name, dryRun, strategy);
129
+ const result = await refreshOverrideFile({
130
+ engineDir: paths.engine,
131
+ overridePath,
132
+ engineRelPath,
133
+ baseCommit,
134
+ fileName: entry.name,
135
+ dryRun,
136
+ strategy,
137
+ });
130
138
  fileResults.push(result);
131
139
  }
132
140
  // Update baseVersion and baseCommit on clean merge (not dry-run, no conflicts)
@@ -1,7 +1,6 @@
1
- import type { loadFurnaceConfig } from '../../core/furnace-config.js';
2
- import { getFurnacePaths } from '../../core/furnace-config.js';
1
+ import type { FurnacePaths } from '../../core/furnace-config.js';
3
2
  import type { RollbackJournal } from '../../core/furnace-rollback.js';
4
- import type { FurnaceState } from '../../types/furnace.js';
3
+ import type { FurnaceConfig, FurnaceState } from '../../types/furnace.js';
5
4
  /**
6
5
  * Engine-side and workspace-side removal for an OVERRIDE component.
7
6
  *
@@ -14,8 +13,8 @@ export declare function performOverrideRemovalMutations(args: {
14
13
  paths: {
15
14
  engine: string;
16
15
  };
17
- furnacePaths: ReturnType<typeof getFurnacePaths>;
18
- freshConfig: Awaited<ReturnType<typeof loadFurnaceConfig>>;
16
+ furnacePaths: FurnacePaths;
17
+ freshConfig: FurnaceConfig;
19
18
  freshState: FurnaceState;
20
19
  ftlDir: string;
21
20
  journal: RollbackJournal;
@@ -30,8 +29,8 @@ export declare function performCustomRemovalMutations(args: {
30
29
  paths: {
31
30
  engine: string;
32
31
  };
33
- furnacePaths: ReturnType<typeof getFurnacePaths>;
34
- freshConfig: Awaited<ReturnType<typeof loadFurnaceConfig>>;
32
+ furnacePaths: FurnacePaths;
33
+ freshConfig: FurnaceConfig;
35
34
  ftlDir: string;
36
35
  journal: RollbackJournal;
37
36
  }): Promise<void>;
@@ -16,7 +16,6 @@ import { join } from 'node:path';
16
16
  import { removeCustomFtlJarMnEntry } from '../../core/furnace-apply-ftl.js';
17
17
  import { getOverrideEngineTargetPath, isOverrideCopyCandidate, restoreOverrideFileToBaseline, } from '../../core/furnace-apply-helpers.js';
18
18
  import { extractComponentChecksums } from '../../core/furnace-checksum-utils.js';
19
- import { getFurnacePaths } from '../../core/furnace-config.js';
20
19
  import { removeCustomElementRegistration, removeJarMnEntries, } from '../../core/furnace-registration.js';
21
20
  import { snapshotDir, snapshotFile } from '../../core/furnace-rollback.js';
22
21
  import { isGitRepository } from '../../core/git.js';
@@ -16,6 +16,7 @@ import { pathExists, readText, removeDir, writeText } from '../../utils/fs.js';
16
16
  import { cancel, info, intro, isCancel, outro, warn } from '../../utils/logger.js';
17
17
  import { performCustomRemovalMutations, performOverrideRemovalMutations, } from './remove-mutations.js';
18
18
  import { dropChecksumsByPrefix } from './remove-state.js';
19
+ import { browserTestFileName } from './test-file-name.js';
19
20
  /**
20
21
  * Removes an entire TOML section (header + body lines) for a given test file.
21
22
  * Matches from `["filename"]` up to the next section header `[` or end-of-file,
@@ -94,12 +95,7 @@ async function cleanupCustomTestFiles(name, projectRoot, journal) {
94
95
  }
95
96
  const paths = getProjectPaths(projectRoot);
96
97
  const binaryName = forgeConfig.binaryName;
97
- const strippedName = name.startsWith('moz-') ? name.slice(4) : name;
98
- const withoutBinaryPrefix = strippedName.startsWith(binaryName + '-')
99
- ? strippedName.slice(binaryName.length + 1)
100
- : strippedName;
101
- const underscored = withoutBinaryPrefix.replace(/-/g, '_');
102
- const testFileName = `browser_${binaryName}_${underscored}.js`;
98
+ const testFileName = browserTestFileName(name, binaryName);
103
99
  const testDir = join(paths.engine, 'browser/base/content/test', binaryName);
104
100
  if (!(await pathExists(testDir)))
105
101
  return { partialFailures };
@@ -1,13 +1,7 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { tagNameToClassName } from '../../core/furnace-constants.js';
3
3
  import { escapeRegex } from '../../utils/regex.js';
4
- function deriveTestStem(componentName, binaryName) {
5
- const strippedName = componentName.startsWith('moz-') ? componentName.slice(4) : componentName;
6
- const withoutBinaryPrefix = strippedName.startsWith(binaryName + '-')
7
- ? strippedName.slice(binaryName.length + 1)
8
- : strippedName;
9
- return withoutBinaryPrefix.replace(/-/g, '_');
10
- }
4
+ import { deriveTestStem } from './test-file-name.js';
11
5
  /** Rewrites scaffolded browser-chrome test literals after a component rename. */
12
6
  export function updateBrowserChromeTestContent(content, oldName, newName, binaryName) {
13
7
  const oldClassName = tagNameToClassName(oldName);