@hominis/fireforge 0.43.1 → 0.44.1

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 (478) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/README.md +3 -3
  3. package/dist/bin/fireforge.d.ts +4 -9
  4. package/dist/bin/fireforge.js +42 -40
  5. package/dist/build-info.json +4 -4
  6. package/dist/src/cli.js +74 -8
  7. package/dist/src/commands/bootstrap.js +17 -20
  8. package/dist/src/commands/build.js +25 -32
  9. package/dist/src/commands/config.js +34 -41
  10. package/dist/src/commands/discard.js +8 -9
  11. package/dist/src/commands/doctor-check-core.d.ts +13 -18
  12. package/dist/src/commands/doctor-check-core.js +8 -35
  13. package/dist/src/commands/doctor-external-toolchains.js +2 -2
  14. package/dist/src/commands/doctor-furnace-config-sync.d.ts +18 -0
  15. package/dist/src/commands/{doctor-furnace-manifest-sync.js → doctor-furnace-config-sync.js} +27 -26
  16. package/dist/src/commands/doctor-furnace-jar.d.ts +7 -9
  17. package/dist/src/commands/doctor-furnace-jar.js +7 -9
  18. package/dist/src/commands/doctor-furnace.js +21 -24
  19. package/dist/src/commands/doctor-orphaned-harness.d.ts +16 -20
  20. package/dist/src/commands/doctor-orphaned-harness.js +18 -44
  21. package/dist/src/commands/doctor-patch-manifest.d.ts +8 -0
  22. package/dist/src/commands/doctor-patch-manifest.js +155 -0
  23. package/dist/src/commands/doctor-post-rebase-audit.d.ts +2 -0
  24. package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
  25. package/dist/src/commands/doctor-working-tree.d.ts +10 -12
  26. package/dist/src/commands/doctor-working-tree.js +11 -18
  27. package/dist/src/commands/doctor.d.ts +1 -1
  28. package/dist/src/commands/doctor.js +64 -96
  29. package/dist/src/commands/download.js +32 -48
  30. package/dist/src/commands/export-all.js +29 -33
  31. package/dist/src/commands/export-flow.js +6 -6
  32. package/dist/src/commands/export-placement-conflicts.js +10 -9
  33. package/dist/src/commands/export-placement-gate.js +5 -5
  34. package/dist/src/commands/export-shared.d.ts +30 -33
  35. package/dist/src/commands/export-shared.js +45 -43
  36. package/dist/src/commands/export.js +21 -27
  37. package/dist/src/commands/furnace/apply.js +14 -23
  38. package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
  39. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
  40. package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
  41. package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
  42. package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
  43. package/dist/src/commands/furnace/chrome-doc.js +31 -41
  44. package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
  45. package/dist/src/commands/furnace/create-browser-test.js +8 -8
  46. package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
  47. package/dist/src/commands/furnace/create-mochikit.js +8 -9
  48. package/dist/src/commands/furnace/create-readback.d.ts +12 -13
  49. package/dist/src/commands/furnace/create-readback.js +12 -13
  50. package/dist/src/commands/furnace/create-templates.d.ts +22 -28
  51. package/dist/src/commands/furnace/create-templates.js +35 -30
  52. package/dist/src/commands/furnace/create-xpcshell.js +2 -2
  53. package/dist/src/commands/furnace/create.js +33 -23
  54. package/dist/src/commands/furnace/deploy.js +16 -24
  55. package/dist/src/commands/furnace/diff.js +15 -10
  56. package/dist/src/commands/furnace/init.js +33 -36
  57. package/dist/src/commands/furnace/override.js +23 -25
  58. package/dist/src/commands/furnace/preview.js +47 -67
  59. package/dist/src/commands/furnace/refresh.js +12 -16
  60. package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
  61. package/dist/src/commands/furnace/remove-mutations.js +14 -17
  62. package/dist/src/commands/furnace/remove-state.js +1 -1
  63. package/dist/src/commands/furnace/remove.js +33 -38
  64. package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
  65. package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
  66. package/dist/src/commands/furnace/rename.js +59 -69
  67. package/dist/src/commands/furnace/scan.js +19 -33
  68. package/dist/src/commands/furnace/status.js +12 -9
  69. package/dist/src/commands/furnace/sync.js +9 -16
  70. package/dist/src/commands/furnace/validate.js +20 -20
  71. package/dist/src/commands/import.js +48 -37
  72. package/dist/src/commands/lint-per-patch.js +15 -10
  73. package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
  74. package/dist/src/commands/lint-per-run-checkjs.js +13 -13
  75. package/dist/src/commands/lint-report.js +6 -7
  76. package/dist/src/commands/lint.d.ts +15 -16
  77. package/dist/src/commands/lint.js +86 -102
  78. package/dist/src/commands/package.js +6 -11
  79. package/dist/src/commands/patch/compact.js +1 -1
  80. package/dist/src/commands/patch/delete.js +24 -33
  81. package/dist/src/commands/patch/index.d.ts +5 -6
  82. package/dist/src/commands/patch/index.js +10 -12
  83. package/dist/src/commands/patch/lint-ignore.js +14 -13
  84. package/dist/src/commands/patch/move-files-create.d.ts +6 -6
  85. package/dist/src/commands/patch/move-files-create.js +8 -8
  86. package/dist/src/commands/patch/move-files-into.d.ts +6 -6
  87. package/dist/src/commands/patch/move-files-into.js +8 -8
  88. package/dist/src/commands/patch/patch-context.d.ts +2 -3
  89. package/dist/src/commands/patch/patch-context.js +2 -3
  90. package/dist/src/commands/patch/rename.d.ts +10 -11
  91. package/dist/src/commands/patch/rename.js +11 -12
  92. package/dist/src/commands/patch/reorder.js +8 -9
  93. package/dist/src/commands/patch/split-plan.js +1 -1
  94. package/dist/src/commands/patch/split.d.ts +8 -10
  95. package/dist/src/commands/patch/split.js +9 -11
  96. package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
  97. package/dist/src/commands/patch/staged-dependency.js +7 -3
  98. package/dist/src/commands/patch/tier.d.ts +9 -9
  99. package/dist/src/commands/patch/tier.js +18 -15
  100. package/dist/src/commands/re-export-adjacent.js +9 -8
  101. package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
  102. package/dist/src/commands/re-export-bulk-scan.js +22 -21
  103. package/dist/src/commands/re-export-drift.js +10 -10
  104. package/dist/src/commands/re-export-files.d.ts +6 -7
  105. package/dist/src/commands/re-export-files.js +34 -44
  106. package/dist/src/commands/re-export-lint.d.ts +10 -13
  107. package/dist/src/commands/re-export-lint.js +16 -14
  108. package/dist/src/commands/re-export-options.d.ts +6 -6
  109. package/dist/src/commands/re-export-options.js +6 -6
  110. package/dist/src/commands/re-export-register.d.ts +4 -10
  111. package/dist/src/commands/re-export-register.js +5 -11
  112. package/dist/src/commands/re-export-scan.js +1 -1
  113. package/dist/src/commands/re-export-single.d.ts +3 -3
  114. package/dist/src/commands/re-export-single.js +33 -25
  115. package/dist/src/commands/re-export.js +13 -21
  116. package/dist/src/commands/rebase/abort.js +3 -3
  117. package/dist/src/commands/rebase/confirm.js +2 -1
  118. package/dist/src/commands/rebase/continue.d.ts +1 -1
  119. package/dist/src/commands/rebase/continue.js +14 -9
  120. package/dist/src/commands/rebase/index.js +11 -11
  121. package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
  122. package/dist/src/commands/rebase/patch-loop.js +60 -39
  123. package/dist/src/commands/rebase/summary.js +1 -1
  124. package/dist/src/commands/register.js +5 -7
  125. package/dist/src/commands/reset.js +2 -1
  126. package/dist/src/commands/resolve.d.ts +16 -13
  127. package/dist/src/commands/resolve.js +30 -33
  128. package/dist/src/commands/run.js +33 -35
  129. package/dist/src/commands/setup-support.js +15 -19
  130. package/dist/src/commands/setup.js +13 -9
  131. package/dist/src/commands/status-check.d.ts +6 -6
  132. package/dist/src/commands/status-json.d.ts +6 -6
  133. package/dist/src/commands/status-json.js +5 -0
  134. package/dist/src/commands/status-output.d.ts +5 -5
  135. package/dist/src/commands/status-output.js +6 -6
  136. package/dist/src/commands/status-ownership.d.ts +16 -14
  137. package/dist/src/commands/status-ownership.js +16 -14
  138. package/dist/src/commands/status.js +42 -34
  139. package/dist/src/commands/test-build-phase.js +26 -13
  140. package/dist/src/commands/test-diagnose.js +44 -44
  141. package/dist/src/commands/test-register.js +1 -0
  142. package/dist/src/commands/test-run.d.ts +14 -16
  143. package/dist/src/commands/test-run.js +21 -13
  144. package/dist/src/commands/test-stale-gate.d.ts +15 -16
  145. package/dist/src/commands/test-stale-gate.js +15 -16
  146. package/dist/src/commands/test-verdict.d.ts +20 -3
  147. package/dist/src/commands/test-verdict.js +30 -0
  148. package/dist/src/commands/test.js +83 -76
  149. package/dist/src/commands/token-coverage.js +3 -4
  150. package/dist/src/commands/token.js +16 -18
  151. package/dist/src/commands/tree.js +47 -31
  152. package/dist/src/commands/typecheck.d.ts +10 -12
  153. package/dist/src/commands/typecheck.js +24 -27
  154. package/dist/src/commands/verify.js +13 -15
  155. package/dist/src/commands/watch.js +21 -23
  156. package/dist/src/commands/wire.js +52 -62
  157. package/dist/src/core/ast-utils.d.ts +8 -7
  158. package/dist/src/core/ast-utils.js +8 -7
  159. package/dist/src/core/branding.d.ts +9 -14
  160. package/dist/src/core/branding.js +7 -11
  161. package/dist/src/core/browser-wire.d.ts +1 -1
  162. package/dist/src/core/browser-wire.js +14 -19
  163. package/dist/src/core/build-audit-platform.js +25 -33
  164. package/dist/src/core/build-audit-registration.js +17 -25
  165. package/dist/src/core/build-audit-resolve.d.ts +15 -17
  166. package/dist/src/core/build-audit-resolve.js +39 -36
  167. package/dist/src/core/build-audit-transforms.js +18 -19
  168. package/dist/src/core/build-audit.js +28 -42
  169. package/dist/src/core/build-baseline-types.d.ts +29 -35
  170. package/dist/src/core/build-baseline.d.ts +5 -4
  171. package/dist/src/core/build-baseline.js +14 -14
  172. package/dist/src/core/build-overwrite-guard.d.ts +7 -14
  173. package/dist/src/core/build-overwrite-guard.js +17 -27
  174. package/dist/src/core/build-prepare.d.ts +2 -2
  175. package/dist/src/core/build-prepare.js +8 -9
  176. package/dist/src/core/config-mutate.js +20 -19
  177. package/dist/src/core/config-state.js +12 -4
  178. package/dist/src/core/config-validate.js +8 -5
  179. package/dist/src/core/config.d.ts +5 -8
  180. package/dist/src/core/config.js +7 -16
  181. package/dist/src/core/coverage-extend.d.ts +15 -13
  182. package/dist/src/core/coverage-extend.js +15 -13
  183. package/dist/src/core/destructive.d.ts +31 -9
  184. package/dist/src/core/destructive.js +29 -26
  185. package/dist/src/core/diff-hunks.js +10 -12
  186. package/dist/src/core/discard-baseline.d.ts +8 -8
  187. package/dist/src/core/discard-baseline.js +16 -15
  188. package/dist/src/core/display-state.d.ts +12 -14
  189. package/dist/src/core/display-state.js +12 -14
  190. package/dist/src/core/engine-changes.d.ts +2 -4
  191. package/dist/src/core/engine-changes.js +2 -4
  192. package/dist/src/core/engine-precondition.d.ts +28 -13
  193. package/dist/src/core/engine-precondition.js +25 -10
  194. package/dist/src/core/engine-session-lock.d.ts +47 -21
  195. package/dist/src/core/engine-session-lock.js +168 -33
  196. package/dist/src/core/export-moved-code-shape.d.ts +10 -10
  197. package/dist/src/core/export-moved-code-shape.js +10 -10
  198. package/dist/src/core/file-lock.d.ts +62 -5
  199. package/dist/src/core/file-lock.js +196 -59
  200. package/dist/src/core/firefox-cache.js +1 -3
  201. package/dist/src/core/firefox-extract.js +10 -14
  202. package/dist/src/core/firefox.d.ts +7 -5
  203. package/dist/src/core/firefox.js +4 -5
  204. package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
  205. package/dist/src/core/furnace-apply-dry-run.js +3 -2
  206. package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
  207. package/dist/src/core/furnace-apply-ftl.js +16 -13
  208. package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
  209. package/dist/src/core/furnace-apply-helpers.js +30 -21
  210. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
  211. package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
  212. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
  213. package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
  214. package/dist/src/core/furnace-apply.d.ts +5 -8
  215. package/dist/src/core/furnace-apply.js +19 -24
  216. package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
  217. package/dist/src/core/furnace-checksum-utils.js +16 -0
  218. package/dist/src/core/furnace-config.d.ts +21 -30
  219. package/dist/src/core/furnace-config.js +32 -54
  220. package/dist/src/core/furnace-constants.d.ts +8 -0
  221. package/dist/src/core/furnace-constants.js +10 -2
  222. package/dist/src/core/furnace-css-fragments.d.ts +5 -5
  223. package/dist/src/core/furnace-css-fragments.js +5 -5
  224. package/dist/src/core/furnace-jsconfig.d.ts +11 -12
  225. package/dist/src/core/furnace-jsconfig.js +11 -12
  226. package/dist/src/core/furnace-marker.d.ts +7 -8
  227. package/dist/src/core/furnace-marker.js +7 -8
  228. package/dist/src/core/furnace-operation.d.ts +31 -0
  229. package/dist/src/core/furnace-operation.js +54 -17
  230. package/dist/src/core/furnace-precondition.d.ts +61 -0
  231. package/dist/src/core/furnace-precondition.js +67 -0
  232. package/dist/src/core/furnace-registration-remove.d.ts +5 -6
  233. package/dist/src/core/furnace-registration-remove.js +6 -7
  234. package/dist/src/core/furnace-registration-validate.d.ts +13 -0
  235. package/dist/src/core/furnace-registration-validate.js +20 -1
  236. package/dist/src/core/furnace-registration.d.ts +5 -6
  237. package/dist/src/core/furnace-registration.js +10 -12
  238. package/dist/src/core/furnace-rollback.js +7 -7
  239. package/dist/src/core/furnace-scanner.d.ts +1 -0
  240. package/dist/src/core/furnace-scanner.js +1 -2
  241. package/dist/src/core/furnace-stale-export.d.ts +10 -10
  242. package/dist/src/core/furnace-stale-export.js +10 -10
  243. package/dist/src/core/furnace-state-persist.d.ts +6 -8
  244. package/dist/src/core/furnace-state-persist.js +6 -8
  245. package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
  246. package/dist/src/core/furnace-validate-accessibility.js +310 -1
  247. package/dist/src/core/furnace-validate-compatibility.js +141 -1
  248. package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
  249. package/dist/src/core/furnace-validate-helpers.js +0 -491
  250. package/dist/src/core/furnace-validate-registration.d.ts +7 -0
  251. package/dist/src/core/furnace-validate-registration.js +115 -41
  252. package/dist/src/core/furnace-validate.d.ts +16 -0
  253. package/dist/src/core/furnace-validate.js +188 -145
  254. package/dist/src/core/furnace-version-drift.d.ts +10 -11
  255. package/dist/src/core/furnace-version-drift.js +10 -11
  256. package/dist/src/core/git-base.d.ts +21 -9
  257. package/dist/src/core/git-base.js +24 -9
  258. package/dist/src/core/git-diff.d.ts +20 -9
  259. package/dist/src/core/git-diff.js +148 -63
  260. package/dist/src/core/git-file-ops.d.ts +19 -19
  261. package/dist/src/core/git-file-ops.js +19 -19
  262. package/dist/src/core/git-readonly-index.d.ts +53 -19
  263. package/dist/src/core/git-readonly-index.js +75 -31
  264. package/dist/src/core/git-status.d.ts +5 -6
  265. package/dist/src/core/git-status.js +5 -6
  266. package/dist/src/core/git.d.ts +9 -10
  267. package/dist/src/core/git.js +57 -71
  268. package/dist/src/core/license-headers.d.ts +17 -23
  269. package/dist/src/core/license-headers.js +17 -23
  270. package/dist/src/core/mach-build-artifacts.d.ts +9 -9
  271. package/dist/src/core/mach-build-artifacts.js +57 -19
  272. package/dist/src/core/mach-error-hints.js +42 -29
  273. package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
  274. package/dist/src/core/mach-known-noise-filter.js +18 -20
  275. package/dist/src/core/mach-mozconfig.d.ts +14 -0
  276. package/dist/src/core/mach-mozconfig.js +30 -0
  277. package/dist/src/core/mach-resource-shim.d.ts +43 -58
  278. package/dist/src/core/mach-resource-shim.js +50 -67
  279. package/dist/src/core/mach.d.ts +37 -41
  280. package/dist/src/core/mach.js +50 -51
  281. package/dist/src/core/marionette-port.d.ts +36 -1
  282. package/dist/src/core/marionette-port.js +96 -31
  283. package/dist/src/core/marionette-preflight.d.ts +8 -12
  284. package/dist/src/core/marionette-preflight.js +86 -52
  285. package/dist/src/core/mochitest-server-port.d.ts +42 -0
  286. package/dist/src/core/mochitest-server-port.js +119 -0
  287. package/dist/src/core/module-resolution-preflight.d.ts +9 -11
  288. package/dist/src/core/module-resolution-preflight.js +9 -11
  289. package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
  290. package/dist/src/core/moz-manifest-helpers.js +163 -0
  291. package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
  292. package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
  293. package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
  294. package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
  295. package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
  296. package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
  297. package/dist/src/core/ownership-table.d.ts +14 -16
  298. package/dist/src/core/ownership-table.js +14 -16
  299. package/dist/src/core/parser-fallback.d.ts +15 -1
  300. package/dist/src/core/parser-fallback.js +22 -3
  301. package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
  302. package/dist/src/core/patch-apply-fuzz.js +15 -18
  303. package/dist/src/core/patch-apply.d.ts +13 -34
  304. package/dist/src/core/patch-apply.js +16 -76
  305. package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
  306. package/dist/src/core/patch-artifact-normalize.js +13 -15
  307. package/dist/src/core/patch-export-metadata.d.ts +12 -2
  308. package/dist/src/core/patch-export-metadata.js +13 -4
  309. package/dist/src/core/patch-export-update.d.ts +13 -1
  310. package/dist/src/core/patch-export-update.js +25 -8
  311. package/dist/src/core/patch-export.d.ts +15 -8
  312. package/dist/src/core/patch-export.js +10 -11
  313. package/dist/src/core/patch-files.d.ts +5 -6
  314. package/dist/src/core/patch-files.js +5 -6
  315. package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
  316. package/dist/src/core/patch-identifier-suggest.js +14 -19
  317. package/dist/src/core/patch-lint-binary.d.ts +51 -0
  318. package/dist/src/core/patch-lint-binary.js +39 -0
  319. package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
  320. package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
  321. package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
  322. package/dist/src/core/patch-lint-checkjs.js +41 -20
  323. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
  324. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
  325. package/dist/src/core/patch-lint-creators.d.ts +8 -8
  326. package/dist/src/core/patch-lint-creators.js +8 -8
  327. package/dist/src/core/patch-lint-cross.d.ts +17 -20
  328. package/dist/src/core/patch-lint-cross.js +16 -16
  329. package/dist/src/core/patch-lint-css.d.ts +3 -5
  330. package/dist/src/core/patch-lint-css.js +17 -22
  331. package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
  332. package/dist/src/core/patch-lint-diff-tag.js +14 -16
  333. package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
  334. package/dist/src/core/patch-lint-jsdoc.js +26 -19
  335. package/dist/src/core/patch-lint-mozbuild.js +20 -20
  336. package/dist/src/core/patch-lint-observer.d.ts +7 -7
  337. package/dist/src/core/patch-lint-observer.js +7 -7
  338. package/dist/src/core/patch-lint.d.ts +38 -29
  339. package/dist/src/core/patch-lint.js +89 -121
  340. package/dist/src/core/patch-lock.d.ts +1 -1
  341. package/dist/src/core/patch-lock.js +45 -9
  342. package/dist/src/core/patch-manifest-consistency.d.ts +57 -6
  343. package/dist/src/core/patch-manifest-consistency.js +111 -40
  344. package/dist/src/core/patch-manifest-files-affected.d.ts +43 -0
  345. package/dist/src/core/patch-manifest-files-affected.js +77 -0
  346. package/dist/src/core/patch-manifest-io.js +14 -18
  347. package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
  348. package/dist/src/core/patch-manifest-resolve.js +9 -12
  349. package/dist/src/core/patch-manifest-validate.d.ts +6 -1
  350. package/dist/src/core/patch-manifest-validate.js +25 -13
  351. package/dist/src/core/patch-manifest.d.ts +2 -1
  352. package/dist/src/core/patch-manifest.js +2 -1
  353. package/dist/src/core/patch-parse.d.ts +24 -13
  354. package/dist/src/core/patch-parse.js +19 -14
  355. package/dist/src/core/patch-policy.d.ts +2 -2
  356. package/dist/src/core/patch-policy.js +2 -2
  357. package/dist/src/core/patch-registration-refs.d.ts +7 -8
  358. package/dist/src/core/patch-registration-refs.js +9 -9
  359. package/dist/src/core/process-cpu-time.d.ts +12 -0
  360. package/dist/src/core/process-cpu-time.js +52 -0
  361. package/dist/src/core/rebase-session.d.ts +38 -26
  362. package/dist/src/core/rebase-session.js +55 -28
  363. package/dist/src/core/register-browser-content.js +9 -74
  364. package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
  365. package/dist/src/core/register-module.js +10 -43
  366. package/dist/src/core/register-scaffold.d.ts +8 -8
  367. package/dist/src/core/register-scaffold.js +10 -10
  368. package/dist/src/core/register-shared-css.d.ts +9 -12
  369. package/dist/src/core/register-shared-css.js +17 -87
  370. package/dist/src/core/register-xpcshell-test.d.ts +6 -7
  371. package/dist/src/core/register-xpcshell-test.js +7 -8
  372. package/dist/src/core/shared-ftl.js +12 -4
  373. package/dist/src/core/state-file.d.ts +45 -0
  374. package/dist/src/core/state-file.js +34 -0
  375. package/dist/src/core/status-binary.d.ts +18 -11
  376. package/dist/src/core/status-binary.js +19 -12
  377. package/dist/src/core/status-classify.d.ts +30 -36
  378. package/dist/src/core/status-classify.js +21 -26
  379. package/dist/src/core/test-changed-prefs.d.ts +30 -0
  380. package/dist/src/core/test-changed-prefs.js +78 -0
  381. package/dist/src/core/test-harness-crash.d.ts +25 -63
  382. package/dist/src/core/test-harness-crash.js +121 -142
  383. package/dist/src/core/test-harness-signature.d.ts +18 -0
  384. package/dist/src/core/test-harness-signature.js +13 -0
  385. package/dist/src/core/test-path-scope.d.ts +12 -15
  386. package/dist/src/core/test-path-scope.js +15 -18
  387. package/dist/src/core/test-stale-check.d.ts +17 -17
  388. package/dist/src/core/test-stale-check.js +50 -43
  389. package/dist/src/core/test-stall-triage.d.ts +51 -0
  390. package/dist/src/core/test-stall-triage.js +126 -0
  391. package/dist/src/core/token-category.js +66 -14
  392. package/dist/src/core/token-coverage.js +9 -9
  393. package/dist/src/core/token-dark-mode.d.ts +9 -11
  394. package/dist/src/core/token-dark-mode.js +29 -13
  395. package/dist/src/core/token-docs.d.ts +2 -0
  396. package/dist/src/core/token-docs.js +3 -1
  397. package/dist/src/core/token-manager.d.ts +10 -2
  398. package/dist/src/core/token-manager.js +17 -8
  399. package/dist/src/core/token-scaffold.d.ts +7 -7
  400. package/dist/src/core/token-scaffold.js +7 -7
  401. package/dist/src/core/token-variant.d.ts +4 -4
  402. package/dist/src/core/token-variant.js +28 -6
  403. package/dist/src/core/toolchain-preflight.d.ts +20 -21
  404. package/dist/src/core/toolchain-preflight.js +28 -31
  405. package/dist/src/core/tree-guard.js +12 -2
  406. package/dist/src/core/tree-store.d.ts +41 -27
  407. package/dist/src/core/tree-store.js +68 -43
  408. package/dist/src/core/typecheck-shim.d.ts +23 -24
  409. package/dist/src/core/typecheck-shim.js +23 -24
  410. package/dist/src/core/typecheck.js +15 -9
  411. package/dist/src/core/wire-destroy.d.ts +3 -3
  412. package/dist/src/core/wire-destroy.js +7 -15
  413. package/dist/src/core/wire-dom-fragment.d.ts +9 -17
  414. package/dist/src/core/wire-dom-fragment.js +11 -44
  415. package/dist/src/core/wire-init.d.ts +2 -3
  416. package/dist/src/core/wire-init.js +8 -19
  417. package/dist/src/core/wire-subscript.d.ts +2 -3
  418. package/dist/src/core/wire-subscript.js +4 -13
  419. package/dist/src/core/wire-utils.d.ts +7 -8
  420. package/dist/src/core/wire-utils.js +7 -8
  421. package/dist/src/core/xpcshell-appdir.d.ts +38 -45
  422. package/dist/src/core/xpcshell-appdir.js +38 -45
  423. package/dist/src/errors/base.d.ts +37 -16
  424. package/dist/src/errors/base.js +37 -16
  425. package/dist/src/errors/build.d.ts +13 -1
  426. package/dist/src/errors/build.js +14 -2
  427. package/dist/src/errors/codes.d.ts +16 -0
  428. package/dist/src/errors/codes.js +16 -0
  429. package/dist/src/errors/furnace.d.ts +4 -4
  430. package/dist/src/errors/git.d.ts +6 -10
  431. package/dist/src/errors/git.js +6 -10
  432. package/dist/src/errors/patch.d.ts +7 -7
  433. package/dist/src/errors/patch.js +7 -7
  434. package/dist/src/errors/rebase.d.ts +4 -4
  435. package/dist/src/errors/rebase.js +4 -4
  436. package/dist/src/index.d.ts +7 -1
  437. package/dist/src/index.js +5 -0
  438. package/dist/src/types/commands/index.d.ts +2 -2
  439. package/dist/src/types/commands/options.d.ts +111 -70
  440. package/dist/src/types/commands/patches.d.ts +24 -34
  441. package/dist/src/types/commands/project.d.ts +5 -34
  442. package/dist/src/types/config.d.ts +25 -23
  443. package/dist/src/types/config.js +6 -1
  444. package/dist/src/types/furnace.d.ts +37 -13
  445. package/dist/src/types/furnace.js +19 -1
  446. package/dist/src/types/index.d.ts +3 -3
  447. package/dist/src/utils/build-info.d.ts +7 -8
  448. package/dist/src/utils/build-info.js +12 -11
  449. package/dist/src/utils/errors.d.ts +6 -8
  450. package/dist/src/utils/errors.js +6 -8
  451. package/dist/src/utils/logger.d.ts +27 -10
  452. package/dist/src/utils/logger.js +28 -16
  453. package/dist/src/utils/machine-output.d.ts +12 -0
  454. package/dist/src/utils/machine-output.js +33 -0
  455. package/dist/src/utils/options.d.ts +31 -14
  456. package/dist/src/utils/options.js +86 -30
  457. package/dist/src/utils/paths.d.ts +10 -8
  458. package/dist/src/utils/paths.js +10 -8
  459. package/dist/src/utils/platform.d.ts +0 -25
  460. package/dist/src/utils/platform.js +1 -42
  461. package/dist/src/utils/process-group.d.ts +9 -4
  462. package/dist/src/utils/process-group.js +19 -5
  463. package/dist/src/utils/process.d.ts +42 -7
  464. package/dist/src/utils/process.js +44 -17
  465. package/dist/src/utils/ps-duration.d.ts +31 -0
  466. package/dist/src/utils/ps-duration.js +70 -0
  467. package/dist/src/utils/regex.d.ts +9 -10
  468. package/dist/src/utils/regex.js +9 -10
  469. package/dist/src/utils/validation.d.ts +58 -28
  470. package/dist/src/utils/validation.js +60 -33
  471. package/package.json +3 -3
  472. package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
  473. package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
  474. package/dist/src/core/manifest-helpers.d.ts +0 -38
  475. package/dist/src/core/manifest-helpers.js +0 -120
  476. /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
  477. /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
  478. /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
@@ -17,12 +17,11 @@ import { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs, resolvePatchOw
17
17
  // Cross-patch lint re-exports
18
18
  // ---------------------------------------------------------------------------
19
19
  //
20
- // The cross-patch lint infrastructure (queue context builder, duplicate-
21
- // creation and forward-import rules, ignore marker) lives in
22
- // `patch-lint-cross.ts` so the per-patch and cross-patch rule bodies can
23
- // each stay within the project's per-file line budget. Re-export the
24
- // public surface from `patch-lint-reexports.ts` so callers continue to
25
- // import from a single module.
20
+ // The cross-patch lint infrastructure (queue context builder,
21
+ // duplicate-creation and forward-import rules, ignore marker) lives in
22
+ // `patch-lint-cross.ts` so the per-patch and cross-patch rule bodies each
23
+ // stay within the per-file line budget. The public surface is re-exported
24
+ // from `patch-lint-reexports.ts` so callers keep importing from one module.
26
25
  export * from './patch-lint-reexports.js';
27
26
  // The CSS rule bodies live in `patch-lint-css.ts` (same per-file-budget
28
27
  // split as the other rule families); re-export the imported binding so
@@ -38,10 +37,10 @@ const FILE_SIZE_THRESHOLDS = {
38
37
  };
39
38
  /**
40
39
  * Counts the lines of `content` the way `wc -l` reports them: a trailing
41
- * newline terminates the last line rather than starting an empty extra
42
- * one. A naive `split('\n').length` over-counts by one for any content
43
- * ending in `\n` (the common case), which made the size rules fire one
44
- * line early against the operator's own `wc -l` measurement.
40
+ * newline terminates the last line rather than starting an empty extra one.
41
+ * A naive `split('\n').length` over-counts by one for any content ending in
42
+ * `\n` (the common case), making the size rules fire one line early against
43
+ * the operator's own `wc -l` measurement.
45
44
  */
46
45
  function countContentLines(content) {
47
46
  const lines = content.split('\n');
@@ -94,50 +93,39 @@ const PATCH_LINE_THRESHOLDS = {
94
93
  * Branding patches have a legitimate reason to be large: they include
95
94
  * every locale's `brand.ftl`, copied upstream CSS/PNG assets, and the
96
95
  * fork-specific `configure.sh` / `brand.properties` under a single
97
- * `browser/branding/<name>/` subtree. Calibrated against:
96
+ * `browser/branding/<name>/` subtree.
98
97
  *
99
- * - The 2026-04-21 eval baseline: a fresh-fork branding export landed
100
- * at 15904 lines (localized brand.ftl across many locales + SVG path
101
- * data + copied upstream CSS).
102
- * - The 2026-04-25 operator data point: a freshly setup branding patch
103
- * (post-binary-exclusion, after Phase 1+2 patch splits) landed at
104
- * 15650 lines — within 2% of the eval baseline.
105
- *
106
- * Both data points need to surface as a soft `notice` rather than a
107
- * `warning`, since they represent the *minimum* branding diff. The
108
- * pre-2026-04-25 calibration {3000/8000/20000} put 15904 firmly in the
109
- * `warning` band, contradicting the docstring's "loud but not
110
- * actionable" intent. The current calibration moves the warning band
111
- * above the eval baseline (with ~13% headroom) and the error band to
112
- * roughly 2× the baseline — reaching `error` strongly suggests
113
- * non-branding work is bundled in.
98
+ * Calibrated against a fresh-fork branding export, which lands around
99
+ * 15–16k lines (localized brand.ftl across many locales + SVG path data +
100
+ * copied upstream CSS). That is the MINIMUM branding diff, so it must
101
+ * surface as a soft `notice` rather than a `warning`: the warning band
102
+ * sits above it with ~13% headroom, and the error band at roughly 2× the
103
+ * baseline, where non-branding work is probably bundled in.
114
104
  *
115
105
  * Permissive thresholds are safe because the *gate* into this tier is
116
- * narrow (auto-detect requires every file under `browser/branding/`
117
- * plus a tight registration allowlist, or an explicit
118
- * `PatchMetadata.tier: "branding"` opt-in). A non-branding patch
119
- * cannot accidentally land here.
106
+ * narrow: auto-detect requires every file under `browser/branding/` plus a
107
+ * tight registration allowlist, or an explicit `PatchMetadata.tier:
108
+ * "branding"` opt-in. A non-branding patch cannot accidentally land here.
120
109
  */
121
110
  branding: { notice: 8000, warning: 18000, error: 30000 },
122
111
  };
123
112
  /**
124
- * File-count thresholds for the `large-patch-files` rule, mirroring the
125
- * tier shape of {@link PATCH_LINE_THRESHOLDS}. A single warning-only
126
- * threshold per tier is intentional — file count expresses scope, not
127
- * blast radius, and there is no error band that would block export.
113
+ * File-count thresholds for the `large-patch-files` rule, mirroring the tier
114
+ * shape of {@link PATCH_LINE_THRESHOLDS}. A single warning-only threshold
115
+ * per tier is intentional — file count expresses scope, not blast radius,
116
+ * and there is no error band that would block export.
128
117
  *
129
118
  * The branding tier sits well above the typical floor because branding
130
- * patches inherently span many files: PNG/ICO icon assets in 7+ sizes,
131
- * MSIX manifests, channel-specific configs, locale `.ftl` files,
132
- * Windows/macOS launcher resources. The 2026-04-25 operator data point
133
- * reported a 56-file fresh-fork branding bundle as the minimum shape;
134
- * 60 leaves headroom for additional channels/locales while still firing
135
- * on a genuinely bloated patch.
119
+ * patches inherently span many files: PNG/ICO icon assets in 7+ sizes, MSIX
120
+ * manifests, channel-specific configs, locale `.ftl` files, Windows/macOS
121
+ * launcher resources. A fresh-fork branding bundle is around 56 files, so 60
122
+ * leaves headroom for additional channels/locales while still firing on a
123
+ * genuinely bloated patch.
136
124
  *
137
- * Test tier matches general because a test-only patch rarely touches
138
- * many files (a single regression test usually adds 1–3 fixtures); the
139
- * elevation in {@link PATCH_LINE_THRESHOLDS.test} addresses big
140
- * table-driven test bodies, not file fan-out.
125
+ * Test tier matches general because a test-only patch rarely touches many
126
+ * files (a single regression test usually adds 1–3 fixtures); the elevation
127
+ * in {@link PATCH_LINE_THRESHOLDS.test} addresses big table-driven test
128
+ * bodies, not file fan-out.
141
129
  */
142
130
  const PATCH_FILES_THRESHOLDS = {
143
131
  general: 5,
@@ -145,29 +133,25 @@ const PATCH_FILES_THRESHOLDS = {
145
133
  branding: 60,
146
134
  };
147
135
  /**
148
- * Shared remedy sentence for the over-budget findings: the two
149
- * places an operator stands when a patch outgrows its budget must name the
150
- * sanctioned one-transaction way out, or they reach for the wrong tools
151
- * first (the field report tried two before finding it). One constant, four
152
- * emission sites — the wording cannot drift.
136
+ * Shared remedy sentence for the over-budget findings. Both places an
137
+ * operator stands when a patch outgrows its budget must name the sanctioned
138
+ * one-transaction way out, or they reach for the wrong tools first. One
139
+ * constant, four emission sites, so the wording cannot drift.
153
140
  */
154
141
  const SPLIT_HINT = 'Consider splitting into smaller, focused patches — ' +
155
142
  '"fireforge patch move-files <from> <to> --file <path> --create --order <n>" ' +
156
143
  'moves files into a new patch in one transaction.';
157
144
  /**
158
145
  * Fixed allowlist of non-branding sibling paths that real-world Firefox
159
- * branding patches legitimately need to touch to register the new
160
- * branding flavor with the top-level configure. The 2026-04-21
161
- * external eval showed that a branding patch which also touches
162
- * `browser/moz.configure` (the canonical registration point) fell
163
- * through to the general lint tier because the original predicate
164
- * required every file to live under `browser/branding/`. This
165
- * allowlist stays intentionally narrow — additions require a real
166
- * operator data point, not a speculative expansion. Add new entries
167
- * only when a genuine branding patch cannot be expressed without a
168
- * specific registration sibling.
146
+ * branding patches legitimately need to touch to register the new branding
147
+ * flavor with the top-level configure. A predicate requiring every file to
148
+ * live under `browser/branding/` drops a branding patch that also touches
149
+ * `browser/moz.configure` — the canonical registration point — into the
150
+ * general lint tier.
169
151
  *
170
- * Pinned against ESR 140.x conventions at time of writing.
152
+ * Intentionally narrow: add entries only when a genuine branding patch
153
+ * cannot be expressed without a specific registration sibling. Pinned
154
+ * against ESR 140.x conventions at time of writing.
171
155
  */
172
156
  const BRANDING_REGISTRATION_FILES = new Set([
173
157
  'browser/moz.configure',
@@ -235,33 +219,31 @@ function isBrowserChromeTestFile(file) {
235
219
  /**
236
220
  * Bare assertion-call detection for the `test-needs-assertion` floor.
237
221
  *
238
- * Each alternative must sit at a call boundary: preceded by something that is
239
- * not an identifier character, `.`, `#`, or `-`. Until 0.41.0 this was a plain
240
- * `strippedContent.includes(tok)` over the tokens `Assert.`, `ok(`, `is(`,
241
- * `isnot(`, `isDeeply(`, so any identifier merely *ending* in one satisfied
242
- * the gate — `this.axis(3)` contains `is(`, `book(` contains `ok(`,
243
- * `promiseIsDeeply(` contains `isDeeply(`. An assertion-free smoke test using
244
- * any such call passed the floor, which is exactly what the rule exists to
245
- * catch.
222
+ * Each alternative must sit at a call boundary: preceded by something that
223
+ * is not an identifier character, `.`, `#`, or `-`. A plain
224
+ * `strippedContent.includes(tok)` over `Assert.`, `ok(`, `is(`, `isnot(`,
225
+ * `isDeeply(` lets any identifier merely *ending* in one satisfy the gate —
226
+ * `this.axis(3)` contains `is(`, `book(` contains `ok(`,
227
+ * `promiseIsDeeply(` contains `isDeeply(` — so an assertion-free smoke test
228
+ * using any such call clears the floor.
246
229
  *
247
- * The namespaced arm must name a member and a call. Matching the namespace and
248
- * its dot alone — as `(?:Assert|SimpleTest)\.` did — re-opened the same hole
249
- * from the other side: `SimpleTest` is mostly *harness* API, so
250
- * `SimpleTest.finish()`, `SimpleTest.waitForExplicitFinish()` and
251
- * `SimpleTest.requestCompleteLog()` each cleared the floor on their own. Those
252
- * are the exact calls an assertion-free smoke test contains. The enumeration
253
- * below is the mochitest/xpcshell assertion surface both namespaces share, and
254
- * it is what the user-facing message already names.
230
+ * The namespaced arm must name a member AND a call. Matching the namespace
231
+ * and its dot alone re-opens the same hole from the other side:
232
+ * `SimpleTest` is mostly *harness* API, so `SimpleTest.finish()`,
233
+ * `SimpleTest.waitForExplicitFinish()` and `SimpleTest.requestCompleteLog()`
234
+ * would each clear the floor on their own — the exact calls an
235
+ * assertion-free smoke test contains. The enumeration below is the
236
+ * mochitest/xpcshell assertion surface both namespaces share, and it is what
237
+ * the user-facing message names.
255
238
  *
256
- * Two qualified spellings of the bare four still count: `window.ok(...)` (the
257
- * globals ARE window properties) and an optional-chained `win?.ok(...)`.
258
- * Blocking those flagged tests whose only assertions are real as
239
+ * Two qualified spellings of the bare four also count: `window.ok(...)` (the
240
+ * globals ARE window properties) and an optional-chained `win?.ok(...)`;
241
+ * blocking those flags tests whose only assertions are real as
259
242
  * assertion-free. `SpecialPowers.ok(` stays excluded — that is API surface,
260
243
  * not an assertion.
261
244
  *
262
- * Deliberately still textual, not AST-based: this runs over patch bodies
263
- * rather than resolvable sources, and the surrounding rule is a floor, not a
264
- * proof.
245
+ * Deliberately textual, not AST-based: this runs over patch bodies rather
246
+ * than resolvable sources, and the surrounding rule is a floor, not a proof.
265
247
  */
266
248
  const ASSERTION_MEMBERS = [
267
249
  'ok',
@@ -318,22 +300,18 @@ export function commentStyleForFile(file) {
318
300
  * Accepted shapes, in order:
319
301
  * - the project's own header for the file's comment style;
320
302
  * - under `browser/branding/`, ANY recognised license header in the
321
- * matching style: the setup-generated branding directory is copied
322
- * from Firefox's `unofficial` template and arrives with Mozilla
323
- * MPL-2.0 headers — legitimate for copyright purposes (the assets are
324
- * Mozilla's) even when the fork's own code is 0BSD / EUPL-1.2 /
325
- * GPL-2.0-or-later;
303
+ * matching style: the setup-generated branding directory is copied from
304
+ * Firefox's `unofficial` template and arrives with Mozilla MPL-2.0
305
+ * headers — legitimate for copyright purposes (the assets are Mozilla's)
306
+ * even when the fork's own code is 0BSD / EUPL-1.2 / GPL-2.0-or-later;
326
307
  * - on an MPL-2.0 project, any recognised header in the matching style;
327
308
  * - the verbatim upstream MPL-2.0 block header (`/* … *\/`, optionally
328
- * behind a leading editor-directive comment) REGARDLESS of the
329
- * project license, on any file whose comment syntax is the block form
330
- * (JS and CSS): a file copied from the Firefox tree legitimately
331
- * keeps Mozilla's header for provenance in an EUPL/GPL/0BSD project
332
- * too. 0.35.0 accepted this on JS only; field verification showed a
333
- * derived CSS file carrying the identical three-line block header
334
- * still needed a patch-level lintIgnore. Only the block form is
335
- * accepted — the `// `-style MPL header is FireForge-generated, not
336
- * upstream provenance — and `hash`-style files (FTL) stay excluded
309
+ * behind a leading editor-directive comment) REGARDLESS of the project
310
+ * license, on any file whose comment syntax is the block form (JS and
311
+ * CSS): a file copied from the Firefox tree legitimately keeps Mozilla's
312
+ * header for provenance in an EUPL/GPL/0BSD project too. Only the block
313
+ * form is accepted — the `// `-style MPL header is FireForge-generated,
314
+ * not upstream provenance — and `hash`-style files (FTL) stay excluded
337
315
  * because `/* … *\/` is not a comment there.
338
316
  *
339
317
  * @param file - Engine-relative path of the new file
@@ -588,16 +566,15 @@ export function lintPatchSize(filesAffected, lineCount, patchTier) {
588
566
  const issues = [];
589
567
  // Tier selection: test > branding > general. Tests keep their elevated
590
568
  // thresholds because a big regression test is legitimate (table-driven
591
- // harnesses run into the thousands of lines). Branding patches get
592
- // their own tier so a first-export of setup-generated branding doesn't
593
- // fire the general hard limit — see `PATCH_LINE_THRESHOLDS.branding`
594
- // and `PATCH_FILES_THRESHOLDS.branding` above for the eval data
595
- // motivating this tier. An explicit `patchTier` opt-in forces branding
596
- // even when `isBrandingOnlyPatch` cannot reach the patch's actual
597
- // shape (a branding patch that also touches a non-allowlisted sibling
598
- // like a vendor-specific icon resource). Both checks read off the
599
- // same decision so the file-count and line-count rules cannot
600
- // disagree about which tier applies.
569
+ // harnesses run into the thousands of lines). Branding patches get their
570
+ // own tier so a first export of setup-generated branding does not fire the
571
+ // general hard limit — see `PATCH_LINE_THRESHOLDS.branding` and
572
+ // `PATCH_FILES_THRESHOLDS.branding` above. An explicit `patchTier` opt-in
573
+ // forces branding even when `isBrandingOnlyPatch` cannot reach the patch's
574
+ // actual shape (a branding patch that also touches a non-allowlisted
575
+ // sibling like a vendor-specific icon resource). Both checks read off the
576
+ // same decision so the file-count and line-count rules cannot disagree
577
+ // about which tier applies.
601
578
  const decision = resolvePatchSizeTier(filesAffected, patchTier);
602
579
  const fileThreshold = decision.tier === 'test'
603
580
  ? PATCH_FILES_THRESHOLDS.test
@@ -687,23 +664,14 @@ export async function lintModifiedFileHeaders(repoDir, affectedFiles, newFiles)
687
664
  * @param affectedFiles - File paths (relative to repoDir) affected by the patch
688
665
  * @param diffContent - Raw unified diff string
689
666
  * @param config - Project configuration
690
- * @param patchQueueCtx - Optional cross-patch context for ownership resolution
691
- * @param ignoreChecks - Optional set of per-patch `check` IDs to drop from the
692
- * returned issues. Threaded from `PatchMetadata.lintIgnore` so a patch that
693
- * is advisory-noisy by nature (a cohesive branding bundle, auto-generated
694
- * manifest, etc.) can opt out of a specific rule without reaching for the
695
- * blunt `--skip-lint` hammer. Not mutated by this function.
696
- * @param patchTier - Optional explicit tier override, threaded from
697
- * `PatchMetadata.tier`. When `"branding"` forces the branding
698
- * thresholds on the `large-patch-lines` rule. Callers with a
699
- * per-patch manifest context (re-export, per-patch lint) should
700
- * pass this; aggregate-mode callers without a specific patch
701
- * context skip it and fall through to auto-detection.
702
- * @param options - Optional behaviour switches; see
703
- * {@link LintExportedPatchOptions}.
667
+ * @param options - Optional context and behaviour switches; see
668
+ * {@link LintExportedPatchOptions}. Every optional input lives here
669
+ * rather than in a positional tail: four of the five call sites had to
670
+ * pass `undefined` placeholders to reach the one member they wanted.
704
671
  * @returns Array of all lint issues found
705
672
  */
706
- export async function lintExportedPatch(repoDir, affectedFiles, diffContent, config, patchQueueCtx, ignoreChecks, patchTier, options) {
673
+ export async function lintExportedPatch(repoDir, affectedFiles, diffContent, config, options) {
674
+ const { patchQueueCtx, ignoreChecks, patchTier } = options ?? {};
707
675
  const newFiles = detectNewFilesInDiff(diffContent);
708
676
  const { textLines: lineCount } = countNonBinaryDiffLines(diffContent);
709
677
  const patchOwnedFiles = resolvePatchOwnedSysMjs(newFiles, patchQueueCtx);
@@ -6,7 +6,7 @@ export interface PatchDirectoryLockOptions {
6
6
  * Wait up to this many seconds for the patch directory lock instead of the
7
7
  * default ~30 s budget. Enables exponential poll backoff (100 ms → 2 s)
8
8
  * and a holder-identified progress line roughly every 5 s. `undefined`
9
- * preserves the historical behavior exactly.
9
+ * keeps the default.
10
10
  */
11
11
  waitLockSeconds?: number | undefined;
12
12
  /** Command name recorded in the lock's owner metadata for holder diagnostics. */
@@ -3,7 +3,7 @@
3
3
  * Filesystem-based lock for serializing patch directory mutations.
4
4
  */
5
5
  import { join } from 'node:path';
6
- import { FireForgeError } from '../errors/base.js';
6
+ import { CommandError, FireForgeError } from '../errors/base.js';
7
7
  import { PatchError } from '../errors/patch.js';
8
8
  import { assert } from '../utils/assert.js';
9
9
  import { toError } from '../utils/errors.js';
@@ -56,7 +56,24 @@ export async function assertPatchDirectoryLockHeld(patchesDir, context) {
56
56
  export async function withPatchDirectoryLock(patchesDir, operation, options = {}) {
57
57
  const lockDir = join(patchesDir, PATCH_DIRECTORY_LOCK);
58
58
  const { waitLockSeconds, command } = options;
59
- return withFileLock(lockDir, operation, {
59
+ // Tag whatever the BODY throws so the catch below can tell it apart from a
60
+ // lock-acquire/release failure. Narrowing by origin rather than by error
61
+ // shape is what "the rewrap is for lock I/O" actually means: an errno test
62
+ // would still reclassify an EACCES raised while the body writes a .patch
63
+ // file, and a class test cannot see a bare `new Error` from a body at all.
64
+ let bodyFailure;
65
+ let bodyThrew = false;
66
+ const trackedOperation = async () => {
67
+ try {
68
+ return await operation();
69
+ }
70
+ catch (error) {
71
+ bodyThrew = true;
72
+ bodyFailure = error;
73
+ throw error;
74
+ }
75
+ };
76
+ return withFileLock(lockDir, trackedOperation, {
60
77
  ...(waitLockSeconds !== undefined
61
78
  ? {
62
79
  timeoutMs: waitLockSeconds * 1000,
@@ -71,12 +88,12 @@ export async function withPatchDirectoryLock(patchesDir, operation, options = {}
71
88
  ...(command !== undefined
72
89
  ? { ownerMetadata: [`command=${command}`, `started=${new Date().toISOString()}`] }
73
90
  : {}),
74
- // Reason first, remedy second. The remedy is waiting, not
75
- // deleting: bulk exports/re-exports legitimately hold this lock for
76
- // minutes, FireForge reaps genuinely stale locks on its own, and the
77
- // old advice ("rm -rf" while a holder was alive) destroyed a live
78
- // lock. The leading sentence is a message contract; extend, don't
79
- // reword. withFileLock appends the holder identification.
91
+ // Reason first, remedy second. The remedy is waiting, not deleting: bulk
92
+ // exports/re-exports legitimately hold this lock for minutes, and
93
+ // FireForge reaps genuinely stale locks on its own — advice to `rm -rf`
94
+ // while a holder is alive destroys a live lock. The leading sentence is
95
+ // a message contract; extend, do not reword. withFileLock appends the
96
+ // holder identification.
80
97
  onTimeoutMessage: `Timed out waiting for another FireForge command mutating ${patchesDir}.\n` +
81
98
  `Pass --wait-lock [seconds] to wait longer (bare --wait-lock waits up to 60 seconds). ` +
82
99
  `FireForge removes genuinely stale locks automatically; only if no other fireforge ` +
@@ -84,9 +101,28 @@ export async function withPatchDirectoryLock(patchesDir, operation, options = {}
84
101
  onStaleLockMessage: (ageMs) => `Removing stale patch lock (age: ${Math.round(ageMs / 1000)}s). ` +
85
102
  'A previous fireforge process may have crashed.',
86
103
  }).catch((error) => {
104
+ // The body's own failures are never ours to reclassify. Rewrapping them
105
+ // reports an EACCES writing patches.json, a postcondition failure from
106
+ // renumberPatchesInManifest, or a plain TypeError from a FireForge bug
107
+ // as a "Patch Error" carrying three fixed remedies about Firefox-version
108
+ // compatibility and `fireforge reset` — none of which apply. Worse,
109
+ // src/cli.ts prints a stack only for non-FireForgeError throwables, so
110
+ // the rewrap moves real bugs into the branch that discards their stack.
111
+ if (bodyThrew && error === bodyFailure) {
112
+ throw error;
113
+ }
87
114
  if (error instanceof FireForgeError) {
88
115
  throw error;
89
116
  }
90
- throw new PatchError(toError(error).message);
117
+ // CommandError is the one class in src/errors/ that does NOT extend
118
+ // FireForgeError: it is the sentinel carrying an already-rendered exit
119
+ // code to the entrypoint. The `instanceof FireForgeError` guard above
120
+ // misses it, so without this the exit code was replaced by PATCH_ERROR.
121
+ if (error instanceof CommandError) {
122
+ throw error;
123
+ }
124
+ // What remains is a genuine failure to acquire or release the lock.
125
+ const cause = toError(error);
126
+ throw new PatchError(cause.message, undefined, cause);
91
127
  });
92
128
  }
@@ -2,6 +2,7 @@
2
2
  * Manifest consistency checks and rebuild/recovery operations.
3
3
  */
4
4
  import type { PatchesManifest } from '../types/commands/index.js';
5
+ import type { PatchDirectoryLockOptions } from './patch-lock.js';
5
6
  /** Consistency issue codes for manifest validation. */
6
7
  export interface PatchManifestConsistencyIssue {
7
8
  code: 'manifest-invalid' | 'manifest-missing' | 'missing-patch-file' | 'untracked-patch-file' | 'files-affected-mismatch' | 'duplicate-manifest-entry';
@@ -14,6 +15,16 @@ export interface PatchManifestConsistencyIssue {
14
15
  * @returns Consistency issues between manifest metadata and on-disk patch files
15
16
  */
16
17
  export declare function validatePatchesManifestConsistency(patchesDir: string): Promise<PatchManifestConsistencyIssue[]>;
18
+ /**
19
+ * Recovery hint for an unrepaired consistency failure.
20
+ *
21
+ * Drift that is only in `filesAffected` is a derived value disagreeing with
22
+ * the diff it describes, and the narrow repair fixes exactly that. Naming the
23
+ * whole-manifest rebuild for it — as this hint used to, unconditionally — puts
24
+ * an operator one keystroke from rewriting every row in the manifest to
25
+ * correct one list.
26
+ */
27
+ export declare function recommendManifestRepair(issues: readonly PatchManifestConsistencyIssue[]): string;
17
28
  /**
18
29
  * Summary of a {@link rebuildPatchesManifest} run. `recoveredFilenames`
19
30
  * lists patches whose manifest entry was reconstructed from filename,
@@ -24,21 +35,61 @@ export declare function validatePatchesManifestConsistency(patchesDir: string):
24
35
  * invented vs which was restored.
25
36
  */
26
37
  export interface RebuildPatchesManifestResult {
27
- /** Rebuilt manifest, ready to be re-applied by callers (already persisted). */
38
+ /**
39
+ * Rebuilt manifest, ready to be re-applied by callers. Persisted unless
40
+ * the run was a dry run — see {@link RebuildPatchesManifestResult.written}.
41
+ */
28
42
  manifest: PatchesManifest;
29
43
  /**
30
44
  * Filenames whose manifest entry had no pre-existing metadata to
31
45
  * preserve — every descriptive field on these entries is inferred.
32
46
  */
33
47
  recoveredFilenames: string[];
48
+ /**
49
+ * Filenames that had a manifest entry but no patch file on disk, so the
50
+ * row was dropped. Discarding these silently is how a manifest quietly
51
+ * loses rows an operator still expected to be there.
52
+ */
53
+ droppedFilenames: string[];
54
+ /** False when the run was a dry run and patches.json was left untouched. */
55
+ written: boolean;
56
+ }
57
+ /** Options for {@link rebuildPatchesManifest}. */
58
+ export interface RebuildPatchesManifestOptions extends PatchDirectoryLockOptions {
59
+ /**
60
+ * Proceed even when patches.json exists but cannot be parsed, in which case
61
+ * there is no existing metadata to preserve and every descriptive field on
62
+ * every entry is invented. Refused without this flag — see
63
+ * {@link rebuildPatchesManifest}.
64
+ */
65
+ allowMetadataLoss?: boolean;
66
+ /** Compute the rebuilt manifest without writing it. */
67
+ dryRun?: boolean;
34
68
  }
35
69
  /**
36
70
  * Rebuilds patches.json from the patch files currently present on disk.
37
- * Existing metadata is preserved when possible; missing entries are recovered
38
- * from filename structure, patch contents, and file mtimes.
71
+ *
72
+ * This is a MERGE, not a from-scratch rebuild: only `filesAffected` and
73
+ * `order` are recomputed from the patch files, and every other field on an
74
+ * existing entry is carried forward untouched. Entries with no pre-existing
75
+ * metadata are recovered from filename structure, patch contents, and file
76
+ * mtimes.
77
+ *
78
+ * Refuses when patches.json exists but cannot be parsed, because that is the
79
+ * one case where nothing can be carried forward and the "merge" silently
80
+ * becomes a whole-manifest reinvention. Pass `allowMetadataLoss` to accept it.
81
+ *
39
82
  * @param patchesDir - Path to the patches directory
40
83
  * @param fallbackSourceEsrVersion - source version to use for recovered legacy entries
41
- * @returns {@link RebuildPatchesManifestResult} — the persisted manifest
42
- * plus the filenames that were reconstructed from generic defaults.
84
+ * @param options - Lock options plus `allowMetadataLoss` / `dryRun`
85
+ * @returns {@link RebuildPatchesManifestResult} — the rebuilt manifest, the
86
+ * filenames that were reconstructed from generic defaults, and the rows
87
+ * dropped because their patch file is gone.
88
+ */
89
+ export declare function rebuildPatchesManifest(patchesDir: string, fallbackSourceEsrVersion: string, options?: RebuildPatchesManifestOptions): Promise<RebuildPatchesManifestResult>;
90
+ /**
91
+ * Canonical form of a `filesAffected` list: deduplicated and sorted, so a
92
+ * comparison between a declared list and one extracted from a patch body is
93
+ * about membership rather than ordering.
43
94
  */
44
- export declare function rebuildPatchesManifest(patchesDir: string, fallbackSourceEsrVersion: string): Promise<RebuildPatchesManifestResult>;
95
+ export declare function normalizeAffectedFiles(files: string[]): string[];