@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
@@ -12,6 +12,7 @@ import { rename } from 'node:fs/promises';
12
12
  import { join } from 'node:path';
13
13
  import { FireForgeError } from '../errors/base.js';
14
14
  import { ExitCode } from '../errors/codes.js';
15
+ import { PatchManifestCorruptError } from '../errors/patch.js';
15
16
  import { toError } from '../utils/errors.js';
16
17
  import { pathExistsStrict, readJson, removeFile, writeJson } from '../utils/fs.js';
17
18
  import { warn } from '../utils/logger.js';
@@ -46,6 +47,13 @@ export async function loadPatchesManifestState(patchesDir) {
46
47
  }
47
48
  /**
48
49
  * Loads the patches manifest if it exists.
50
+ *
51
+ * READ-ONLY callers only: this collapses "absent" and "corrupt" into null.
52
+ * Any code path that will WRITE the manifest (or delete patch files based
53
+ * on its content) must use {@link loadPatchesManifestForWrite} instead —
54
+ * treating a corrupt manifest as empty and then saving destroys every
55
+ * existing patch's metadata.
56
+ *
49
57
  * @param patchesDir - Path to the patches directory
50
58
  * @returns PatchesManifest or null if not found
51
59
  */
@@ -53,6 +61,21 @@ export async function loadPatchesManifest(patchesDir) {
53
61
  const state = await loadPatchesManifestState(patchesDir);
54
62
  return state.manifest;
55
63
  }
64
+ /**
65
+ * Loads the patches manifest for a mutating operation.
66
+ *
67
+ * Returns null only when the manifest genuinely does not exist; a manifest
68
+ * that exists but fails to parse/validate throws
69
+ * {@link PatchManifestCorruptError} so the caller aborts instead of
70
+ * rebuilding an empty queue over the top of the corrupt file.
71
+ */
72
+ export async function loadPatchesManifestForWrite(patchesDir) {
73
+ const state = await loadPatchesManifestState(patchesDir);
74
+ if (state.exists && state.manifest === null) {
75
+ throw new PatchManifestCorruptError(join(patchesDir, PATCHES_MANIFEST), state.parseError);
76
+ }
77
+ return state.manifest;
78
+ }
56
79
  /**
57
80
  * Saves the patches manifest.
58
81
  * @param patchesDir - Path to the patches directory
@@ -125,7 +148,8 @@ export async function mutatePatchRowsInManifest(patchesDir, filenames, mutator)
125
148
  * @param removeFilenames - Optional filenames to remove in the same read-modify-write cycle
126
149
  */
127
150
  export async function addPatchToManifest(patchesDir, metadata, removeFilenames) {
128
- const manifest = (await loadPatchesManifest(patchesDir)) ?? {
151
+ // ForWrite: a corrupt manifest must abort here, not be rebuilt as empty.
152
+ const manifest = (await loadPatchesManifestForWrite(patchesDir)) ?? {
129
153
  version: 1,
130
154
  patches: [],
131
155
  };
@@ -156,7 +180,7 @@ export async function addPatchToManifest(patchesDir, metadata, removeFilenames)
156
180
  * false when no matching entry existed
157
181
  */
158
182
  export async function removePatchFromManifest(patchesDir, filename) {
159
- const manifest = await loadPatchesManifest(patchesDir);
183
+ const manifest = await loadPatchesManifestForWrite(patchesDir);
160
184
  if (!manifest)
161
185
  return false;
162
186
  const originalLength = manifest.patches.length;
@@ -168,8 +192,9 @@ export async function removePatchFromManifest(patchesDir, filename) {
168
192
  return true;
169
193
  }
170
194
  /**
171
- * Rewrites `stagedDependencies.forwardImports[].owner` references on one
172
- * patch through a rename lookup. Owners embed exact patch filenames, so any
195
+ * Rewrites `stagedDependencies.forwardImports[].owner` and
196
+ * `stagedDependencies.registrations[].owner` references on one patch
197
+ * through a rename lookup. Owners embed exact patch filenames, so any
173
198
  * renumber (compact, reorder, placement export, rename) that does not remap
174
199
  * them leaves dangling references that surface as false forward-import
175
200
  * errors on the next lint.
@@ -183,25 +208,35 @@ export async function removePatchFromManifest(patchesDir, filename) {
183
208
  * @returns The same row, or a copy with remapped owners
184
209
  */
185
210
  export function rewriteStagedDependencyOwners(patch, renameLookup) {
186
- const forwardImports = patch.stagedDependencies?.forwardImports;
187
- if (!forwardImports || forwardImports.length === 0)
211
+ const staged = patch.stagedDependencies;
212
+ if (!staged)
188
213
  return patch;
189
- const rewritten = forwardImports.map((fi) => {
190
- if (!fi.owner)
191
- return fi;
192
- const newOwner = renameLookup(fi.owner);
193
- if (newOwner === undefined || newOwner === fi.owner)
194
- return fi;
195
- return { ...fi, owner: newOwner };
196
- });
197
- const changed = rewritten.some((fi, index) => fi !== forwardImports[index]);
198
- if (!changed)
214
+ const rewriteOwners = (entries) => {
215
+ if (!entries || entries.length === 0)
216
+ return { entries, changed: false };
217
+ const rewritten = entries.map((entry) => {
218
+ if (!entry.owner)
219
+ return entry;
220
+ const newOwner = renameLookup(entry.owner);
221
+ if (newOwner === undefined || newOwner === entry.owner)
222
+ return entry;
223
+ return { ...entry, owner: newOwner };
224
+ });
225
+ return {
226
+ entries: rewritten,
227
+ changed: rewritten.some((entry, index) => entry !== entries[index]),
228
+ };
229
+ };
230
+ const forwardImports = rewriteOwners(staged.forwardImports);
231
+ const registrations = rewriteOwners(staged.registrations);
232
+ if (!forwardImports.changed && !registrations.changed)
199
233
  return patch;
200
234
  return {
201
235
  ...patch,
202
236
  stagedDependencies: {
203
- ...patch.stagedDependencies,
204
- forwardImports: rewritten,
237
+ ...staged,
238
+ ...(forwardImports.entries !== undefined ? { forwardImports: forwardImports.entries } : {}),
239
+ ...(registrations.entries !== undefined ? { registrations: registrations.entries } : {}),
205
240
  },
206
241
  };
207
242
  }
@@ -240,7 +275,7 @@ export function rewriteStagedDependencyOwners(patch, renameLookup) {
240
275
  export async function renumberPatchesInManifest(patchesDir, renameMap) {
241
276
  if (renameMap.size === 0)
242
277
  return;
243
- const manifest = await loadPatchesManifest(patchesDir);
278
+ const manifest = await loadPatchesManifestForWrite(patchesDir);
244
279
  if (!manifest) {
245
280
  throw new Error('Cannot renumber patches: patches.json is missing.');
246
281
  }
@@ -425,7 +460,9 @@ export class PatchDeleteRollbackError extends FireForgeError {
425
460
  */
426
461
  export async function removePatchFileAndManifest(patchesDir, filename) {
427
462
  const patchPath = join(patchesDir, filename);
428
- const originalManifest = await loadPatchesManifest(patchesDir);
463
+ // ForWrite: deleting the patch FILE while a corrupt manifest still
464
+ // references it would strand the queue; abort on corruption instead.
465
+ const originalManifest = await loadPatchesManifestForWrite(patchesDir);
429
466
  const removedFromManifest = await removePatchFromManifest(patchesDir, filename);
430
467
  try {
431
468
  if (await pathExistsStrict(patchPath)) {
@@ -451,4 +488,3 @@ export async function removePatchFileAndManifest(patchesDir, filename) {
451
488
  throw deleteError;
452
489
  }
453
490
  }
454
- //# sourceMappingURL=patch-manifest-io.js.map
@@ -3,7 +3,7 @@
3
3
  * Query helpers: finding patches by file, integrity checks, version compat, stamping.
4
4
  */
5
5
  import { readText } from '../utils/fs.js';
6
- import { fileExistsInHead } from './git-file-ops.js';
6
+ import { listTrackedInHead } from './git-file-ops.js';
7
7
  import { discoverPatches, getAllTargetFilesFromPatch } from './patch-files.js';
8
8
  import { withPatchDirectoryLock } from './patch-lock.js';
9
9
  import { loadPatchesManifest, mutatePatchRowsInManifest } from './patch-manifest-io.js';
@@ -79,6 +79,12 @@ export async function findPatchesAffectingFile(patchesDir, filePath) {
79
79
  export async function validatePatchIntegrity(patchesDir, engineDir) {
80
80
  const issues = [];
81
81
  const patches = await discoverPatches(patchesDir);
82
+ // Collect every modification target first, then resolve HEAD existence in
83
+ // ONE batched ls-tree pass (chunked for ARG_MAX). The per-file
84
+ // fileExistsInHead fan-out spawned one git process per (patch × target) on
85
+ // every import — a 56-file branding patch alone cost ~56 spawns before a
86
+ // single patch was applied.
87
+ const modificationTargets = [];
82
88
  for (const patch of patches) {
83
89
  // Check all files in the patch (supports multi-file patches)
84
90
  const patchContent = await readText(patch.path);
@@ -87,14 +93,17 @@ export async function validatePatchIntegrity(patchesDir, engineDir) {
87
93
  // Skip new-file sections — they don't need to exist in HEAD
88
94
  if (isNewFileInPatch(patchContent, targetFile))
89
95
  continue;
90
- const existsInHead = await fileExistsInHead(engineDir, targetFile);
91
- if (!existsInHead) {
92
- issues.push({
93
- filename: patch.filename,
94
- message: `Modification patch for file that doesn't exist in source. Re-export with: fireforge export ${targetFile}`,
95
- targetFile,
96
- });
97
- }
96
+ modificationTargets.push({ filename: patch.filename, targetFile });
97
+ }
98
+ }
99
+ const tracked = await listTrackedInHead(engineDir, modificationTargets.map((t) => t.targetFile));
100
+ for (const { filename, targetFile } of modificationTargets) {
101
+ if (!tracked.has(targetFile)) {
102
+ issues.push({
103
+ filename,
104
+ message: `Modification patch for file that doesn't exist in source. Re-export with: fireforge export ${targetFile}`,
105
+ targetFile,
106
+ });
98
107
  }
99
108
  }
100
109
  return issues;
@@ -128,4 +137,3 @@ export async function stampPatchVersions(patchesDir, filenames, newVersion, newP
128
137
  return null;
129
138
  }));
130
139
  }
131
- //# sourceMappingURL=patch-manifest-query.js.map
@@ -36,4 +36,3 @@ export function resolvePatchIdentifier(identifier, patches) {
36
36
  // manifest at export time. See function docstring.
37
37
  return patches.find((p) => p.name === identifier) ?? null;
38
38
  }
39
- //# sourceMappingURL=patch-manifest-resolve.js.map
@@ -24,13 +24,37 @@ function parseForwardImports(data, label) {
24
24
  return dependency;
25
25
  });
26
26
  }
27
+ function parseRegistrations(data, label) {
28
+ if (!isArray(data)) {
29
+ throw new Error(`${label} must be an array`);
30
+ }
31
+ return data.map((entry, index) => {
32
+ const rec = parseObject(entry, `${label}[${index}]`);
33
+ const dependency = {
34
+ file: rec.string('file'),
35
+ line: rec.string('line'),
36
+ creates: rec.string('creates'),
37
+ };
38
+ const owner = rec.optionalString('owner');
39
+ if (owner !== undefined)
40
+ dependency.owner = owner;
41
+ const reason = rec.optionalString('reason');
42
+ if (reason !== undefined)
43
+ dependency.reason = reason;
44
+ return dependency;
45
+ });
46
+ }
27
47
  function parseStagedDependencies(data, label) {
28
48
  const rec = parseObject(data, label);
29
49
  const rawForwardImports = rec.raw('forwardImports');
50
+ const rawRegistrations = rec.raw('registrations');
30
51
  const staged = {};
31
52
  if (rawForwardImports !== undefined) {
32
53
  staged.forwardImports = parseForwardImports(rawForwardImports, `${label}.forwardImports`);
33
54
  }
55
+ if (rawRegistrations !== undefined) {
56
+ staged.registrations = parseRegistrations(rawRegistrations, `${label}.registrations`);
57
+ }
34
58
  return staged;
35
59
  }
36
60
  /**
@@ -144,4 +168,3 @@ export function inferPatchMetadataFromFilename(filename) {
144
168
  }
145
169
  return { category: 'infra', name: filename.replace(/\.patch$/, '') };
146
170
  }
147
- //# sourceMappingURL=patch-manifest-validate.js.map
@@ -5,7 +5,7 @@
5
5
  * is an implementation detail.
6
6
  */
7
7
  export { rebuildPatchesManifest, validatePatchesManifestConsistency, } from './patch-manifest-consistency.js';
8
- export { addPatchToManifest, loadPatchesManifest, mutatePatchRowsInManifest, PATCHES_MANIFEST, type PatchManifestRowMutation, type PatchManifestRowMutationResult, type PatchRenameEntry, removePatchFileAndManifest, renumberPatchesInManifest, rewriteStagedDependencyOwners, savePatchesManifest, } from './patch-manifest-io.js';
8
+ export { addPatchToManifest, loadPatchesManifest, loadPatchesManifestForWrite, mutatePatchRowsInManifest, PATCHES_MANIFEST, type PatchManifestRowMutation, type PatchManifestRowMutationResult, type PatchRenameEntry, removePatchFileAndManifest, renumberPatchesInManifest, rewriteStagedDependencyOwners, savePatchesManifest, } from './patch-manifest-io.js';
9
9
  export { checkVersionCompatibility, findPatchesAffectingFile, getClaimedFiles, stampPatchVersions, validatePatchIntegrity, } from './patch-manifest-query.js';
10
10
  export { resolvePatchIdentifier } from './patch-manifest-resolve.js';
11
11
  export { validatePatchesManifest } from './patch-manifest-validate.js';
@@ -6,8 +6,7 @@
6
6
  * is an implementation detail.
7
7
  */
8
8
  export { rebuildPatchesManifest, validatePatchesManifestConsistency, } from './patch-manifest-consistency.js';
9
- export { addPatchToManifest, loadPatchesManifest, mutatePatchRowsInManifest, PATCHES_MANIFEST, removePatchFileAndManifest, renumberPatchesInManifest, rewriteStagedDependencyOwners, savePatchesManifest, } from './patch-manifest-io.js';
9
+ export { addPatchToManifest, loadPatchesManifest, loadPatchesManifestForWrite, mutatePatchRowsInManifest, PATCHES_MANIFEST, removePatchFileAndManifest, renumberPatchesInManifest, rewriteStagedDependencyOwners, savePatchesManifest, } from './patch-manifest-io.js';
10
10
  export { checkVersionCompatibility, findPatchesAffectingFile, getClaimedFiles, stampPatchVersions, validatePatchIntegrity, } from './patch-manifest-query.js';
11
11
  export { resolvePatchIdentifier } from './patch-manifest-resolve.js';
12
12
  export { validatePatchesManifest } from './patch-manifest-validate.js';
13
- //# sourceMappingURL=patch-manifest.js.map
@@ -1,6 +1,15 @@
1
1
  /**
2
2
  * Pure parsing functions for extracting information from patch files.
3
3
  * All functions are synchronous and operate on string content.
4
+ *
5
+ * This module is the ONE unified-diff walker in the codebase. Before
6
+ * {@link parseDiffSections} existed, six hand-rolled `diff --git` walkers
7
+ * lived across patch-parse, patch-lint-diff, patch-registration-refs, and
8
+ * patch-transform — with divergent CRLF handling (a CRLF-saved patch file
9
+ * silently failed target-file matching and `\ No newline` detection on
10
+ * Windows), no quoted-path support, and greedy path captures that
11
+ * mis-split any path containing ` b/`. New diff-shaped parsing must build
12
+ * on {@link parseDiffSections} rather than re-walking lines.
4
13
  */
5
14
  /**
6
15
  * Extracts the order number from a patch filename.
@@ -9,6 +18,56 @@
9
18
  * @returns Order number, or Infinity if no prefix
10
19
  */
11
20
  export declare function extractOrder(filename: string): number;
21
+ /**
22
+ * A parsed `diff --git` header: both sides' paths, unquoted and with any
23
+ * CRLF residue stripped.
24
+ */
25
+ export interface DiffGitHeader {
26
+ /** Old-side (`a/`) path. */
27
+ sourcePath: string;
28
+ /** New-side (`b/`) path — the file the patch produces. */
29
+ targetPath: string;
30
+ }
31
+ /**
32
+ * Parses a `diff --git` line into its two paths.
33
+ *
34
+ * Handles the quoted form git emits for paths with special characters
35
+ * (`diff --git "a/x y" "b/x y"`), and disambiguates the unquoted form for
36
+ * paths that themselves contain ` b/`: for the overwhelmingly common
37
+ * non-rename case the two paths are identical, so a symmetric split
38
+ * (`a/<p> b/<p>`) is tried first — the previous greedy regex
39
+ * (`a\/.+ b\/(.+)$`) split such lines at the LAST ` b/` and returned a
40
+ * truncated path. Falls back to a non-greedy split for renames.
41
+ *
42
+ * @returns null when the line is not a diff --git header.
43
+ */
44
+ export declare function parseDiffGitHeader(line: string): DiffGitHeader | null;
45
+ /**
46
+ * One `diff --git` section of a unified diff, fully structured.
47
+ */
48
+ export interface DiffSection {
49
+ /** New-side (`b/`) path — the file this section produces. */
50
+ targetPath: string;
51
+ /** Old-side (`a/`) path. */
52
+ sourcePath: string;
53
+ /** True when the section carries a `new file mode` marker. */
54
+ isNewFile: boolean;
55
+ /** True when the section carries a `deleted file mode` marker. */
56
+ isDeletedFile: boolean;
57
+ /**
58
+ * True for binary sections (`GIT binary patch` or `Binary files … differ`).
59
+ * Binary sections have no parseable hunks; text-oriented consumers must
60
+ * refuse them rather than treat base85 lines starting with `+` as content.
61
+ */
62
+ isBinary: boolean;
63
+ /** Parsed hunks, in file order. Empty for binary or metadata-only sections. */
64
+ hunks: ParsedHunk[];
65
+ }
66
+ /**
67
+ * Parses a unified diff into structured per-file sections — the single
68
+ * shared walker every diff consumer builds on.
69
+ */
70
+ export declare function parseDiffSections(diffContent: string): DiffSection[];
12
71
  /**
13
72
  * Checks whether a specific file is a new-file addition within a patch.
14
73
  * For multi-file patches, only inspects the section belonging to targetFile.