@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
@@ -2,6 +2,15 @@
2
2
  /**
3
3
  * Pure parsing functions for extracting information from patch files.
4
4
  * All functions are synchronous and operate on string content.
5
+ *
6
+ * This module is the ONE unified-diff walker in the codebase. Before
7
+ * {@link parseDiffSections} existed, six hand-rolled `diff --git` walkers
8
+ * lived across patch-parse, patch-lint-diff, patch-registration-refs, and
9
+ * patch-transform — with divergent CRLF handling (a CRLF-saved patch file
10
+ * silently failed target-file matching and `\ No newline` detection on
11
+ * Windows), no quoted-path support, and greedy path captures that
12
+ * mis-split any path containing ` b/`. New diff-shaped parsing must build
13
+ * on {@link parseDiffSections} rather than re-walking lines.
5
14
  */
6
15
  /**
7
16
  * Extracts the order number from a patch filename.
@@ -17,87 +26,155 @@ export function extractOrder(filename) {
17
26
  return Infinity;
18
27
  }
19
28
  /**
20
- * Checks whether a specific file is a new-file addition within a patch.
21
- * For multi-file patches, only inspects the section belonging to targetFile.
22
- * @param patchContent - The full patch content
23
- * @param targetFile - The file path to check
24
- * @returns true if the patch creates targetFile as a new file
29
+ * Un-escapes a path from git's C-style quoted form (`"a/pfad m\303\244..."`).
30
+ * Handles the escapes git emits: `\\`, `\"`, `\t`, `\n`, `\r` and three-digit
31
+ * octal byte escapes. Octal bytes are decoded through a byte buffer so
32
+ * multibyte UTF-8 sequences (git's default `core.quotePath=true` encodes any
33
+ * non-ASCII path this way) come back as the real characters.
25
34
  */
26
- export function isNewFileInPatch(patchContent, targetFile) {
27
- const lines = patchContent.split('\n');
28
- let inTargetFile = false;
29
- for (const line of lines) {
30
- if (line.startsWith('diff --git')) {
31
- const match = /^diff --git a\/.+ b\/(.+)$/.exec(line);
32
- inTargetFile = match?.[1] === targetFile;
35
+ function unquoteGitPath(quoted) {
36
+ const bytes = [];
37
+ for (let i = 0; i < quoted.length; i++) {
38
+ const ch = quoted[i];
39
+ if (ch !== '\\') {
40
+ // Plain character may itself be multi-byte when the diff already
41
+ // contains raw UTF-8; push its bytes.
42
+ const encoded = Buffer.from(quoted[i] ?? '', 'utf-8');
43
+ for (const b of encoded)
44
+ bytes.push(b);
33
45
  continue;
34
46
  }
35
- if (!inTargetFile)
36
- continue;
37
- // Found hunk header — stop scanning metadata for this file section
38
- if (line.startsWith('@@'))
47
+ const next = quoted[i + 1];
48
+ if (next === undefined)
39
49
  break;
40
- if (line.startsWith('new file mode ')) {
41
- return true;
50
+ if (next >= '0' && next <= '7') {
51
+ const octal = quoted.slice(i + 1, i + 4);
52
+ bytes.push(parseInt(octal, 8));
53
+ i += 3;
54
+ continue;
42
55
  }
56
+ const mapped = next === 'n' ? 0x0a : next === 't' ? 0x09 : next === 'r' ? 0x0d : next.charCodeAt(0);
57
+ bytes.push(mapped);
58
+ i += 1;
43
59
  }
44
- return false;
60
+ return Buffer.from(bytes).toString('utf-8');
45
61
  }
46
62
  /**
47
- * Extracts affected file paths from a diff/patch content.
48
- * @param diffContent - The diff content to parse
49
- * @returns Array of file paths
63
+ * Parses a `diff --git` line into its two paths.
64
+ *
65
+ * Handles the quoted form git emits for paths with special characters
66
+ * (`diff --git "a/x y" "b/x y"`), and disambiguates the unquoted form for
67
+ * paths that themselves contain ` b/`: for the overwhelmingly common
68
+ * non-rename case the two paths are identical, so a symmetric split
69
+ * (`a/<p> b/<p>`) is tried first — the previous greedy regex
70
+ * (`a\/.+ b\/(.+)$`) split such lines at the LAST ` b/` and returned a
71
+ * truncated path. Falls back to a non-greedy split for renames.
72
+ *
73
+ * @returns null when the line is not a diff --git header.
50
74
  */
51
- export function extractAffectedFiles(diffContent) {
52
- const files = new Set();
53
- const lines = diffContent.split('\n');
54
- for (const line of lines) {
55
- // Match "diff --git a/path/to/file b/path/to/file"
56
- const diffMatch = /^diff --git a\/.+ b\/(.+)$/.exec(line);
57
- if (diffMatch?.[1]) {
58
- files.add(diffMatch[1]);
59
- continue;
60
- }
61
- // Match "+++ b/path/to/file" for new files
62
- const addMatch = /^\+\+\+ b\/(.+)$/.exec(line);
63
- if (addMatch?.[1] && addMatch[1] !== '/dev/null') {
64
- files.add(addMatch[1]);
75
+ export function parseDiffGitHeader(line) {
76
+ const stripped = line.endsWith('\r') ? line.slice(0, -1) : line;
77
+ if (!stripped.startsWith('diff --git '))
78
+ return null;
79
+ const body = stripped.slice('diff --git '.length);
80
+ // Quoted form (either or both sides may be quoted).
81
+ const quoted = /^(?:"a\/((?:[^"\\]|\\.)*)"|a\/(\S+)) (?:"b\/((?:[^"\\]|\\.)*)"|b\/(.+))$/.exec(body);
82
+ if (quoted && (quoted[1] !== undefined || quoted[3] !== undefined)) {
83
+ const sourcePath = quoted[1] !== undefined ? unquoteGitPath(quoted[1]) : (quoted[2] ?? '');
84
+ const targetPath = quoted[3] !== undefined ? unquoteGitPath(quoted[3]) : (quoted[4] ?? '');
85
+ return { sourcePath, targetPath };
86
+ }
87
+ if (!body.startsWith('a/'))
88
+ return null;
89
+ const rest = body.slice(2);
90
+ // Symmetric split: body === `a/<p> b/<p>`. Correct even when <p>
91
+ // contains ` b/`, which defeats any single-regex approach.
92
+ if ((rest.length - 3) % 2 === 0) {
93
+ const pathLength = (rest.length - 3) / 2;
94
+ const candidate = rest.slice(0, pathLength);
95
+ if (rest === `${candidate} b/${candidate}`) {
96
+ return { sourcePath: candidate, targetPath: candidate };
65
97
  }
66
98
  }
67
- return Array.from(files).sort();
99
+ // Rename/copy (differing paths): non-greedy split at the first ` b/`.
100
+ const asymmetric = /^(.+?) b\/(.+)$/.exec(rest);
101
+ if (asymmetric?.[1] !== undefined && asymmetric[2] !== undefined) {
102
+ return { sourcePath: asymmetric[1], targetPath: asymmetric[2] };
103
+ }
104
+ return null;
68
105
  }
69
106
  /**
70
- * Parses hunks from a patch file for a specific target file.
71
- * @param patchContent - The full patch content
72
- * @param targetFile - The file path to extract hunks for
73
- * @returns Array of hunk objects with line info and changes
107
+ * Splits raw diff content into lines, tolerating CRLF-saved patch files.
108
+ *
109
+ * The distinction matters: a patch file saved with CRLF endings (Windows
110
+ * editor, `core.autocrlf` checkout) has `\r` on EVERY line including
111
+ * structural ones — strip it, or headers, `@@` lines and the
112
+ * `` marker all fail to match. But an LF patch
113
+ * of a file whose CONTENT is CRLF has `\r` only on payload lines, where it
114
+ * is significant and must be preserved. Detect the former by checking the
115
+ * structural lines themselves for a trailing `\r`.
74
116
  */
75
- export function parseHunksForFile(patchContent, targetFile) {
76
- const hunks = [];
77
- const lines = patchContent.split('\n');
78
- let inTargetFile = false;
117
+ function splitDiffLines(diffContent) {
118
+ const lines = diffContent.split('\n');
119
+ const isCrlfPatchFile = lines.some((line) => (line.startsWith('diff --git') || line.startsWith('@@ ')) &&
120
+ line.endsWith('\r') &&
121
+ line.length > 1);
122
+ if (!isCrlfPatchFile)
123
+ return lines;
124
+ return lines.map((line) => (line.endsWith('\r') ? line.slice(0, -1) : line));
125
+ }
126
+ /**
127
+ * Parses a unified diff into structured per-file sections — the single
128
+ * shared walker every diff consumer builds on.
129
+ */
130
+ export function parseDiffSections(diffContent) {
131
+ const sections = [];
132
+ const lines = splitDiffLines(diffContent);
133
+ let current = null;
79
134
  let currentHunk = null;
80
- for (let i = 0; i < lines.length; i++) {
81
- const line = lines[i] ?? '';
82
- // Check for diff header
83
- if (line.startsWith('diff --git')) {
84
- // Check if this is for our target file
85
- const match = /^diff --git a\/.+ b\/(.+)$/.exec(line);
86
- if (currentHunk) {
87
- hunks.push(currentHunk);
88
- }
89
- inTargetFile = match?.[1] === targetFile;
90
- currentHunk = null;
135
+ const finishHunk = () => {
136
+ if (current && currentHunk) {
137
+ current.hunks.push(currentHunk);
138
+ }
139
+ currentHunk = null;
140
+ };
141
+ for (const line of lines) {
142
+ const header = parseDiffGitHeader(line);
143
+ if (header) {
144
+ finishHunk();
145
+ current = {
146
+ targetPath: header.targetPath,
147
+ sourcePath: header.sourcePath,
148
+ isNewFile: false,
149
+ isDeletedFile: false,
150
+ isBinary: false,
151
+ hunks: [],
152
+ };
153
+ sections.push(current);
91
154
  continue;
92
155
  }
93
- if (!inTargetFile)
156
+ if (!current)
157
+ continue;
158
+ if (currentHunk === null) {
159
+ // File metadata zone (between the header and the first hunk).
160
+ if (line.startsWith('new file mode')) {
161
+ current.isNewFile = true;
162
+ continue;
163
+ }
164
+ if (line.startsWith('deleted file mode')) {
165
+ current.isDeletedFile = true;
166
+ continue;
167
+ }
168
+ if (line === 'GIT binary patch' || /^Binary files .* differ$/.test(line)) {
169
+ current.isBinary = true;
170
+ continue;
171
+ }
172
+ }
173
+ if (current.isBinary)
94
174
  continue;
95
- // Check for hunk header
96
175
  const hunkMatch = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(line);
97
176
  if (hunkMatch) {
98
- if (currentHunk) {
99
- hunks.push(currentHunk);
100
- }
177
+ finishHunk();
101
178
  currentHunk = {
102
179
  oldStart: parseInt(hunkMatch[1] ?? '0', 10),
103
180
  oldCount: parseInt(hunkMatch[2] ?? '1', 10),
@@ -109,41 +186,82 @@ export function parseHunksForFile(patchContent, targetFile) {
109
186
  };
110
187
  continue;
111
188
  }
112
- // Collect hunk lines
113
- if (currentHunk) {
114
- if (line === '\') {
115
- // The marker is an annotation on the immediately preceding body
116
- // line. Peek the last collected line to decide which side(s) the
117
- // annotation applies to — a single boolean cannot represent the
118
- // asymmetric case where only one side lacks the trailing newline.
119
- const previous = currentHunk.lines[currentHunk.lines.length - 1] ?? '';
120
- if (previous.startsWith('-')) {
121
- currentHunk.noNewlineAtEndOld = true;
122
- }
123
- else if (previous.startsWith('+')) {
124
- currentHunk.noNewlineAtEndNew = true;
125
- }
126
- else if (previous.startsWith(' ')) {
127
- // Context line: present in both sides, so the trailing-newline
128
- // absence applies to both. This is rare (it only happens when
129
- // the hunk ends on an unchanged line that itself is the last
130
- // line of the file) but real — git emits it.
131
- currentHunk.noNewlineAtEndOld = true;
132
- currentHunk.noNewlineAtEndNew = true;
133
- }
134
- // If the marker appears with no preceding body line (malformed
135
- // diff), leave both flags false — the downstream apply logic
136
- // will still produce a defined result.
189
+ if (!currentHunk)
190
+ continue;
191
+ if (line === '\') {
192
+ // The marker is an annotation on the immediately preceding body
193
+ // line. Peek the last collected line to decide which side(s) the
194
+ // annotation applies to — a single boolean cannot represent the
195
+ // asymmetric case where only one side lacks the trailing newline.
196
+ const previous = currentHunk.lines[currentHunk.lines.length - 1] ?? '';
197
+ if (previous.startsWith('-')) {
198
+ currentHunk.noNewlineAtEndOld = true;
199
+ }
200
+ else if (previous.startsWith('+')) {
201
+ currentHunk.noNewlineAtEndNew = true;
137
202
  }
138
- else if (line.startsWith('+') || line.startsWith('-') || line.startsWith(' ')) {
139
- currentHunk.lines.push(line);
203
+ else if (previous.startsWith(' ')) {
204
+ // Context line: present in both sides, so the trailing-newline
205
+ // absence applies to both. This is rare (it only happens when
206
+ // the hunk ends on an unchanged line that itself is the last
207
+ // line of the file) but real — git emits it.
208
+ currentHunk.noNewlineAtEndOld = true;
209
+ currentHunk.noNewlineAtEndNew = true;
140
210
  }
211
+ // If the marker appears with no preceding body line (malformed
212
+ // diff), leave both flags false — the downstream apply logic
213
+ // will still produce a defined result.
214
+ continue;
215
+ }
216
+ if (line.startsWith('+') || line.startsWith('-') || line.startsWith(' ')) {
217
+ currentHunk.lines.push(line);
141
218
  }
142
219
  }
143
- if (currentHunk) {
144
- hunks.push(currentHunk);
220
+ finishHunk();
221
+ return sections;
222
+ }
223
+ /**
224
+ * Checks whether a specific file is a new-file addition within a patch.
225
+ * For multi-file patches, only inspects the section belonging to targetFile.
226
+ * @param patchContent - The full patch content
227
+ * @param targetFile - The file path to check
228
+ * @returns true if the patch creates targetFile as a new file
229
+ */
230
+ export function isNewFileInPatch(patchContent, targetFile) {
231
+ return parseDiffSections(patchContent).some((section) => section.targetPath === targetFile && section.isNewFile);
232
+ }
233
+ /**
234
+ * Extracts affected file paths from a diff/patch content.
235
+ * @param diffContent - The diff content to parse
236
+ * @returns Array of file paths
237
+ */
238
+ export function extractAffectedFiles(diffContent) {
239
+ const files = new Set();
240
+ for (const section of parseDiffSections(diffContent)) {
241
+ files.add(section.targetPath);
242
+ }
243
+ if (files.size === 0) {
244
+ // Header-less unified diff (no `diff --git` lines, e.g. hand-written
245
+ // or `diff -u` output): fall back to the `+++ b/<path>` markers.
246
+ for (const line of splitDiffLines(diffContent)) {
247
+ const addMatch = /^\+\+\+ b\/(.+)$/.exec(line);
248
+ if (addMatch?.[1] && addMatch[1] !== '/dev/null') {
249
+ files.add(addMatch[1]);
250
+ }
251
+ }
145
252
  }
146
- return hunks;
253
+ return Array.from(files).sort();
254
+ }
255
+ /**
256
+ * Parses hunks from a patch file for a specific target file.
257
+ * @param patchContent - The full patch content
258
+ * @param targetFile - The file path to extract hunks for
259
+ * @returns Array of hunk objects with line info and changes
260
+ */
261
+ export function parseHunksForFile(patchContent, targetFile) {
262
+ return parseDiffSections(patchContent)
263
+ .filter((section) => section.targetPath === targetFile)
264
+ .flatMap((section) => section.hunks);
147
265
  }
148
266
  /**
149
267
  * Extracts conflicting file paths from git apply error message.
@@ -162,4 +280,3 @@ export function extractConflictingFiles(error) {
162
280
  }
163
281
  return files;
164
282
  }
165
- //# sourceMappingURL=patch-parse.js.map
@@ -376,4 +376,3 @@ export function allocatePolicyOrder(config, patches, category) {
376
376
  }
377
377
  return null;
378
378
  }
379
- //# sourceMappingURL=patch-policy.js.map
@@ -14,6 +14,7 @@
14
14
  * references (widget tag names, locale fluent names). Unrelated jar.mn
15
15
  * or customElements.js edits pass through without spurious warnings.
16
16
  */
17
+ import { parseDiffSections } from './patch-parse.js';
17
18
  /** Canonical file paths that registration-shaped diffs touch. */
18
19
  const REGISTRATION_FILE_PATHS = new Set([
19
20
  'toolkit/content/customElements.js',
@@ -36,32 +37,22 @@ export function collectPatchRegistrationReferences(patchBody) {
36
37
  if (!patchBody)
37
38
  return [];
38
39
  const refs = [];
39
- let currentFile;
40
- // Walk line-by-line. The canonical unified-diff header line is
41
- // `diff --git a/<path> b/<path>` we key the file state off the `b/`
42
- // path because that names the target side and is stable against
43
- // renames. Additional diff metadata lines (index/---/+++/@@) are
44
- // ignored for the purposes of tracking the current file.
45
- const lines = patchBody.split(/\r?\n/);
46
- for (const line of lines) {
47
- const diffHeader = /^diff --git a\/(.+?) b\/(.+)$/.exec(line);
48
- if (diffHeader?.[2]) {
49
- currentFile = diffHeader[2];
40
+ // Key the file state off the `b/` path because that names the target
41
+ // side and is stable against renames. Only real hunk adds count
42
+ // parseDiffSections has already filtered out header/metadata lines.
43
+ for (const section of parseDiffSections(patchBody)) {
44
+ if (!REGISTRATION_FILE_PATHS.has(section.targetPath))
50
45
  continue;
51
- }
52
- if (!currentFile)
53
- continue;
54
- if (!REGISTRATION_FILE_PATHS.has(currentFile))
55
- continue;
56
- if (!line.startsWith('+'))
57
- continue;
58
- // Skip the `+++ b/<path>` header line — only real hunk adds count.
59
- if (line.startsWith('+++'))
60
- continue;
61
- const added = line.slice(1);
62
- const extracted = extractTargetPathsFromRegistrationLine(currentFile, added);
63
- for (const target of extracted) {
64
- refs.push({ targetPath: target, source: currentFile, lineText: added });
46
+ for (const hunk of section.hunks) {
47
+ for (const line of hunk.lines) {
48
+ if (!line.startsWith('+'))
49
+ continue;
50
+ const added = line.slice(1);
51
+ const extracted = extractTargetPathsFromRegistrationLine(section.targetPath, added);
52
+ for (const target of extracted) {
53
+ refs.push({ targetPath: target, source: section.targetPath, lineText: added });
54
+ }
55
+ }
65
56
  }
66
57
  }
67
58
  return refs;
@@ -114,4 +105,3 @@ function extractTargetPathsFromRegistrationLine(sourceFile, added) {
114
105
  }
115
106
  return [];
116
107
  }
117
- //# sourceMappingURL=patch-registration-refs.js.map
@@ -14,4 +14,3 @@ export function getPatchSourceVersion(patch) {
14
14
  export function getPatchSourceProduct(patch) {
15
15
  return patch.sourceProduct;
16
16
  }
17
- //# sourceMappingURL=patch-source-metadata.js.map
@@ -12,6 +12,10 @@
12
12
  * @param diff - Raw unified-diff content
13
13
  * @param targetFile - Optional target file to scope extraction to
14
14
  * @returns The file content that the patch would create
15
+ * @throws PatchError for binary sections — a `GIT binary patch` payload is
16
+ * base85 (whose alphabet includes `+`), so "extract the + lines" would
17
+ * silently write garbage text over a binary target. Callers must treat
18
+ * binary new-file conflicts as unresolvable rather than auto-resolving.
15
19
  */
16
20
  export declare function extractNewFileContentFromDiff(diff: string, targetFile?: string): string;
17
21
  /**
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { PatchError } from '../errors/patch.js';
7
7
  import { readText } from '../utils/fs.js';
8
- import { isNewFileInPatch, parseHunksForFile } from './patch-parse.js';
8
+ import { isNewFileInPatch, parseDiffSections, parseHunksForFile } from './patch-parse.js';
9
9
  /**
10
10
  * Extracts the complete file content from a "new file" patch given a raw
11
11
  * diff string already in memory. Callers with a patch file path should
@@ -16,48 +16,37 @@ import { isNewFileInPatch, parseHunksForFile } from './patch-parse.js';
16
16
  * @param diff - Raw unified-diff content
17
17
  * @param targetFile - Optional target file to scope extraction to
18
18
  * @returns The file content that the patch would create
19
+ * @throws PatchError for binary sections — a `GIT binary patch` payload is
20
+ * base85 (whose alphabet includes `+`), so "extract the + lines" would
21
+ * silently write garbage text over a binary target. Callers must treat
22
+ * binary new-file conflicts as unresolvable rather than auto-resolving.
19
23
  */
20
24
  export function extractNewFileContentFromDiff(diff, targetFile) {
21
- const lines = diff.split('\n');
25
+ const sections = parseDiffSections(diff).filter((section) => !targetFile || section.targetPath === targetFile);
26
+ const binary = sections.find((section) => section.isBinary);
27
+ if (binary) {
28
+ throw new PatchError(`Cannot extract text content from binary patch section for ${binary.targetPath}`, binary.targetPath);
29
+ }
30
+ // An empty new file is a legitimate git diff: `new file mode` with no
31
+ // hunks at all. It must extract as '' — the historical line-walker
32
+ // returned '\n' for it, creating a one-byte file that failed checksum
33
+ // and drift comparisons against the truly empty file the patch creates.
34
+ const hunks = sections.flatMap((section) => section.hunks);
35
+ if (hunks.length === 0) {
36
+ return '';
37
+ }
22
38
  const contentLines = [];
23
- let inHunk = false;
24
- let inTargetFile = !targetFile; // If no targetFile, accept all sections
25
- let hasNoNewlineMarker = false;
26
- for (const line of lines) {
27
- // Track which file section we're in
28
- if (line.startsWith('diff --git')) {
29
- if (targetFile) {
30
- const match = /^diff --git a\/.+ b\/(.+)$/.exec(line);
31
- const wasInTarget = inTargetFile;
32
- inTargetFile = match?.[1] === targetFile;
33
- // If we were in the target file and hit a new diff header, we're done
34
- if (wasInTarget && !inTargetFile)
35
- break;
36
- }
37
- inHunk = false;
38
- continue;
39
- }
40
- if (!inTargetFile)
41
- continue;
42
- // Start of hunk
43
- if (line.startsWith('@@')) {
44
- inHunk = true;
45
- continue;
46
- }
47
- if (inHunk) {
48
- // Check for "No newline at end of file" marker
49
- if (line === '\') {
50
- hasNoNewlineMarker = true;
51
- continue;
52
- }
53
- // Lines starting with + are added content (skip the + prefix)
39
+ for (const hunk of hunks) {
40
+ for (const line of hunk.lines) {
41
+ // Lines starting with + are added content (skip the + prefix).
42
+ // `-`/context lines shouldn't exist in new-file patches.
54
43
  if (line.startsWith('+')) {
55
44
  contentLines.push(line.slice(1));
56
45
  }
57
- // Lines starting with - are removed (shouldn't exist in new file patches)
58
- // Context lines (no prefix) shouldn't exist in new file patches
59
46
  }
60
47
  }
48
+ const lastHunk = hunks[hunks.length - 1];
49
+ const hasNoNewlineMarker = lastHunk?.noNewlineAtEndNew ?? false;
61
50
  // Join lines and handle trailing newline
62
51
  const result = contentLines.join('\n');
63
52
  return hasNoNewlineMarker ? result : result + '\n';
@@ -129,8 +118,12 @@ export async function applyPatchToContent(content, patchPath, targetFile) {
129
118
  if (actualOldCount !== hunk.oldCount) {
130
119
  throw new PatchError(`Patch hunk header mismatch for ${targetFile}: header says ${hunk.oldCount} old lines but body has ${actualOldCount}`, targetFile);
131
120
  }
132
- // Replace the old lines with new lines
133
- const startIndex = hunk.oldStart - 1;
121
+ // Replace the old lines with new lines. Unified-diff convention: for a
122
+ // pure-insertion hunk (`@@ -N,0 +M,k @@`), N names the line BEFORE the
123
+ // insertion point, so content is spliced at index N (after line N) —
124
+ // `N - 1` inserted one line too early, and `@@ -0,0` (insert at top of
125
+ // file) produced splice(-1, …), which inserts before the LAST element.
126
+ const startIndex = hunk.oldCount === 0 ? hunk.oldStart : hunk.oldStart - 1;
134
127
  // Verify context lines match before applying
135
128
  let verifyIndex = startIndex;
136
129
  for (const hunkLine of hunk.lines) {
@@ -152,4 +145,3 @@ export async function applyPatchToContent(content, patchPath, targetFile) {
152
145
  }
153
146
  return contentLines.join('\n') + '\n';
154
147
  }
155
- //# sourceMappingURL=patch-transform.js.map
@@ -62,4 +62,3 @@ export async function clearRebaseSession(projectRoot) {
62
62
  export async function hasActiveRebaseSession(projectRoot) {
63
63
  return pathExists(sessionPath(projectRoot));
64
64
  }
65
- //# sourceMappingURL=rebase-session.js.map
@@ -113,4 +113,3 @@ export async function registerBrowserContent(engineDir, fileName, after, sourceP
113
113
  afterFallback: value.afterFallback,
114
114
  };
115
115
  }
116
- //# sourceMappingURL=register-browser-content.js.map
@@ -86,4 +86,3 @@ export async function registerFireForgeModule(engineDir, fileName, moduleDir, dr
86
86
  }
87
87
  return { manifest, entry, previousEntry: value.previousEntry, skipped: false };
88
88
  }
89
- //# sourceMappingURL=register-module.js.map
@@ -6,4 +6,3 @@
6
6
  * type-only back-edge made the registration dependency graph cyclic.
7
7
  */
8
8
  export {};
9
- //# sourceMappingURL=register-result.js.map
@@ -143,4 +143,3 @@ export async function ensureXpcshellManifestWiring(engineDir, manifestRelPath, d
143
143
  dirRel = dirname(dirRel).replace(/\\/g, '/');
144
144
  }
145
145
  }
146
- //# sourceMappingURL=register-scaffold.js.map
@@ -178,4 +178,3 @@ export async function registerSharedCSS(engineDir, fileName, after, dryRun = fal
178
178
  afterFallback: value.afterFallback,
179
179
  };
180
180
  }
181
- //# sourceMappingURL=register-shared-css.js.map
@@ -98,4 +98,3 @@ export async function deregisterTestManifest(engineDir, testDir) {
98
98
  await writeText(manifestPath, filtered.join('\n'));
99
99
  return true;
100
100
  }
101
- //# sourceMappingURL=register-test-manifest.js.map
@@ -93,4 +93,3 @@ export async function registerXpcshellTest(engineDir, dirRel, fileName, dryRun =
93
93
  }
94
94
  return { manifest: manifestRel, entry, skipped: false };
95
95
  }
96
- //# sourceMappingURL=register-xpcshell-test.js.map
@@ -39,4 +39,3 @@ export function validateSharedFtl(raw, context) {
39
39
  }
40
40
  return { ok: true, value };
41
41
  }
42
- //# sourceMappingURL=shared-ftl.js.map
@@ -62,4 +62,3 @@ export async function waitForActiveCriticalSections(timeoutMs) {
62
62
  new Promise((resolve) => setTimeout(resolve, timeoutMs)),
63
63
  ]);
64
64
  }
65
- //# sourceMappingURL=signal-critical.js.map
@@ -97,4 +97,3 @@ export function compileAllowlistFromStrings(sources) {
97
97
  });
98
98
  return compiled;
99
99
  }
100
- //# sourceMappingURL=smoke-patterns.js.map
@@ -22,4 +22,3 @@ export async function quarantineStateFile(statePath, reason = 'corrupt') {
22
22
  await rename(statePath, quarantinedPath);
23
23
  return basename(quarantinedPath);
24
24
  }
25
- //# sourceMappingURL=state-file.js.map
@@ -41,6 +41,22 @@ export interface ClassifiedFile extends StatusFile {
41
41
  */
42
42
  claimedBy?: string[];
43
43
  }
44
+ /**
45
+ * Builds the file → owning-patch-filenames multimap from manifest rows.
46
+ * Single source of truth for cross-patch claim detection — status
47
+ * classification, verify's cross-claim check, and the ownership table all
48
+ * consume this builder (they used to each rebuild it, one drift away from
49
+ * disagreeing about what "claimed" means).
50
+ */
51
+ export declare function buildPatchClaims(manifestPatches: ReadonlyArray<{
52
+ filename: string;
53
+ filesAffected: string[];
54
+ }>): Map<string, string[]>;
55
+ /**
56
+ * Reduces a two-character porcelain XY status to its primary code.
57
+ * Single source of truth — status-output.ts renders from the same logic.
58
+ */
59
+ export declare function getPrimaryStatusCode(status: string): string;
44
60
  /**
45
61
  * Classifies files into patch-backed, unmanaged, branding, furnace, or
46
62
  * conflict buckets.