@hominis/fireforge 0.35.0 → 0.37.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 (369) hide show
  1. package/CHANGELOG.md +42 -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 +5 -4
  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-orphaned-harness.d.ts +52 -0
  19. package/dist/src/commands/doctor-orphaned-harness.js +132 -0
  20. package/dist/src/commands/doctor-working-tree.js +0 -1
  21. package/dist/src/commands/doctor.js +6 -1
  22. package/dist/src/commands/download.js +28 -13
  23. package/dist/src/commands/export-all.js +5 -3
  24. package/dist/src/commands/export-flow.js +12 -7
  25. package/dist/src/commands/export-placement-gate.js +0 -1
  26. package/dist/src/commands/export-placement-policy.js +0 -1
  27. package/dist/src/commands/export-shared.d.ts +5 -1
  28. package/dist/src/commands/export-shared.js +18 -6
  29. package/dist/src/commands/export.js +22 -10
  30. package/dist/src/commands/furnace/apply.js +22 -6
  31. package/dist/src/commands/furnace/chrome-doc-remove.js +0 -1
  32. package/dist/src/commands/furnace/chrome-doc-templates.js +0 -1
  33. package/dist/src/commands/furnace/chrome-doc-tests.js +0 -1
  34. package/dist/src/commands/furnace/chrome-doc.js +0 -1
  35. package/dist/src/commands/furnace/create-browser-test.js +0 -1
  36. package/dist/src/commands/furnace/create-dry-run.js +0 -1
  37. package/dist/src/commands/furnace/create-features.js +0 -1
  38. package/dist/src/commands/furnace/create-mochikit.js +0 -1
  39. package/dist/src/commands/furnace/create-readback.js +0 -1
  40. package/dist/src/commands/furnace/create-templates.js +0 -1
  41. package/dist/src/commands/furnace/create-validation.js +0 -1
  42. package/dist/src/commands/furnace/create-xpcshell.js +0 -1
  43. package/dist/src/commands/furnace/create.js +0 -1
  44. package/dist/src/commands/furnace/deploy.js +7 -83
  45. package/dist/src/commands/furnace/diff.js +0 -1
  46. package/dist/src/commands/furnace/index.js +59 -25
  47. package/dist/src/commands/furnace/init.js +0 -1
  48. package/dist/src/commands/furnace/list.js +0 -1
  49. package/dist/src/commands/furnace/override.js +0 -1
  50. package/dist/src/commands/furnace/preview.js +3 -3
  51. package/dist/src/commands/furnace/refresh.js +0 -1
  52. package/dist/src/commands/furnace/remove-state.d.ts +15 -0
  53. package/dist/src/commands/furnace/remove-state.js +47 -1
  54. package/dist/src/commands/furnace/remove.js +3 -9
  55. package/dist/src/commands/furnace/rename-browser-test.js +1 -5
  56. package/dist/src/commands/furnace/rename-helpers.d.ts +6 -0
  57. package/dist/src/commands/furnace/rename-helpers.js +30 -1
  58. package/dist/src/commands/furnace/rename-xpcshell.js +7 -6
  59. package/dist/src/commands/furnace/rename.js +15 -36
  60. package/dist/src/commands/furnace/scan.js +0 -1
  61. package/dist/src/commands/furnace/status.js +0 -1
  62. package/dist/src/commands/furnace/sync.js +76 -4
  63. package/dist/src/commands/furnace/validate.js +0 -1
  64. package/dist/src/commands/furnace/validation-output.js +0 -1
  65. package/dist/src/commands/import.js +35 -9
  66. package/dist/src/commands/lint-per-patch.js +19 -7
  67. package/dist/src/commands/lint.js +13 -3
  68. package/dist/src/commands/manifest.js +1 -2
  69. package/dist/src/commands/package.js +2 -1
  70. package/dist/src/commands/patch/compact.js +1 -1
  71. package/dist/src/commands/patch/delete.js +1 -1
  72. package/dist/src/commands/patch/index.js +1 -1
  73. package/dist/src/commands/patch/lint-ignore.js +21 -6
  74. package/dist/src/commands/patch/move-files.js +1 -1
  75. package/dist/src/commands/patch/patch-context.js +0 -1
  76. package/dist/src/commands/patch/rename.js +4 -4
  77. package/dist/src/commands/patch/reorder.js +3 -4
  78. package/dist/src/commands/patch/split-plan.js +2 -1
  79. package/dist/src/commands/patch/split.js +4 -4
  80. package/dist/src/commands/patch/staged-dependency.d.ts +1 -1
  81. package/dist/src/commands/patch/staged-dependency.js +114 -52
  82. package/dist/src/commands/patch/tier.js +18 -5
  83. package/dist/src/commands/re-export-bulk-scan.js +0 -1
  84. package/dist/src/commands/re-export-files.js +5 -1
  85. package/dist/src/commands/re-export-options.js +0 -1
  86. package/dist/src/commands/re-export-scan.js +0 -1
  87. package/dist/src/commands/re-export.js +14 -4
  88. package/dist/src/commands/rebase/abort.js +2 -9
  89. package/dist/src/commands/rebase/confirm.js +0 -1
  90. package/dist/src/commands/rebase/conflict-summary.js +0 -1
  91. package/dist/src/commands/rebase/continue.js +0 -1
  92. package/dist/src/commands/rebase/index.d.ts +1 -1
  93. package/dist/src/commands/rebase/index.js +10 -15
  94. package/dist/src/commands/rebase/patch-loop.js +38 -7
  95. package/dist/src/commands/rebase/summary.js +2 -3
  96. package/dist/src/commands/rebase.js +0 -1
  97. package/dist/src/commands/register.js +1 -1
  98. package/dist/src/commands/reset.js +16 -9
  99. package/dist/src/commands/resolve.js +2 -2
  100. package/dist/src/commands/run.js +4 -4
  101. package/dist/src/commands/setup-support.js +0 -1
  102. package/dist/src/commands/setup.js +1 -2
  103. package/dist/src/commands/source.js +1 -2
  104. package/dist/src/commands/status-output.js +1 -12
  105. package/dist/src/commands/status.js +147 -133
  106. package/dist/src/commands/test-appdir.js +0 -1
  107. package/dist/src/commands/test-diagnose.js +38 -3
  108. package/dist/src/commands/test-modes.d.ts +15 -0
  109. package/dist/src/commands/test-modes.js +61 -0
  110. package/dist/src/commands/test-register.d.ts +4 -0
  111. package/dist/src/commands/test-register.js +64 -0
  112. package/dist/src/commands/test-run.d.ts +23 -7
  113. package/dist/src/commands/test-run.js +15 -14
  114. package/dist/src/commands/test.d.ts +0 -4
  115. package/dist/src/commands/test.js +147 -96
  116. package/dist/src/commands/token-coverage.js +0 -1
  117. package/dist/src/commands/token.js +1 -2
  118. package/dist/src/commands/typecheck.js +1 -1
  119. package/dist/src/commands/verify.js +3 -9
  120. package/dist/src/commands/watch.js +5 -1
  121. package/dist/src/commands/wire.js +1 -1
  122. package/dist/src/core/ast-utils.js +0 -1
  123. package/dist/src/core/brand-validation.js +0 -1
  124. package/dist/src/core/branding.js +0 -1
  125. package/dist/src/core/browser-wire.js +0 -1
  126. package/dist/src/core/build-audit-platform.js +0 -1
  127. package/dist/src/core/build-audit-registration.js +0 -1
  128. package/dist/src/core/build-audit-resolve.js +0 -1
  129. package/dist/src/core/build-audit-transforms.js +0 -1
  130. package/dist/src/core/build-audit.js +0 -1
  131. package/dist/src/core/build-baseline-types.d.ts +24 -0
  132. package/dist/src/core/build-baseline-types.js +0 -1
  133. package/dist/src/core/build-baseline.d.ts +5 -2
  134. package/dist/src/core/build-baseline.js +5 -2
  135. package/dist/src/core/build-prepare.js +2 -2
  136. package/dist/src/core/config-mutate.js +0 -1
  137. package/dist/src/core/config-paths.d.ts +2 -2
  138. package/dist/src/core/config-paths.js +6 -1
  139. package/dist/src/core/config-state.js +0 -1
  140. package/dist/src/core/config-validate-patch-policy.js +0 -1
  141. package/dist/src/core/config-validate-test-toolchains.d.ts +8 -0
  142. package/dist/src/core/config-validate-test-toolchains.js +105 -0
  143. package/dist/src/core/config-validate.js +3 -1
  144. package/dist/src/core/config.js +0 -1
  145. package/dist/src/core/destructive.js +0 -1
  146. package/dist/src/core/diff-hunks.js +13 -7
  147. package/dist/src/core/engine-changes.js +0 -1
  148. package/dist/src/core/engine-session-lock.d.ts +12 -0
  149. package/dist/src/core/engine-session-lock.js +45 -0
  150. package/dist/src/core/file-lock.d.ts +8 -0
  151. package/dist/src/core/file-lock.js +157 -48
  152. package/dist/src/core/firefox-archive.d.ts +4 -0
  153. package/dist/src/core/firefox-archive.js +6 -1
  154. package/dist/src/core/firefox-cache.js +60 -4
  155. package/dist/src/core/firefox-download.js +0 -1
  156. package/dist/src/core/firefox-extract.js +75 -16
  157. package/dist/src/core/firefox-ignorefile.js +0 -1
  158. package/dist/src/core/firefox.d.ts +12 -0
  159. package/dist/src/core/firefox.js +64 -2
  160. package/dist/src/core/furnace-apply-dry-run.js +0 -1
  161. package/dist/src/core/furnace-apply-ftl.d.ts +7 -3
  162. package/dist/src/core/furnace-apply-ftl.js +10 -5
  163. package/dist/src/core/furnace-apply-helpers.js +0 -1
  164. package/dist/src/core/furnace-apply-output.js +2 -2
  165. package/dist/src/core/furnace-apply.d.ts +7 -1
  166. package/dist/src/core/furnace-apply.js +47 -13
  167. package/dist/src/core/furnace-checksum-utils.js +0 -1
  168. package/dist/src/core/furnace-config-array-utils.js +0 -1
  169. package/dist/src/core/furnace-config-custom.js +10 -1
  170. package/dist/src/core/furnace-config-order.js +0 -1
  171. package/dist/src/core/furnace-config-tokens.js +0 -1
  172. package/dist/src/core/furnace-config-validate.js +0 -1
  173. package/dist/src/core/furnace-config.d.ts +11 -0
  174. package/dist/src/core/furnace-config.js +37 -1
  175. package/dist/src/core/furnace-constants.js +0 -1
  176. package/dist/src/core/furnace-css-fragments.js +0 -1
  177. package/dist/src/core/furnace-graph-utils.js +0 -1
  178. package/dist/src/core/furnace-jsconfig.d.ts +2 -3
  179. package/dist/src/core/furnace-jsconfig.js +25 -18
  180. package/dist/src/core/furnace-marker.js +0 -1
  181. package/dist/src/core/furnace-operation.js +0 -1
  182. package/dist/src/core/furnace-refresh.js +0 -1
  183. package/dist/src/core/furnace-registration-ast.d.ts +14 -0
  184. package/dist/src/core/furnace-registration-ast.js +1 -2
  185. package/dist/src/core/furnace-registration-remove.js +0 -1
  186. package/dist/src/core/furnace-registration-validate.js +1 -5
  187. package/dist/src/core/furnace-registration.js +0 -1
  188. package/dist/src/core/furnace-rollback.js +0 -1
  189. package/dist/src/core/furnace-scanner.js +0 -1
  190. package/dist/src/core/furnace-stale-export.d.ts +59 -0
  191. package/dist/src/core/furnace-stale-export.js +123 -0
  192. package/dist/src/core/furnace-staleness.js +0 -1
  193. package/dist/src/core/furnace-state-persist.d.ts +72 -0
  194. package/dist/src/core/furnace-state-persist.js +118 -0
  195. package/dist/src/core/furnace-step-errors.d.ts +23 -0
  196. package/dist/src/core/furnace-step-errors.js +25 -0
  197. package/dist/src/core/furnace-stories.js +0 -1
  198. package/dist/src/core/furnace-validate-accessibility.js +0 -1
  199. package/dist/src/core/furnace-validate-checks.js +0 -1
  200. package/dist/src/core/furnace-validate-compatibility.js +9 -3
  201. package/dist/src/core/furnace-validate-helpers.js +0 -1
  202. package/dist/src/core/furnace-validate-registration.js +7 -7
  203. package/dist/src/core/furnace-validate-structure.d.ts +8 -0
  204. package/dist/src/core/furnace-validate-structure.js +14 -4
  205. package/dist/src/core/furnace-validate.js +0 -1
  206. package/dist/src/core/furnace-version-drift.d.ts +6 -3
  207. package/dist/src/core/furnace-version-drift.js +6 -4
  208. package/dist/src/core/git-base.js +0 -1
  209. package/dist/src/core/git-diff.js +0 -1
  210. package/dist/src/core/git-file-ops.js +0 -1
  211. package/dist/src/core/git-status.d.ts +13 -0
  212. package/dist/src/core/git-status.js +32 -2
  213. package/dist/src/core/git.d.ts +10 -1
  214. package/dist/src/core/git.js +63 -6
  215. package/dist/src/core/license-headers.js +0 -1
  216. package/dist/src/core/lint-cache.js +0 -1
  217. package/dist/src/core/lint-projection.js +0 -1
  218. package/dist/src/core/mach-build-artifacts.js +0 -1
  219. package/dist/src/core/mach-error-hints.js +0 -1
  220. package/dist/src/core/mach-known-noise-filter.d.ts +60 -0
  221. package/dist/src/core/mach-known-noise-filter.js +193 -0
  222. package/dist/src/core/mach-mozconfig.js +0 -1
  223. package/dist/src/core/mach-python.js +0 -1
  224. package/dist/src/core/mach-resource-shim.js +0 -1
  225. package/dist/src/core/mach.d.ts +8 -0
  226. package/dist/src/core/mach.js +36 -4
  227. package/dist/src/core/manifest-helpers.js +0 -1
  228. package/dist/src/core/manifest-register.js +0 -1
  229. package/dist/src/core/manifest-rules.js +0 -1
  230. package/dist/src/core/manifest-tokenizers.js +0 -1
  231. package/dist/src/core/marionette-port.d.ts +8 -0
  232. package/dist/src/core/marionette-port.js +34 -3
  233. package/dist/src/core/marionette-preflight.js +0 -1
  234. package/dist/src/core/markdown-table.js +0 -1
  235. package/dist/src/core/ownership-table.js +2 -9
  236. package/dist/src/core/parser-fallback.js +0 -1
  237. package/dist/src/core/patch-apply-fuzz.d.ts +27 -9
  238. package/dist/src/core/patch-apply-fuzz.js +71 -34
  239. package/dist/src/core/patch-apply.d.ts +30 -0
  240. package/dist/src/core/patch-apply.js +101 -18
  241. package/dist/src/core/patch-artifact-normalize.d.ts +18 -5
  242. package/dist/src/core/patch-artifact-normalize.js +19 -15
  243. package/dist/src/core/patch-export-coverage.js +0 -1
  244. package/dist/src/core/patch-export-metadata.js +0 -1
  245. package/dist/src/core/patch-export-update.js +2 -3
  246. package/dist/src/core/patch-export.d.ts +2 -6
  247. package/dist/src/core/patch-export.js +24 -48
  248. package/dist/src/core/patch-files.js +0 -1
  249. package/dist/src/core/patch-identifier-suggest.js +0 -1
  250. package/dist/src/core/patch-lint-checkjs.js +0 -1
  251. package/dist/src/core/patch-lint-chrome-jsdoc.js +0 -1
  252. package/dist/src/core/patch-lint-cross.js +80 -3
  253. package/dist/src/core/patch-lint-css.js +0 -1
  254. package/dist/src/core/patch-lint-diff-tag.js +0 -1
  255. package/dist/src/core/patch-lint-diff.d.ts +2 -1
  256. package/dist/src/core/patch-lint-diff.js +17 -32
  257. package/dist/src/core/patch-lint-imports.js +0 -1
  258. package/dist/src/core/patch-lint-jsdoc.js +0 -1
  259. package/dist/src/core/patch-lint-observer.js +0 -1
  260. package/dist/src/core/patch-lint-ownership.js +0 -1
  261. package/dist/src/core/patch-lint-reexports.js +0 -1
  262. package/dist/src/core/patch-lint.d.ts +37 -2
  263. package/dist/src/core/patch-lint.js +47 -34
  264. package/dist/src/core/patch-lock.js +0 -1
  265. package/dist/src/core/patch-manifest-consistency.js +9 -1
  266. package/dist/src/core/patch-manifest-io.d.ts +19 -2
  267. package/dist/src/core/patch-manifest-io.js +57 -21
  268. package/dist/src/core/patch-manifest-query.js +18 -10
  269. package/dist/src/core/patch-manifest-resolve.js +0 -1
  270. package/dist/src/core/patch-manifest-validate.js +24 -1
  271. package/dist/src/core/patch-manifest.d.ts +1 -1
  272. package/dist/src/core/patch-manifest.js +1 -2
  273. package/dist/src/core/patch-parse.d.ts +59 -0
  274. package/dist/src/core/patch-parse.js +209 -92
  275. package/dist/src/core/patch-policy.js +0 -1
  276. package/dist/src/core/patch-registration-refs.js +16 -26
  277. package/dist/src/core/patch-source-metadata.js +0 -1
  278. package/dist/src/core/patch-transform.d.ts +4 -0
  279. package/dist/src/core/patch-transform.js +30 -38
  280. package/dist/src/core/rebase-session.js +0 -1
  281. package/dist/src/core/register-browser-content.js +0 -1
  282. package/dist/src/core/register-module.js +0 -1
  283. package/dist/src/core/register-result.js +0 -1
  284. package/dist/src/core/register-scaffold.js +0 -1
  285. package/dist/src/core/register-shared-css.js +0 -1
  286. package/dist/src/core/register-test-manifest.js +0 -1
  287. package/dist/src/core/register-xpcshell-test.js +0 -1
  288. package/dist/src/core/shared-ftl.js +0 -1
  289. package/dist/src/core/signal-critical.js +0 -1
  290. package/dist/src/core/smoke-patterns.js +0 -1
  291. package/dist/src/core/state-file.js +0 -1
  292. package/dist/src/core/status-classify.d.ts +16 -0
  293. package/dist/src/core/status-classify.js +24 -16
  294. package/dist/src/core/test-harness-crash.d.ts +80 -7
  295. package/dist/src/core/test-harness-crash.js +251 -15
  296. package/dist/src/core/test-harness-output.js +0 -1
  297. package/dist/src/core/test-path-scope.d.ts +21 -13
  298. package/dist/src/core/test-path-scope.js +43 -28
  299. package/dist/src/core/test-stale-check.d.ts +29 -1
  300. package/dist/src/core/test-stale-check.js +59 -1
  301. package/dist/src/core/test-stale-symlink.js +0 -1
  302. package/dist/src/core/test-xpcshell-retry.js +0 -1
  303. package/dist/src/core/token-coverage.js +0 -1
  304. package/dist/src/core/token-dark-mode.js +0 -1
  305. package/dist/src/core/token-docs.js +0 -1
  306. package/dist/src/core/token-manager.js +0 -1
  307. package/dist/src/core/token-scaffold.js +0 -1
  308. package/dist/src/core/token-variant.js +0 -1
  309. package/dist/src/core/toolchain-preflight.d.ts +31 -10
  310. package/dist/src/core/toolchain-preflight.js +97 -36
  311. package/dist/src/core/typecheck-shim.js +13 -1
  312. package/dist/src/core/typecheck.js +0 -1
  313. package/dist/src/core/wire-destroy.js +0 -1
  314. package/dist/src/core/wire-dom-fragment.js +0 -1
  315. package/dist/src/core/wire-init.js +0 -1
  316. package/dist/src/core/wire-subscript.js +0 -1
  317. package/dist/src/core/wire-targets.js +0 -1
  318. package/dist/src/core/wire-utils.js +0 -1
  319. package/dist/src/core/xpcshell-appdir.js +1 -4
  320. package/dist/src/errors/base.d.ts +29 -0
  321. package/dist/src/errors/base.js +35 -1
  322. package/dist/src/errors/build.js +0 -1
  323. package/dist/src/errors/codes.d.ts +6 -0
  324. package/dist/src/errors/codes.js +6 -1
  325. package/dist/src/errors/config.js +0 -1
  326. package/dist/src/errors/download.js +0 -1
  327. package/dist/src/errors/furnace.d.ts +11 -1
  328. package/dist/src/errors/furnace.js +6 -5
  329. package/dist/src/errors/git.js +0 -1
  330. package/dist/src/errors/patch.d.ts +17 -0
  331. package/dist/src/errors/patch.js +29 -1
  332. package/dist/src/errors/rebase.js +0 -1
  333. package/dist/src/errors/run.js +1 -1
  334. package/dist/src/index.js +0 -1
  335. package/dist/src/types/cli.js +0 -1
  336. package/dist/src/types/commands/index.d.ts +1 -1
  337. package/dist/src/types/commands/index.js +0 -1
  338. package/dist/src/types/commands/options.d.ts +40 -9
  339. package/dist/src/types/commands/options.js +0 -1
  340. package/dist/src/types/commands/patches.d.ts +37 -0
  341. package/dist/src/types/commands/patches.js +0 -1
  342. package/dist/src/types/commands/project.d.ts +11 -2
  343. package/dist/src/types/commands/project.js +0 -1
  344. package/dist/src/types/config.d.ts +32 -0
  345. package/dist/src/types/config.js +0 -1
  346. package/dist/src/types/furnace.d.ts +23 -0
  347. package/dist/src/types/furnace.js +0 -1
  348. package/dist/src/types/index.js +0 -1
  349. package/dist/src/types/typecheck.js +0 -1
  350. package/dist/src/utils/elapsed.js +0 -1
  351. package/dist/src/utils/errors.js +0 -1
  352. package/dist/src/utils/fs.js +37 -5
  353. package/dist/src/utils/logger.d.ts +7 -0
  354. package/dist/src/utils/logger.js +85 -3
  355. package/dist/src/utils/options.d.ts +15 -0
  356. package/dist/src/utils/options.js +25 -1
  357. package/dist/src/utils/package-root.js +11 -4
  358. package/dist/src/utils/parse.js +0 -1
  359. package/dist/src/utils/paths.js +0 -1
  360. package/dist/src/utils/platform.js +0 -1
  361. package/dist/src/utils/process-group.d.ts +33 -0
  362. package/dist/src/utils/process-group.js +161 -0
  363. package/dist/src/utils/process.d.ts +48 -4
  364. package/dist/src/utils/process.js +258 -59
  365. package/dist/src/utils/regex.d.ts +13 -3
  366. package/dist/src/utils/regex.js +53 -4
  367. package/dist/src/utils/validation.d.ts +0 -12
  368. package/dist/src/utils/validation.js +13 -26
  369. package/package.json +9 -8
@@ -7,6 +7,7 @@
7
7
  * budget; consumed only by the split command.
8
8
  */
9
9
  import { join } from 'node:path';
10
+ import {} from '../../core/destructive.js';
10
11
  import { getDiffForFilesAgainstHead } from '../../core/git-diff.js';
11
12
  import { computeProjectedLintRegressions } from '../../core/lint-projection.js';
12
13
  import { extractAffectedFiles } from '../../core/patch-apply.js';
@@ -18,6 +19,7 @@ import { extractNewFileContentFromDiff } from '../../core/patch-transform.js';
18
19
  import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
19
20
  import { pathExists } from '../../utils/fs.js';
20
21
  import { warn } from '../../utils/logger.js';
22
+ import {} from '../export-flow.js';
21
23
  /**
22
24
  *
23
25
  */
@@ -249,4 +251,3 @@ export function buildSplitSummary(plan) {
249
251
  }
250
252
  return summary;
251
253
  }
252
- //# sourceMappingURL=split-plan.js.map
@@ -18,6 +18,7 @@
18
18
  * the worktree.
19
19
  */
20
20
  import { join } from 'node:path';
21
+ import { Command } from 'commander';
21
22
  import { getProjectPaths, loadConfig } from '../../core/config.js';
22
23
  import { appendHistory, confirmDestructive } from '../../core/destructive.js';
23
24
  import { normalizePatchArtifact } from '../../core/patch-artifact-normalize.js';
@@ -29,7 +30,7 @@ import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
29
30
  import { toError } from '../../utils/errors.js';
30
31
  import { readText, removeFile, writeText } from '../../utils/fs.js';
31
32
  import { info, intro, outro, success, warn } from '../../utils/logger.js';
32
- import { pickDefined } from '../../utils/options.js';
33
+ import { commanderArgParser, pickDefined } from '../../utils/options.js';
33
34
  import { placementPlansEqual, resolvePlacementPlan } from '../export-flow.js';
34
35
  import { runPatchLint } from '../export-shared.js';
35
36
  import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary, findOwnerRewriteHolders, mergeStagedForwardImports, projectSplitManifest, rewriteSplitOwners, runProjectedSplitLint, } from './split-plan.js';
@@ -258,13 +259,13 @@ export function registerPatchSplit(parent, context) {
258
259
  .requiredOption('--name <name>', 'Name for the new patch')
259
260
  .option('--category <category>', "Category for the new patch (default: the source patch's)")
260
261
  .option('--description <desc>', 'Description for the new patch')
261
- .option('--order <n>', 'Exact sparse order for the new patch', (raw) => {
262
+ .option('--order <n>', 'Exact sparse order for the new patch', commanderArgParser((raw) => {
262
263
  const n = Number.parseInt(raw, 10);
263
264
  if (!Number.isInteger(n) || n <= 0) {
264
265
  throw new InvalidArgumentError(`--order must be a positive integer, got "${raw}".`, '--order');
265
266
  }
266
267
  return n;
267
- })
268
+ }))
268
269
  .option('--before <patch>', 'Place the new patch before this patch')
269
270
  .option('--after <patch>', 'Place the new patch after this patch (default: the source patch)')
270
271
  .option('--dry-run', 'Show what would happen without writing')
@@ -289,4 +290,3 @@ export function registerPatchSplit(parent, context) {
289
290
  });
290
291
  }));
291
292
  }
292
- //# sourceMappingURL=split.js.map
@@ -10,7 +10,7 @@ import type { PatchStagedDependencyOptions } from '../../types/commands/index.js
10
10
  *
11
11
  * @param projectRoot - Project root directory
12
12
  * @param identifier - Patch filename, ordinal, or manifest name
13
- * @param options - Mutation mode and forward-import fields
13
+ * @param options - Mutation mode, entry kind, and declaration fields
14
14
  */
15
15
  export declare function patchStagedDependencyCommand(projectRoot: string, identifier: string, options?: PatchStagedDependencyOptions): Promise<void>;
16
16
  /**
@@ -3,6 +3,7 @@
3
3
  * `fireforge patch staged-dependency <name>` — edits
4
4
  * PatchMetadata.stagedDependencies without rewriting the .patch body.
5
5
  */
6
+ import { Command } from 'commander';
6
7
  import { appendHistory } from '../../core/destructive.js';
7
8
  import { mutatePatchMetadata } from '../../core/patch-export.js';
8
9
  import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
@@ -22,6 +23,19 @@ function modeFromOptions(options) {
22
23
  }
23
24
  return adding ? 'add' : removing ? 'remove' : 'clear';
24
25
  }
26
+ function kindFromOptions(options) {
27
+ const kind = options.kind ?? 'import';
28
+ if (kind !== 'import' && kind !== 'registration') {
29
+ throw new InvalidArgumentError(`--kind must be "import" or "registration" (got "${kind}").`, 'patch staged-dependency');
30
+ }
31
+ if (kind === 'import' && options.line !== undefined) {
32
+ throw new InvalidArgumentError('--line only applies to --kind registration; import declarations use --specifier.', 'patch staged-dependency');
33
+ }
34
+ if (kind === 'registration' && options.specifier !== undefined) {
35
+ throw new InvalidArgumentError('--specifier only applies to --kind import; registration declarations use --line.', 'patch staged-dependency');
36
+ }
37
+ return kind;
38
+ }
25
39
  function requireForwardImportOptions(options, mode) {
26
40
  if (!options.file || !options.specifier || !options.creates) {
27
41
  throw new InvalidArgumentError(`--${mode} requires --file, --specifier, and --creates.`, 'patch staged-dependency');
@@ -37,102 +51,149 @@ function requireForwardImportOptions(options, mode) {
37
51
  dependency.reason = options.reason;
38
52
  return dependency;
39
53
  }
40
- function dependencyKey(dependency) {
41
- return [dependency.file, dependency.specifier, dependency.creates, dependency.owner ?? ''].join('\0');
54
+ function requireRegistrationOptions(options, mode) {
55
+ if (!options.file || !options.line || !options.creates) {
56
+ throw new InvalidArgumentError(`--${mode} --kind registration requires --file, --line, and --creates.`, 'patch staged-dependency');
57
+ }
58
+ const dependency = {
59
+ file: options.file,
60
+ line: options.line,
61
+ creates: options.creates,
62
+ };
63
+ if (options.owner !== undefined)
64
+ dependency.owner = options.owner;
65
+ if (options.reason !== undefined)
66
+ dependency.reason = options.reason;
67
+ return dependency;
68
+ }
69
+ function importView(dependency) {
70
+ return { ...dependency, matcher: dependency.specifier };
42
71
  }
43
- function dependencyLabel(dependency) {
44
- const owner = dependency.owner ? ` owner=${dependency.owner}` : '';
45
- const reason = dependency.reason ? ` reason="${dependency.reason}"` : '';
46
- return `${dependency.file} imports "${dependency.specifier}" from ${dependency.creates}${owner}${reason}`;
72
+ function registrationView(dependency) {
73
+ return { ...dependency, matcher: dependency.line };
47
74
  }
48
- function removeMatching(existing, target) {
49
- return existing.filter((dependency) => dependency.file !== target.file ||
50
- dependency.specifier !== target.specifier ||
51
- dependency.creates !== target.creates ||
52
- (target.owner !== undefined && dependency.owner !== target.owner));
75
+ function dependencyKey(view) {
76
+ return [view.file, view.matcher, view.creates, view.owner ?? ''].join('\0');
53
77
  }
54
- function applyMode(existing, mode, dependency) {
78
+ function dependencyLabel(kind, view) {
79
+ const owner = view.owner ? ` owner=${view.owner}` : '';
80
+ const reason = view.reason ? ` reason="${view.reason}"` : '';
81
+ if (kind === 'import') {
82
+ return `${view.file} imports "${view.matcher}" from ${view.creates}${owner}${reason}`;
83
+ }
84
+ return `${view.file} registers "${view.matcher}" for ${view.creates}${owner}${reason}`;
85
+ }
86
+ function matchesTarget(view, target) {
87
+ return (view.file === target.file &&
88
+ view.matcher === target.matcher &&
89
+ view.creates === target.creates &&
90
+ (target.owner === undefined || view.owner === target.owner));
91
+ }
92
+ function applyMode(existing, mode, target, toView, addition) {
55
93
  if (mode === 'clear')
56
94
  return [];
57
- if (!dependency)
95
+ if (!target)
58
96
  return [...existing];
59
97
  if (mode === 'remove')
60
- return removeMatching(existing, dependency);
61
- const seen = new Set(existing.map(dependencyKey));
62
- if (seen.has(dependencyKey(dependency)))
98
+ return existing.filter((entry) => !matchesTarget(toView(entry), target));
99
+ const seen = new Set(existing.map((entry) => dependencyKey(toView(entry))));
100
+ if (seen.has(dependencyKey(target)) || addition === undefined)
63
101
  return [...existing];
64
- return [...existing, dependency];
102
+ return [...existing, addition];
65
103
  }
66
104
  /**
67
105
  * Renders a one-line summary of a staged-dependency metadata change.
68
106
  */
69
- function describeStagedDependencyChange(before, after, mode, dependency) {
107
+ function describeStagedDependencyChange(beforeCount, afterCount, mode, kind, target) {
108
+ const noun = kind === 'import' ? 'staged forward-import' : 'staged registration';
70
109
  if (mode === 'clear') {
71
- return before.length === 0
110
+ return beforeCount === 0
72
111
  ? 'stagedDependencies was already empty — no change'
73
- : `cleared ${before.length} staged forward-import declaration(s)`;
112
+ : `cleared ${beforeCount} staged dependency declaration(s)`;
74
113
  }
75
- if (!dependency)
114
+ if (!target)
76
115
  return 'stagedDependencies unchanged';
77
116
  if (mode === 'add') {
78
- return after.length === before.length
79
- ? `staged forward-import already present: ${dependencyLabel(dependency)}`
80
- : `added staged forward-import: ${dependencyLabel(dependency)}`;
117
+ return afterCount === beforeCount
118
+ ? `${noun} already present: ${dependencyLabel(kind, target)}`
119
+ : `added ${noun}: ${dependencyLabel(kind, target)}`;
81
120
  }
82
- return after.length === before.length
83
- ? `no staged forward-import matched: ${dependencyLabel(dependency)}`
84
- : `removed ${before.length - after.length} staged forward-import declaration(s): ${dependencyLabel(dependency)}`;
121
+ return afterCount === beforeCount
122
+ ? `no ${noun} matched: ${dependencyLabel(kind, target)}`
123
+ : `removed ${beforeCount - afterCount} ${noun} declaration(s): ${dependencyLabel(kind, target)}`;
124
+ }
125
+ function countStagedEntries(staged) {
126
+ return (staged?.forwardImports?.length ?? 0) + (staged?.registrations?.length ?? 0);
85
127
  }
86
128
  /**
87
129
  * Runs the metadata-only staged-dependency mutation command.
88
130
  *
89
131
  * @param projectRoot - Project root directory
90
132
  * @param identifier - Patch filename, ordinal, or manifest name
91
- * @param options - Mutation mode and forward-import fields
133
+ * @param options - Mutation mode, entry kind, and declaration fields
92
134
  */
93
135
  export async function patchStagedDependencyCommand(projectRoot, identifier, options = {}) {
94
136
  const isDryRun = options.dryRun === true;
95
137
  intro(isDryRun ? 'FireForge patch staged-dependency (dry run)' : 'FireForge patch staged-dependency');
96
138
  const mode = modeFromOptions(options);
97
- const dependency = mode === 'clear' ? undefined : requireForwardImportOptions(options, mode);
139
+ const kind = kindFromOptions(options);
140
+ const importDependency = mode === 'clear' || kind !== 'import' ? undefined : requireForwardImportOptions(options, mode);
141
+ const registrationDependency = mode === 'clear' || kind !== 'registration'
142
+ ? undefined
143
+ : requireRegistrationOptions(options, mode);
144
+ const target = importDependency !== undefined
145
+ ? importView(importDependency)
146
+ : registrationDependency !== undefined
147
+ ? registrationView(registrationDependency)
148
+ : undefined;
98
149
  const { paths, manifest } = await requirePatchQueue(projectRoot);
99
- const target = requirePatchTarget(identifier, manifest.patches);
100
- const existing = target.stagedDependencies?.forwardImports ?? [];
101
- const projected = applyMode(existing, mode, dependency);
102
- const summary = describeStagedDependencyChange(existing, projected, mode, dependency);
150
+ const targetPatch = requirePatchTarget(identifier, manifest.patches);
151
+ const applyToStaged = (staged) => {
152
+ if (mode === 'clear')
153
+ return {};
154
+ const forwardImports = applyMode(staged?.forwardImports ?? [], kind === 'import' ? mode : 'add', kind === 'import' ? target : undefined, importView, importDependency);
155
+ const registrations = applyMode(staged?.registrations ?? [], kind === 'registration' ? mode : 'add', kind === 'registration' ? target : undefined, registrationView, registrationDependency);
156
+ return {
157
+ ...(forwardImports.length > 0 ? { forwardImports } : {}),
158
+ ...(registrations.length > 0 ? { registrations } : {}),
159
+ };
160
+ };
161
+ const existing = targetPatch.stagedDependencies;
162
+ const projected = applyToStaged(existing);
163
+ const summary = describeStagedDependencyChange(countStagedEntries(existing), countStagedEntries(projected), mode, kind, target);
103
164
  if (isDryRun) {
104
- info(`[dry-run] ${target.filename}: ${summary}.`);
165
+ info(`[dry-run] ${targetPatch.filename}: ${summary}.`);
105
166
  outro('Dry run complete — no changes made');
106
167
  return;
107
168
  }
108
- const result = await mutatePatchMetadata(paths.patches, target.filename, (current) => {
109
- const before = current.stagedDependencies?.forwardImports ?? [];
110
- const after = applyMode(before, mode, dependency);
111
- if (after.length === 0)
169
+ const result = await mutatePatchMetadata(paths.patches, targetPatch.filename, (current) => {
170
+ const after = applyToStaged(current.stagedDependencies);
171
+ if (countStagedEntries(after) === 0)
112
172
  return { unset: ['stagedDependencies'] };
113
- return { set: { stagedDependencies: { forwardImports: after } } };
173
+ return { set: { stagedDependencies: after } };
114
174
  });
115
175
  if (!result) {
116
- throw new GeneralError(`Patch ${target.filename} disappeared from the manifest during the update. Re-run after investigating.`);
176
+ throw new GeneralError(`Patch ${targetPatch.filename} disappeared from the manifest during the update. Re-run after investigating.`);
117
177
  }
118
- const before = result.before.stagedDependencies?.forwardImports ?? [];
119
- const after = result.after.stagedDependencies?.forwardImports ?? [];
120
- info(`${target.filename}: ${describeStagedDependencyChange(before, after, mode, dependency)}.`);
178
+ const before = result.before.stagedDependencies;
179
+ const after = result.after.stagedDependencies;
180
+ info(`${targetPatch.filename}: ${describeStagedDependencyChange(countStagedEntries(before), countStagedEntries(after), mode, kind, target)}.`);
121
181
  try {
122
182
  await appendHistory(paths.patches, {
123
183
  operation: 'patch-staged-dependency',
124
184
  args: {
125
- filename: target.filename,
185
+ filename: targetPatch.filename,
126
186
  mode,
127
- before,
128
- after,
187
+ kind,
188
+ before: before ?? {},
189
+ after: after ?? {},
129
190
  },
130
191
  ...(options.yes === true ? { yes: true } : {}),
131
192
  result: 'ok',
132
193
  });
133
194
  }
134
195
  catch (historyError) {
135
- warn(`History log append failed after patch staged-dependency committed (${target.filename}): ${toError(historyError).message}`);
196
+ warn(`History log append failed after patch staged-dependency committed (${targetPatch.filename}): ${toError(historyError).message}`);
136
197
  }
137
198
  outro('Patch staged-dependency complete');
138
199
  }
@@ -147,11 +208,13 @@ export function registerPatchStagedDependency(parent, context) {
147
208
  parent
148
209
  .command('staged-dependency <name>')
149
210
  .description('Edit PatchMetadata.stagedDependencies on a single patch (no .patch body rewrite).')
150
- .option('--add', 'Add a staged forward-import declaration')
151
- .option('--remove', 'Remove matching staged forward-import declaration(s)')
211
+ .option('--add', 'Add a staged dependency declaration')
212
+ .option('--remove', 'Remove matching staged dependency declaration(s)')
152
213
  .option('--clear', 'Drop the stagedDependencies field entirely')
153
- .option('--file <path>', 'Importing file path relative to engine/')
154
- .option('--specifier <specifier>', 'Exact import specifier as it appears in source')
214
+ .option('--kind <kind>', 'Declaration shape: "import" (forward import, the default) or "registration" (jar.mn packaging line, customElements or actor registration)')
215
+ .option('--file <path>', 'Declaring file path relative to engine/')
216
+ .option('--specifier <specifier>', 'Exact import specifier as it appears in source (--kind import)')
217
+ .option('--line <text>', 'Registration/packaging line as the patch adds it, compared whitespace-trimmed (--kind registration)')
155
218
  .option('--creates <path>', 'Later-created file path relative to engine/')
156
219
  .option('--owner <patch>', 'Exact later patch filename expected to create --creates')
157
220
  .option('--reason <text>', 'Human-readable rationale stored with the declaration')
@@ -161,4 +224,3 @@ export function registerPatchStagedDependency(parent, context) {
161
224
  await patchStagedDependencyCommand(getProjectRoot(), name, options);
162
225
  }));
163
226
  }
164
- //# sourceMappingURL=staged-dependency.js.map
@@ -14,8 +14,8 @@
14
14
  * Modes are mutually exclusive: exactly one of `--tier <branding>` or
15
15
  * `--clear` must be supplied per invocation.
16
16
  */
17
- import { Option } from 'commander';
18
- import { appendHistory } from '../../core/destructive.js';
17
+ import { Command, Option } from 'commander';
18
+ import { appendHistory, confirmDestructive } from '../../core/destructive.js';
19
19
  import { updatePatchMetadata } from '../../core/patch-export.js';
20
20
  import { InvalidArgumentError } from '../../errors/base.js';
21
21
  import { toError } from '../../utils/errors.js';
@@ -60,11 +60,25 @@ export async function patchTierCommand(projectRoot, identifier, options = {}) {
60
60
  : before === undefined
61
61
  ? `set tier to "${after}"`
62
62
  : `change tier from "${before}" to "${after}"`;
63
- if (isDryRun) {
64
- info(`[dry-run] ${target.filename}: would ${action}.`);
63
+ // Destructive-operation contract (docs/lifecycle-invariants.md): manifest
64
+ // metadata mutations get summary + dry-run + confirmation/--yes + history
65
+ // uniformly. This command used to accept --yes without ever prompting, so
66
+ // the flag only appeared in the history record.
67
+ const decision = await confirmDestructive({
68
+ operation: 'patch-tier',
69
+ title: `Patch tier: ${target.filename}`,
70
+ summary: [`${target.filename}: ${action}`],
71
+ yes: options.yes === true,
72
+ dryRun: isDryRun,
73
+ });
74
+ if (decision === 'dry-run') {
65
75
  outro('Dry run complete — no changes made');
66
76
  return;
67
77
  }
78
+ if (decision === 'cancelled') {
79
+ outro('Cancelled — no changes made');
80
+ return;
81
+ }
68
82
  // Single write under the patch directory lock (delegated inside
69
83
  // updatePatchMetadata). Setting routes through `updates`; clearing
70
84
  // routes through `unsetFields` so TypeScript's exact optional types
@@ -116,4 +130,3 @@ export function registerPatchTier(parent, context) {
116
130
  });
117
131
  }));
118
132
  }
119
- //# sourceMappingURL=tier.js.map
@@ -78,4 +78,3 @@ export async function withDryRunReExportLock(fireforgeDir, isDryRun, operation)
78
78
  onStaleLockMessage: (ageMs) => `Removing stale FireForge re-export dry-run lock (age: ${Math.round(ageMs / 1000)}s). A previous dry-run process may have crashed.`,
79
79
  });
80
80
  }
81
- //# sourceMappingURL=re-export-bulk-scan.js.map
@@ -1,6 +1,8 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { join } from 'node:path';
3
+ import { getProjectPaths } from '../core/config.js';
3
4
  import { appendHistory, confirmDestructive } from '../core/destructive.js';
5
+ import { enforceFreshFurnaceSources } from '../core/furnace-stale-export.js';
4
6
  import { getDiffForFilesAgainstHead } from '../core/git-diff.js';
5
7
  import { computeProjectedLintRegressions } from '../core/lint-projection.js';
6
8
  import { extractAffectedFiles } from '../core/patch-apply.js';
@@ -150,6 +152,9 @@ export async function reExportFilesInPlace(paths, selectedPatches, options, conf
150
152
  throw new InvalidArgumentError('reExportFilesInPlace called with no --files.', '--files');
151
153
  }
152
154
  const requested = [...new Set(filesOption)].sort();
155
+ // Stale-furnace-source gate (0.37.0 item 4): same refusal as the generic
156
+ // re-export path — the projected diff would capture stale deployed copies.
157
+ await enforceFreshFurnaceSources(paths.root, requested, options.allowStaleFurnace === true, 're-export');
153
158
  const removed = target.filesAffected.filter((f) => !requested.includes(f));
154
159
  const added = requested.filter((f) => !target.filesAffected.includes(f));
155
160
  const retained = target.filesAffected.filter((f) => requested.includes(f));
@@ -260,4 +265,3 @@ export async function reExportFilesInPlace(paths, selectedPatches, options, conf
260
265
  success(`Re-exported ${target.filename}`);
261
266
  outro('Re-export complete');
262
267
  }
263
- //# sourceMappingURL=re-export-files.js.map
@@ -83,4 +83,3 @@ export function validateReExportOptionCombinations(patches, options) {
83
83
  throw new InvalidArgumentError('--tier and --lint-ignore require explicit patch identifiers and cannot be combined with --all (different patches typically need different metadata).', '--all');
84
84
  }
85
85
  }
86
- //# sourceMappingURL=re-export-options.js.map
@@ -144,4 +144,3 @@ export function assertScanFileAdditionsHaveDiffHunks(args) {
144
144
  return;
145
145
  throw new InvalidArgumentError(`Refusing to re-export ${patchFilename} with --scan-file because ${noDiffScanFiles.length} explicit added path${noDiffScanFiles.length === 1 ? '' : 's'} produced no diff hunks (${noDiffScanFiles.join(', ')}). Remove unchanged paths or modify them before retrying.`, '--scan-file');
146
146
  }
147
- //# sourceMappingURL=re-export-scan.js.map
@@ -1,8 +1,11 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
+ /* eslint-disable max-lines -- Re-export is at the guardrail; splitting register plumbing is unrelated to this fix. */
2
3
  import { dirname, join } from 'node:path';
3
4
  import { multiselect } from '@clack/prompts';
4
- import { Option } from 'commander';
5
+ import { Command, Option } from 'commander';
5
6
  import { getProjectPaths, loadConfig } from '../core/config.js';
7
+ import { withEngineSessionLock } from '../core/engine-session-lock.js';
8
+ import { enforceFreshFurnaceSources } from '../core/furnace-stale-export.js';
6
9
  import { isGitRepository } from '../core/git.js';
7
10
  import { getDiffForFilesAgainstHead } from '../core/git-diff.js';
8
11
  import { getModifiedFilesInDir, getUntrackedFilesInDir } from '../core/git-status.js';
@@ -127,6 +130,12 @@ async function reExportSinglePatch(patch, paths, manifest, options, isDryRun, co
127
130
  for (const f of removed)
128
131
  info(` - ${f}`);
129
132
  }
133
+ // Stale-furnace-source gate (0.37.0 item 4): re-export captures deployed
134
+ // engine copies, so a component source edited after the last furnace
135
+ // apply would land in the patch as its OLD deployed content. Refuse (or
136
+ // warn under --allow-stale-furnace) before diffing. Runs in dry-run too
137
+ // so the failure surfaces early.
138
+ await enforceFreshFurnaceSources(paths.root, currentFilesAffected, options.allowStaleFurnace === true, 're-export');
130
139
  const missingFiles = await findMissingFiles(paths.engine, currentFilesAffected);
131
140
  if (missingFiles.length === currentFilesAffected.length) {
132
141
  warn(`Skipped ${patch.filename}: all affected files missing`);
@@ -413,6 +422,7 @@ export function registerReExport(program, { getProjectRoot, withErrorHandling })
413
422
  .filter((v) => v.length > 0))
414
423
  .option('--dry-run', 'Show what would change without writing')
415
424
  .option('--skip-lint', 'Skip patch lint checks (downgrade errors to warnings)')
425
+ .option('--allow-stale-furnace', 'Export the deployed engine copy even when the components/ source changed since the last furnace apply')
416
426
  .option('--allow-shrink', 'Allow --files to remove paths currently owned by the patch. Required before --yes can bypass the shrink confirmation.')
417
427
  .option('-y, --yes', 'Skip confirmation prompts (required for non-TTY destructive writes)')
418
428
  .option('--force-unsafe', 'Bypass cross-patch lint refusal when --files shrinks a patch')
@@ -421,13 +431,13 @@ export function registerReExport(program, { getProjectRoot, withErrorHandling })
421
431
  .option('--lint-ignore <check-id>', 'Append a lint check ID to the patch\'s PatchMetadata.lintIgnore (union, de-duped, repeatable). Mutually exclusive with --all. Use "fireforge patch lint-ignore" for --remove / --clear.', (value, prev) => [...prev, value], [])
422
432
  .action(withErrorHandling(async (patches, options) => {
423
433
  const { tier, lintIgnore, scanFile, scanFiles, ...rest } = options;
424
- await reExportCommand(getProjectRoot(), patches, {
434
+ const projectRoot = getProjectRoot();
435
+ await withEngineSessionLock(projectRoot, 're-export', () => reExportCommand(projectRoot, patches, {
425
436
  ...pickDefined(rest),
426
437
  ...(scanFile !== undefined && scanFile.length > 0 ? { scanFiles: scanFile } : {}),
427
438
  ...(scanFiles !== undefined ? { scanFilesManifest: scanFiles } : {}),
428
439
  ...(tier !== undefined ? { tier: tier } : {}),
429
440
  ...(lintIgnore !== undefined && lintIgnore.length > 0 ? { lintIgnore } : {}),
430
- });
441
+ }));
431
442
  }));
432
443
  }
433
- //# sourceMappingURL=re-export.js.map
@@ -3,11 +3,10 @@
3
3
  * Rebase abort flow.
4
4
  */
5
5
  import { getProjectPaths, updateState } from '../../core/config.js';
6
- import { getFurnacePaths, updateFurnaceState } from '../../core/furnace-config.js';
6
+ import { clearAppliedFurnaceState } from '../../core/furnace-config.js';
7
7
  import { resetChanges } from '../../core/git.js';
8
8
  import { clearRebaseSession, loadRebaseSession } from '../../core/rebase-session.js';
9
9
  import { NoRebaseSessionError } from '../../errors/rebase.js';
10
- import { pathExists } from '../../utils/fs.js';
11
10
  import { intro, outro, spinner, success } from '../../utils/logger.js';
12
11
  import { confirmDirtyEngineReset } from './confirm.js';
13
12
  /**
@@ -47,12 +46,7 @@ export async function handleAbort(projectRoot, yes) {
47
46
  // misleading now that reset already succeeded). The rebase session is
48
47
  // still kept on disk so the user can retry the abort and let it
49
48
  // idempotently re-clear furnace state.
50
- const furnacePaths = getFurnacePaths(projectRoot);
51
- if (await pathExists(furnacePaths.furnaceState)) {
52
- await updateFurnaceState(projectRoot, (current) => ({
53
- ...(current.pendingRepair ? { pendingRepair: current.pendingRepair } : {}),
54
- }));
55
- }
49
+ await clearAppliedFurnaceState(projectRoot);
56
50
  // Step 3: clear pending resolution transactionally.
57
51
  await updateState(projectRoot, (current) => {
58
52
  if (!current.pendingResolution)
@@ -67,4 +61,3 @@ export async function handleAbort(projectRoot, yes) {
67
61
  success('Rebase aborted and session cleared.');
68
62
  outro('Rebase aborted');
69
63
  }
70
- //# sourceMappingURL=abort.js.map
@@ -30,4 +30,3 @@ export async function confirmDirtyEngineReset({ engineDir, yes, nonInteractiveCo
30
30
  }
31
31
  return true;
32
32
  }
33
- //# sourceMappingURL=confirm.js.map
@@ -35,4 +35,3 @@ export function buildRebaseConflictSummary(args) {
35
35
  ],
36
36
  };
37
37
  }
38
- //# sourceMappingURL=conflict-summary.js.map
@@ -104,4 +104,3 @@ export async function handleContinue(projectRoot, maxFuzz) {
104
104
  // Continue applying remaining patches
105
105
  await runPatchLoop(projectRoot, session, paths, maxFuzz);
106
106
  }
107
- //# sourceMappingURL=continue.js.map
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Orchestrates the full patch-rebase workflow:
5
5
  * 1. Reset engine to baseline
6
- * 2. Apply each patch with escalating fuzz
6
+ * 2. Apply each patch with escalating context reduction (git apply -C<n>)
7
7
  * 3. Pause on failures for manual resolution
8
8
  * 4. Re-export successfully applied patches with the new version stamp
9
9
  *
@@ -4,14 +4,15 @@
4
4
  *
5
5
  * Orchestrates the full patch-rebase workflow:
6
6
  * 1. Reset engine to baseline
7
- * 2. Apply each patch with escalating fuzz
7
+ * 2. Apply each patch with escalating context reduction (git apply -C<n>)
8
8
  * 3. Pause on failures for manual resolution
9
9
  * 4. Re-export successfully applied patches with the new version stamp
10
10
  *
11
11
  * Supports `--continue` (resume after manual fix) and `--abort` (cancel).
12
12
  */
13
+ import { Command } from 'commander';
13
14
  import { getProjectPaths, loadConfig } from '../../core/config.js';
14
- import { getFurnacePaths, updateFurnaceState } from '../../core/furnace-config.js';
15
+ import { clearAppliedFurnaceState } from '../../core/furnace-config.js';
15
16
  import { getHead, isGitRepository, isMissingHeadError, resetChanges } from '../../core/git.js';
16
17
  import { discoverPatches } from '../../core/patch-files.js';
17
18
  import { loadPatchesManifest } from '../../core/patch-manifest.js';
@@ -21,7 +22,8 @@ import { GeneralError } from '../../errors/base.js';
21
22
  import { RebaseSessionExistsError } from '../../errors/rebase.js';
22
23
  import { pathExists } from '../../utils/fs.js';
23
24
  import { info, intro, outro, spinner } from '../../utils/logger.js';
24
- import { pickDefined } from '../../utils/options.js';
25
+ import { commanderArgParser, pickDefined } from '../../utils/options.js';
26
+ import { parsePositiveIntegerFlag } from '../../utils/validation.js';
25
27
  import { handleAbort } from './abort.js';
26
28
  import { confirmDirtyEngineReset } from './confirm.js';
27
29
  import { handleContinue } from './continue.js';
@@ -78,9 +80,9 @@ async function handleFreshStart(projectRoot, options) {
78
80
  }
79
81
  info(`Rebasing patches: ${fromVersion} → ${currentVersion}`);
80
82
  info(`Found ${manifest.patches.length} patch(es)`);
81
- info(`Max fuzz factor: ${maxFuzz}`);
83
+ info(`Max context-reduction steps (fuzz-like): ${maxFuzz}`);
82
84
  if (isDryRun) {
83
- info('[dry-run] Would reset engine, apply patches with fuzz, and re-export.');
85
+ info('[dry-run] Would reset engine, apply patches with context reduction, and re-export.');
84
86
  outro('Dry run complete');
85
87
  return;
86
88
  }
@@ -102,13 +104,7 @@ async function handleFreshStart(projectRoot, options) {
102
104
  await resetChanges(paths.engine);
103
105
  resetSpinner.stop('Engine reset to baseline');
104
106
  // Clear Furnace state — the engine no longer contains deployed components.
105
- // Preserve pendingRepair since that tracks authoring-side rollback issues.
106
- const furnacePaths = getFurnacePaths(projectRoot);
107
- if (await pathExists(furnacePaths.furnaceState)) {
108
- await updateFurnaceState(projectRoot, (current) => ({
109
- ...(current.pendingRepair ? { pendingRepair: current.pendingRepair } : {}),
110
- }));
111
- }
107
+ await clearAppliedFurnaceState(projectRoot);
112
108
  // Create rebase session
113
109
  const allPatches = await discoverPatches(paths.patches);
114
110
  const session = {
@@ -147,14 +143,13 @@ export async function rebaseCommand(projectRoot, options = {}) {
147
143
  export function registerRebase(program, { getProjectRoot, withErrorHandling }) {
148
144
  program
149
145
  .command('rebase')
150
- .description('Semi-automated Firefox source version upgrade — apply patches with fuzz and re-export')
146
+ .description('Semi-automated Firefox source version upgrade — apply patches with drift tolerance and re-export')
151
147
  .option('--continue', 'Resume after manually resolving a failed patch')
152
148
  .option('--abort', 'Cancel the rebase and restore engine to pre-rebase state')
153
149
  .option('--dry-run', 'Show what would happen without modifying anything')
154
- .option('--max-fuzz <n>', 'Maximum fuzz factor for git apply (default: 3)', parseInt)
150
+ .option('--max-fuzz <n>', 'Maximum context-reduction steps for git apply -C<n> (fuzz-like drift tolerance; default: 3)', commanderArgParser((v) => parsePositiveIntegerFlag('--max-fuzz', v)))
155
151
  .option('-y, --yes', 'Skip dirty-tree confirmation prompt')
156
152
  .action(withErrorHandling(async (options) => {
157
153
  await rebaseCommand(getProjectRoot(), pickDefined(options));
158
154
  }));
159
155
  }
160
- //# sourceMappingURL=index.js.map