@hominis/fireforge 0.43.1 → 0.44.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 (470) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +10 -1
  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 +4 -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-post-rebase-audit.d.ts +2 -0
  22. package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
  23. package/dist/src/commands/doctor-working-tree.d.ts +10 -12
  24. package/dist/src/commands/doctor-working-tree.js +11 -18
  25. package/dist/src/commands/doctor.js +35 -53
  26. package/dist/src/commands/download.js +32 -48
  27. package/dist/src/commands/export-all.js +29 -33
  28. package/dist/src/commands/export-flow.js +6 -6
  29. package/dist/src/commands/export-placement-conflicts.js +10 -9
  30. package/dist/src/commands/export-placement-gate.js +5 -5
  31. package/dist/src/commands/export-shared.d.ts +30 -33
  32. package/dist/src/commands/export-shared.js +45 -43
  33. package/dist/src/commands/export.js +21 -27
  34. package/dist/src/commands/furnace/apply.js +14 -23
  35. package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
  36. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
  37. package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
  38. package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
  39. package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
  40. package/dist/src/commands/furnace/chrome-doc.js +31 -41
  41. package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
  42. package/dist/src/commands/furnace/create-browser-test.js +8 -8
  43. package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
  44. package/dist/src/commands/furnace/create-mochikit.js +8 -9
  45. package/dist/src/commands/furnace/create-readback.d.ts +12 -13
  46. package/dist/src/commands/furnace/create-readback.js +12 -13
  47. package/dist/src/commands/furnace/create-templates.d.ts +22 -28
  48. package/dist/src/commands/furnace/create-templates.js +35 -30
  49. package/dist/src/commands/furnace/create-xpcshell.js +2 -2
  50. package/dist/src/commands/furnace/create.js +33 -23
  51. package/dist/src/commands/furnace/deploy.js +16 -24
  52. package/dist/src/commands/furnace/diff.js +9 -9
  53. package/dist/src/commands/furnace/init.js +33 -36
  54. package/dist/src/commands/furnace/override.js +23 -25
  55. package/dist/src/commands/furnace/preview.js +47 -67
  56. package/dist/src/commands/furnace/refresh.js +12 -16
  57. package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
  58. package/dist/src/commands/furnace/remove-mutations.js +9 -16
  59. package/dist/src/commands/furnace/remove-state.js +1 -1
  60. package/dist/src/commands/furnace/remove.js +33 -38
  61. package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
  62. package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
  63. package/dist/src/commands/furnace/rename.js +59 -69
  64. package/dist/src/commands/furnace/scan.js +19 -33
  65. package/dist/src/commands/furnace/status.js +12 -9
  66. package/dist/src/commands/furnace/sync.js +9 -16
  67. package/dist/src/commands/furnace/validate.js +20 -20
  68. package/dist/src/commands/import.js +42 -35
  69. package/dist/src/commands/lint-per-patch.js +15 -10
  70. package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
  71. package/dist/src/commands/lint-per-run-checkjs.js +13 -13
  72. package/dist/src/commands/lint-report.js +6 -7
  73. package/dist/src/commands/lint.d.ts +15 -16
  74. package/dist/src/commands/lint.js +86 -102
  75. package/dist/src/commands/package.js +6 -11
  76. package/dist/src/commands/patch/compact.js +1 -1
  77. package/dist/src/commands/patch/delete.js +24 -33
  78. package/dist/src/commands/patch/index.d.ts +5 -6
  79. package/dist/src/commands/patch/index.js +10 -12
  80. package/dist/src/commands/patch/lint-ignore.js +14 -13
  81. package/dist/src/commands/patch/move-files-create.d.ts +6 -6
  82. package/dist/src/commands/patch/move-files-create.js +8 -8
  83. package/dist/src/commands/patch/move-files-into.d.ts +6 -6
  84. package/dist/src/commands/patch/move-files-into.js +8 -8
  85. package/dist/src/commands/patch/patch-context.d.ts +2 -3
  86. package/dist/src/commands/patch/patch-context.js +2 -3
  87. package/dist/src/commands/patch/rename.d.ts +10 -11
  88. package/dist/src/commands/patch/rename.js +11 -12
  89. package/dist/src/commands/patch/reorder.js +8 -9
  90. package/dist/src/commands/patch/split-plan.js +1 -1
  91. package/dist/src/commands/patch/split.d.ts +8 -10
  92. package/dist/src/commands/patch/split.js +9 -11
  93. package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
  94. package/dist/src/commands/patch/staged-dependency.js +7 -3
  95. package/dist/src/commands/patch/tier.d.ts +9 -9
  96. package/dist/src/commands/patch/tier.js +18 -15
  97. package/dist/src/commands/re-export-adjacent.js +9 -8
  98. package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
  99. package/dist/src/commands/re-export-bulk-scan.js +22 -21
  100. package/dist/src/commands/re-export-drift.js +10 -10
  101. package/dist/src/commands/re-export-files.d.ts +6 -7
  102. package/dist/src/commands/re-export-files.js +34 -44
  103. package/dist/src/commands/re-export-lint.d.ts +10 -13
  104. package/dist/src/commands/re-export-lint.js +16 -14
  105. package/dist/src/commands/re-export-options.d.ts +6 -6
  106. package/dist/src/commands/re-export-options.js +6 -6
  107. package/dist/src/commands/re-export-register.d.ts +4 -10
  108. package/dist/src/commands/re-export-register.js +5 -11
  109. package/dist/src/commands/re-export-scan.js +1 -1
  110. package/dist/src/commands/re-export-single.d.ts +3 -3
  111. package/dist/src/commands/re-export-single.js +33 -25
  112. package/dist/src/commands/re-export.js +13 -21
  113. package/dist/src/commands/rebase/abort.js +3 -3
  114. package/dist/src/commands/rebase/confirm.js +2 -1
  115. package/dist/src/commands/rebase/continue.d.ts +1 -1
  116. package/dist/src/commands/rebase/continue.js +14 -9
  117. package/dist/src/commands/rebase/index.js +11 -11
  118. package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
  119. package/dist/src/commands/rebase/patch-loop.js +60 -39
  120. package/dist/src/commands/rebase/summary.js +1 -1
  121. package/dist/src/commands/register.js +5 -7
  122. package/dist/src/commands/reset.js +2 -1
  123. package/dist/src/commands/resolve.d.ts +16 -13
  124. package/dist/src/commands/resolve.js +30 -33
  125. package/dist/src/commands/run.js +33 -35
  126. package/dist/src/commands/setup-support.js +15 -19
  127. package/dist/src/commands/setup.js +13 -9
  128. package/dist/src/commands/status-check.d.ts +6 -6
  129. package/dist/src/commands/status-json.d.ts +6 -6
  130. package/dist/src/commands/status-json.js +5 -0
  131. package/dist/src/commands/status-output.d.ts +5 -5
  132. package/dist/src/commands/status-output.js +6 -6
  133. package/dist/src/commands/status-ownership.d.ts +16 -14
  134. package/dist/src/commands/status-ownership.js +16 -14
  135. package/dist/src/commands/status.js +42 -34
  136. package/dist/src/commands/test-build-phase.js +26 -13
  137. package/dist/src/commands/test-diagnose.js +44 -44
  138. package/dist/src/commands/test-register.js +1 -0
  139. package/dist/src/commands/test-run.d.ts +14 -16
  140. package/dist/src/commands/test-run.js +21 -13
  141. package/dist/src/commands/test-stale-gate.d.ts +15 -16
  142. package/dist/src/commands/test-stale-gate.js +15 -16
  143. package/dist/src/commands/test-verdict.d.ts +20 -3
  144. package/dist/src/commands/test-verdict.js +30 -0
  145. package/dist/src/commands/test.js +83 -76
  146. package/dist/src/commands/token-coverage.js +3 -4
  147. package/dist/src/commands/token.js +16 -18
  148. package/dist/src/commands/tree.js +47 -31
  149. package/dist/src/commands/typecheck.d.ts +10 -12
  150. package/dist/src/commands/typecheck.js +24 -27
  151. package/dist/src/commands/verify.js +13 -15
  152. package/dist/src/commands/watch.js +21 -23
  153. package/dist/src/commands/wire.js +52 -62
  154. package/dist/src/core/ast-utils.d.ts +8 -7
  155. package/dist/src/core/ast-utils.js +8 -7
  156. package/dist/src/core/branding.d.ts +9 -14
  157. package/dist/src/core/branding.js +7 -11
  158. package/dist/src/core/browser-wire.d.ts +1 -1
  159. package/dist/src/core/browser-wire.js +14 -19
  160. package/dist/src/core/build-audit-platform.js +25 -33
  161. package/dist/src/core/build-audit-registration.js +17 -25
  162. package/dist/src/core/build-audit-resolve.d.ts +15 -17
  163. package/dist/src/core/build-audit-resolve.js +27 -33
  164. package/dist/src/core/build-audit-transforms.js +18 -19
  165. package/dist/src/core/build-audit.js +28 -42
  166. package/dist/src/core/build-baseline-types.d.ts +29 -35
  167. package/dist/src/core/build-baseline.d.ts +5 -4
  168. package/dist/src/core/build-baseline.js +14 -14
  169. package/dist/src/core/build-overwrite-guard.d.ts +7 -14
  170. package/dist/src/core/build-overwrite-guard.js +17 -27
  171. package/dist/src/core/build-prepare.d.ts +2 -2
  172. package/dist/src/core/build-prepare.js +8 -9
  173. package/dist/src/core/config-state.js +12 -4
  174. package/dist/src/core/config-validate.js +8 -5
  175. package/dist/src/core/config.d.ts +5 -8
  176. package/dist/src/core/config.js +7 -16
  177. package/dist/src/core/coverage-extend.d.ts +15 -13
  178. package/dist/src/core/coverage-extend.js +15 -13
  179. package/dist/src/core/destructive.d.ts +31 -9
  180. package/dist/src/core/destructive.js +29 -26
  181. package/dist/src/core/diff-hunks.js +10 -12
  182. package/dist/src/core/discard-baseline.d.ts +8 -8
  183. package/dist/src/core/discard-baseline.js +16 -15
  184. package/dist/src/core/display-state.d.ts +12 -14
  185. package/dist/src/core/display-state.js +12 -14
  186. package/dist/src/core/engine-changes.d.ts +2 -4
  187. package/dist/src/core/engine-changes.js +2 -4
  188. package/dist/src/core/engine-precondition.d.ts +28 -13
  189. package/dist/src/core/engine-precondition.js +25 -10
  190. package/dist/src/core/engine-session-lock.d.ts +47 -21
  191. package/dist/src/core/engine-session-lock.js +168 -33
  192. package/dist/src/core/export-moved-code-shape.d.ts +10 -10
  193. package/dist/src/core/export-moved-code-shape.js +10 -10
  194. package/dist/src/core/file-lock.d.ts +62 -5
  195. package/dist/src/core/file-lock.js +196 -59
  196. package/dist/src/core/firefox-cache.js +1 -3
  197. package/dist/src/core/firefox-extract.js +10 -14
  198. package/dist/src/core/firefox.d.ts +7 -5
  199. package/dist/src/core/firefox.js +4 -5
  200. package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
  201. package/dist/src/core/furnace-apply-dry-run.js +3 -2
  202. package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
  203. package/dist/src/core/furnace-apply-ftl.js +12 -12
  204. package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
  205. package/dist/src/core/furnace-apply-helpers.js +19 -16
  206. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
  207. package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
  208. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
  209. package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
  210. package/dist/src/core/furnace-apply.d.ts +5 -8
  211. package/dist/src/core/furnace-apply.js +19 -24
  212. package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
  213. package/dist/src/core/furnace-checksum-utils.js +16 -0
  214. package/dist/src/core/furnace-config.d.ts +21 -30
  215. package/dist/src/core/furnace-config.js +32 -54
  216. package/dist/src/core/furnace-constants.d.ts +8 -0
  217. package/dist/src/core/furnace-constants.js +10 -2
  218. package/dist/src/core/furnace-css-fragments.d.ts +5 -5
  219. package/dist/src/core/furnace-css-fragments.js +5 -5
  220. package/dist/src/core/furnace-jsconfig.d.ts +11 -12
  221. package/dist/src/core/furnace-jsconfig.js +11 -12
  222. package/dist/src/core/furnace-marker.d.ts +7 -8
  223. package/dist/src/core/furnace-marker.js +7 -8
  224. package/dist/src/core/furnace-operation.d.ts +31 -0
  225. package/dist/src/core/furnace-operation.js +54 -17
  226. package/dist/src/core/furnace-precondition.d.ts +61 -0
  227. package/dist/src/core/furnace-precondition.js +67 -0
  228. package/dist/src/core/furnace-registration-remove.d.ts +5 -6
  229. package/dist/src/core/furnace-registration-remove.js +6 -7
  230. package/dist/src/core/furnace-registration-validate.d.ts +13 -0
  231. package/dist/src/core/furnace-registration-validate.js +20 -1
  232. package/dist/src/core/furnace-registration.d.ts +5 -6
  233. package/dist/src/core/furnace-registration.js +10 -12
  234. package/dist/src/core/furnace-rollback.js +7 -7
  235. package/dist/src/core/furnace-scanner.d.ts +1 -0
  236. package/dist/src/core/furnace-scanner.js +1 -2
  237. package/dist/src/core/furnace-stale-export.d.ts +10 -10
  238. package/dist/src/core/furnace-stale-export.js +10 -10
  239. package/dist/src/core/furnace-state-persist.d.ts +6 -8
  240. package/dist/src/core/furnace-state-persist.js +6 -8
  241. package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
  242. package/dist/src/core/furnace-validate-accessibility.js +310 -1
  243. package/dist/src/core/furnace-validate-compatibility.js +141 -1
  244. package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
  245. package/dist/src/core/furnace-validate-helpers.js +0 -491
  246. package/dist/src/core/furnace-validate-registration.d.ts +7 -0
  247. package/dist/src/core/furnace-validate-registration.js +115 -41
  248. package/dist/src/core/furnace-validate.d.ts +16 -0
  249. package/dist/src/core/furnace-validate.js +188 -145
  250. package/dist/src/core/furnace-version-drift.d.ts +10 -11
  251. package/dist/src/core/furnace-version-drift.js +10 -11
  252. package/dist/src/core/git-base.d.ts +7 -9
  253. package/dist/src/core/git-base.js +7 -9
  254. package/dist/src/core/git-diff.d.ts +20 -9
  255. package/dist/src/core/git-diff.js +148 -63
  256. package/dist/src/core/git-file-ops.d.ts +19 -19
  257. package/dist/src/core/git-file-ops.js +19 -19
  258. package/dist/src/core/git-readonly-index.d.ts +53 -19
  259. package/dist/src/core/git-readonly-index.js +75 -31
  260. package/dist/src/core/git-status.d.ts +5 -6
  261. package/dist/src/core/git-status.js +5 -6
  262. package/dist/src/core/git.d.ts +9 -10
  263. package/dist/src/core/git.js +54 -70
  264. package/dist/src/core/license-headers.d.ts +17 -23
  265. package/dist/src/core/license-headers.js +17 -23
  266. package/dist/src/core/mach-build-artifacts.d.ts +9 -9
  267. package/dist/src/core/mach-build-artifacts.js +57 -19
  268. package/dist/src/core/mach-error-hints.js +42 -29
  269. package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
  270. package/dist/src/core/mach-known-noise-filter.js +18 -20
  271. package/dist/src/core/mach-mozconfig.d.ts +14 -0
  272. package/dist/src/core/mach-mozconfig.js +30 -0
  273. package/dist/src/core/mach-resource-shim.d.ts +43 -58
  274. package/dist/src/core/mach-resource-shim.js +50 -67
  275. package/dist/src/core/mach.d.ts +37 -41
  276. package/dist/src/core/mach.js +50 -51
  277. package/dist/src/core/marionette-port.d.ts +36 -1
  278. package/dist/src/core/marionette-port.js +96 -31
  279. package/dist/src/core/marionette-preflight.d.ts +8 -12
  280. package/dist/src/core/marionette-preflight.js +86 -52
  281. package/dist/src/core/mochitest-server-port.d.ts +42 -0
  282. package/dist/src/core/mochitest-server-port.js +119 -0
  283. package/dist/src/core/module-resolution-preflight.d.ts +9 -11
  284. package/dist/src/core/module-resolution-preflight.js +9 -11
  285. package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
  286. package/dist/src/core/moz-manifest-helpers.js +163 -0
  287. package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
  288. package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
  289. package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
  290. package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
  291. package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
  292. package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
  293. package/dist/src/core/ownership-table.d.ts +14 -16
  294. package/dist/src/core/ownership-table.js +14 -16
  295. package/dist/src/core/parser-fallback.d.ts +15 -1
  296. package/dist/src/core/parser-fallback.js +22 -3
  297. package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
  298. package/dist/src/core/patch-apply-fuzz.js +15 -18
  299. package/dist/src/core/patch-apply.d.ts +13 -34
  300. package/dist/src/core/patch-apply.js +16 -76
  301. package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
  302. package/dist/src/core/patch-artifact-normalize.js +13 -15
  303. package/dist/src/core/patch-export-metadata.d.ts +12 -2
  304. package/dist/src/core/patch-export-metadata.js +13 -4
  305. package/dist/src/core/patch-export-update.d.ts +13 -1
  306. package/dist/src/core/patch-export-update.js +25 -8
  307. package/dist/src/core/patch-export.d.ts +15 -8
  308. package/dist/src/core/patch-export.js +10 -11
  309. package/dist/src/core/patch-files.d.ts +5 -6
  310. package/dist/src/core/patch-files.js +5 -6
  311. package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
  312. package/dist/src/core/patch-identifier-suggest.js +14 -19
  313. package/dist/src/core/patch-lint-binary.d.ts +51 -0
  314. package/dist/src/core/patch-lint-binary.js +39 -0
  315. package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
  316. package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
  317. package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
  318. package/dist/src/core/patch-lint-checkjs.js +16 -16
  319. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
  320. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
  321. package/dist/src/core/patch-lint-creators.d.ts +8 -8
  322. package/dist/src/core/patch-lint-creators.js +8 -8
  323. package/dist/src/core/patch-lint-cross.d.ts +17 -20
  324. package/dist/src/core/patch-lint-cross.js +16 -16
  325. package/dist/src/core/patch-lint-css.d.ts +3 -5
  326. package/dist/src/core/patch-lint-css.js +17 -22
  327. package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
  328. package/dist/src/core/patch-lint-diff-tag.js +14 -16
  329. package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
  330. package/dist/src/core/patch-lint-jsdoc.js +26 -19
  331. package/dist/src/core/patch-lint-mozbuild.js +20 -20
  332. package/dist/src/core/patch-lint-observer.d.ts +7 -7
  333. package/dist/src/core/patch-lint-observer.js +7 -7
  334. package/dist/src/core/patch-lint.d.ts +38 -29
  335. package/dist/src/core/patch-lint.js +89 -121
  336. package/dist/src/core/patch-lock.d.ts +1 -1
  337. package/dist/src/core/patch-lock.js +45 -9
  338. package/dist/src/core/patch-manifest-consistency.d.ts +2 -1
  339. package/dist/src/core/patch-manifest-consistency.js +25 -27
  340. package/dist/src/core/patch-manifest-io.js +14 -18
  341. package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
  342. package/dist/src/core/patch-manifest-resolve.js +9 -12
  343. package/dist/src/core/patch-manifest-validate.d.ts +6 -1
  344. package/dist/src/core/patch-manifest-validate.js +25 -13
  345. package/dist/src/core/patch-parse.d.ts +24 -13
  346. package/dist/src/core/patch-parse.js +19 -14
  347. package/dist/src/core/patch-policy.d.ts +2 -2
  348. package/dist/src/core/patch-policy.js +2 -2
  349. package/dist/src/core/patch-registration-refs.d.ts +7 -8
  350. package/dist/src/core/patch-registration-refs.js +9 -9
  351. package/dist/src/core/process-cpu-time.d.ts +12 -0
  352. package/dist/src/core/process-cpu-time.js +52 -0
  353. package/dist/src/core/rebase-session.d.ts +38 -26
  354. package/dist/src/core/rebase-session.js +55 -28
  355. package/dist/src/core/register-browser-content.js +9 -74
  356. package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
  357. package/dist/src/core/register-module.js +10 -43
  358. package/dist/src/core/register-scaffold.d.ts +8 -8
  359. package/dist/src/core/register-scaffold.js +10 -10
  360. package/dist/src/core/register-shared-css.d.ts +9 -12
  361. package/dist/src/core/register-shared-css.js +17 -87
  362. package/dist/src/core/register-xpcshell-test.d.ts +6 -7
  363. package/dist/src/core/register-xpcshell-test.js +7 -8
  364. package/dist/src/core/shared-ftl.js +12 -4
  365. package/dist/src/core/state-file.d.ts +45 -0
  366. package/dist/src/core/state-file.js +34 -0
  367. package/dist/src/core/status-binary.d.ts +18 -11
  368. package/dist/src/core/status-binary.js +19 -12
  369. package/dist/src/core/status-classify.d.ts +30 -36
  370. package/dist/src/core/status-classify.js +21 -26
  371. package/dist/src/core/test-changed-prefs.d.ts +30 -0
  372. package/dist/src/core/test-changed-prefs.js +78 -0
  373. package/dist/src/core/test-harness-crash.d.ts +25 -63
  374. package/dist/src/core/test-harness-crash.js +121 -142
  375. package/dist/src/core/test-harness-signature.d.ts +18 -0
  376. package/dist/src/core/test-harness-signature.js +13 -0
  377. package/dist/src/core/test-path-scope.d.ts +12 -15
  378. package/dist/src/core/test-path-scope.js +15 -18
  379. package/dist/src/core/test-stale-check.d.ts +17 -17
  380. package/dist/src/core/test-stale-check.js +50 -43
  381. package/dist/src/core/test-stall-triage.d.ts +51 -0
  382. package/dist/src/core/test-stall-triage.js +126 -0
  383. package/dist/src/core/token-category.js +5 -7
  384. package/dist/src/core/token-coverage.js +9 -9
  385. package/dist/src/core/token-dark-mode.d.ts +9 -11
  386. package/dist/src/core/token-dark-mode.js +9 -11
  387. package/dist/src/core/token-docs.d.ts +2 -0
  388. package/dist/src/core/token-docs.js +2 -0
  389. package/dist/src/core/token-manager.d.ts +10 -2
  390. package/dist/src/core/token-manager.js +17 -8
  391. package/dist/src/core/token-scaffold.d.ts +7 -7
  392. package/dist/src/core/token-scaffold.js +7 -7
  393. package/dist/src/core/token-variant.d.ts +4 -4
  394. package/dist/src/core/token-variant.js +4 -4
  395. package/dist/src/core/toolchain-preflight.d.ts +20 -21
  396. package/dist/src/core/toolchain-preflight.js +28 -31
  397. package/dist/src/core/tree-guard.js +12 -2
  398. package/dist/src/core/tree-store.d.ts +41 -27
  399. package/dist/src/core/tree-store.js +63 -42
  400. package/dist/src/core/typecheck-shim.d.ts +23 -24
  401. package/dist/src/core/typecheck-shim.js +23 -24
  402. package/dist/src/core/typecheck.js +8 -8
  403. package/dist/src/core/wire-destroy.d.ts +3 -3
  404. package/dist/src/core/wire-destroy.js +7 -15
  405. package/dist/src/core/wire-dom-fragment.d.ts +9 -17
  406. package/dist/src/core/wire-dom-fragment.js +11 -44
  407. package/dist/src/core/wire-init.d.ts +2 -3
  408. package/dist/src/core/wire-init.js +8 -19
  409. package/dist/src/core/wire-subscript.d.ts +2 -3
  410. package/dist/src/core/wire-subscript.js +4 -13
  411. package/dist/src/core/wire-utils.d.ts +7 -8
  412. package/dist/src/core/wire-utils.js +7 -8
  413. package/dist/src/core/xpcshell-appdir.d.ts +38 -45
  414. package/dist/src/core/xpcshell-appdir.js +38 -45
  415. package/dist/src/errors/base.d.ts +37 -16
  416. package/dist/src/errors/base.js +37 -16
  417. package/dist/src/errors/build.d.ts +13 -1
  418. package/dist/src/errors/build.js +14 -2
  419. package/dist/src/errors/codes.d.ts +16 -0
  420. package/dist/src/errors/codes.js +16 -0
  421. package/dist/src/errors/furnace.d.ts +4 -4
  422. package/dist/src/errors/git.d.ts +6 -10
  423. package/dist/src/errors/git.js +6 -10
  424. package/dist/src/errors/patch.d.ts +7 -7
  425. package/dist/src/errors/patch.js +7 -7
  426. package/dist/src/errors/rebase.d.ts +4 -4
  427. package/dist/src/errors/rebase.js +4 -4
  428. package/dist/src/index.d.ts +7 -1
  429. package/dist/src/index.js +5 -0
  430. package/dist/src/types/commands/index.d.ts +2 -2
  431. package/dist/src/types/commands/options.d.ts +88 -70
  432. package/dist/src/types/commands/patches.d.ts +24 -34
  433. package/dist/src/types/commands/project.d.ts +5 -34
  434. package/dist/src/types/config.d.ts +25 -23
  435. package/dist/src/types/config.js +6 -1
  436. package/dist/src/types/furnace.d.ts +37 -13
  437. package/dist/src/types/furnace.js +19 -1
  438. package/dist/src/types/index.d.ts +3 -3
  439. package/dist/src/utils/build-info.d.ts +7 -8
  440. package/dist/src/utils/build-info.js +12 -11
  441. package/dist/src/utils/errors.d.ts +6 -8
  442. package/dist/src/utils/errors.js +6 -8
  443. package/dist/src/utils/logger.d.ts +27 -10
  444. package/dist/src/utils/logger.js +28 -16
  445. package/dist/src/utils/machine-output.d.ts +12 -0
  446. package/dist/src/utils/machine-output.js +33 -0
  447. package/dist/src/utils/options.d.ts +31 -14
  448. package/dist/src/utils/options.js +86 -30
  449. package/dist/src/utils/paths.d.ts +10 -8
  450. package/dist/src/utils/paths.js +10 -8
  451. package/dist/src/utils/platform.d.ts +0 -25
  452. package/dist/src/utils/platform.js +1 -42
  453. package/dist/src/utils/process-group.d.ts +9 -4
  454. package/dist/src/utils/process-group.js +19 -5
  455. package/dist/src/utils/process.d.ts +42 -7
  456. package/dist/src/utils/process.js +44 -17
  457. package/dist/src/utils/ps-duration.d.ts +31 -0
  458. package/dist/src/utils/ps-duration.js +70 -0
  459. package/dist/src/utils/regex.d.ts +9 -10
  460. package/dist/src/utils/regex.js +9 -10
  461. package/dist/src/utils/validation.d.ts +58 -28
  462. package/dist/src/utils/validation.js +60 -33
  463. package/package.json +2 -2
  464. package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
  465. package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
  466. package/dist/src/core/manifest-helpers.d.ts +0 -38
  467. package/dist/src/core/manifest-helpers.js +0 -120
  468. /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
  469. /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
  470. /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
@@ -2,7 +2,12 @@
2
2
  * Schema validation for patches.json manifest data.
3
3
  */
4
4
  import type { PatchCategory, PatchesManifest } from '../types/commands/index.js';
5
- /** Validates raw patches.json data and returns the typed manifest shape. */
5
+ /**
6
+ * Validates raw patches.json data and returns the typed manifest shape.
7
+ *
8
+ * @param data - Parsed patches.json content
9
+ * @returns The validated manifest
10
+ */
6
11
  export declare function validatePatchesManifest(data: unknown): PatchesManifest;
7
12
  /**
8
13
  * Infers patch metadata from a filename pattern.
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { parseObject } from '../utils/parse.js';
6
6
  import { isArray, isObject, isValidFirefoxProduct, isValidFirefoxVersion, PATCH_CATEGORIES, } from '../utils/validation.js';
7
+ import { checkDocumentVersion, describeNewerDocument } from './state-file.js';
7
8
  function parseForwardImports(data, label) {
8
9
  if (!isArray(data)) {
9
10
  throw new Error(`${label} must be an array`);
@@ -91,16 +92,13 @@ function validatePatchMetadata(data, index) {
91
92
  throw new Error(`patches[${index}].sourceEsrVersion must be a valid Firefox version string`);
92
93
  }
93
94
  const filesAffected = rec.stringArray('filesAffected');
94
- // Optional fields. These were silently stripped before the 0.17.0
95
- // branding-tier work reached in and audited the loader — the 0.16.0
96
- // `lintIgnore` escape hatch demonstrably round-tripped only through
97
- // test fixtures that mocked `loadPatchesManifest` directly. Real
98
- // operator edits to `patches.json` were dropped on every subsequent
99
- // load, so any patch that relied on `lintIgnore` to suppress a
100
- // specific lint rule was quietly re-tripped the next time the
101
- // manifest validated. Preserve both the pre-existing `lintIgnore`
102
- // and the new `tier` field here so future-added optional fields
103
- // have a ready template to follow.
95
+ // Optional fields. These are easy to strip silently — an escape hatch
96
+ // that round-trips only through test fixtures mocking
97
+ // `loadPatchesManifest` looks fine while real operator edits to
98
+ // `patches.json` are dropped on every load, so a patch relying on
99
+ // `lintIgnore` to suppress a rule is quietly re-tripped the next time the
100
+ // manifest validates. Preserve both `lintIgnore` and `tier` here; future
101
+ // optional fields follow the same template.
104
102
  const lintIgnore = rec.optionalStringArray('lintIgnore');
105
103
  const rawTier = rec.raw('tier');
106
104
  let tier;
@@ -133,13 +131,27 @@ function validatePatchMetadata(data, index) {
133
131
  }
134
132
  return result;
135
133
  }
136
- /** Validates raw patches.json data and returns the typed manifest shape. */
134
+ /** Manifest schema version this build writes and understands. */
135
+ const PATCHES_MANIFEST_VERSION = 1;
136
+ /**
137
+ * Validates raw patches.json data and returns the typed manifest shape.
138
+ *
139
+ * @param data - Parsed patches.json content
140
+ * @returns The validated manifest
141
+ */
137
142
  export function validatePatchesManifest(data) {
138
143
  if (!isObject(data)) {
139
144
  throw new Error('patches.json must be a JSON object');
140
145
  }
141
- if (data['version'] !== 1) {
142
- throw new Error('patches.json version must be 1');
146
+ // A manifest from a NEWER FireForge is not corrupt. The bare "version must
147
+ // be 1" this replaced sent the operator looking for damage in a file that
148
+ // only needed a newer build to read it.
149
+ const version = checkDocumentVersion(data, 'version', PATCHES_MANIFEST_VERSION);
150
+ if (version.kind === 'newer') {
151
+ throw new Error(describeNewerDocument('patches manifest', version.found, PATCHES_MANIFEST_VERSION));
152
+ }
153
+ if (version.kind !== 'current') {
154
+ throw new Error(`patches.json version must be ${PATCHES_MANIFEST_VERSION}`);
143
155
  }
144
156
  if (!isArray(data['patches'])) {
145
157
  throw new Error('patches.json field "patches" must be an array');
@@ -1,15 +1,13 @@
1
1
  /**
2
- * Pure parsing functions for extracting information from patch files.
3
- * All functions are synchronous and operate on string content.
2
+ * Pure parsing functions for extracting information from patch files. All
3
+ * functions are synchronous and operate on string content.
4
4
  *
5
- * This module is the ONE unified-diff walker in the codebase. Before
6
- * {@link parseDiffSections} existed, six hand-rolled `diff --git` walkers
7
- * lived across patch-parse, patch-lint-diff, patch-registration-refs, and
8
- * patch-transform — with divergent CRLF handling (a CRLF-saved patch file
9
- * silently failed target-file matching and `\ No newline` detection on
10
- * Windows), no quoted-path support, and greedy path captures that
11
- * mis-split any path containing ` b/`. New diff-shaped parsing must build
12
- * on {@link parseDiffSections} rather than re-walking lines.
5
+ * This module is the ONE unified-diff walker in the codebase. Hand-rolled
6
+ * `diff --git` walkers diverge in ways that matter: CRLF handling (a
7
+ * CRLF-saved patch file silently fails target-file matching and
8
+ * `\ No newline` detection on Windows), quoted-path support, and greedy path
9
+ * captures that mis-split any path containing ` b/`. New diff-shaped parsing
10
+ * must build on {@link parseDiffSections} rather than re-walking lines.
13
11
  */
14
12
  /**
15
13
  * Extracts the order number from a patch filename.
@@ -53,9 +51,9 @@ export interface DiffGitHeader {
53
51
  * (`diff --git "a/x y" "b/x y"`), and disambiguates the unquoted form for
54
52
  * paths that themselves contain ` b/`: for the overwhelmingly common
55
53
  * non-rename case the two paths are identical, so a symmetric split
56
- * (`a/<p> b/<p>`) is tried first — the previous greedy regex
57
- * (`a\/.+ b\/(.+)$`) split such lines at the LAST ` b/` and returned a
58
- * truncated path. Falls back to a non-greedy split for renames.
54
+ * (`a/<p> b/<p>`) is tried first — a greedy `a\/.+ b\/(.+)$` splits such
55
+ * lines at the LAST ` b/` and returns a truncated path. Falls back to a
56
+ * non-greedy split for renames.
59
57
  *
60
58
  * @returns null when the line is not a diff --git header.
61
59
  */
@@ -78,6 +76,19 @@ export interface DiffSection {
78
76
  * refuse them rather than treat base85 lines starting with `+` as content.
79
77
  */
80
78
  isBinary: boolean;
79
+ /**
80
+ * True ONLY for `GIT binary patch` sections — the body carries a
81
+ * reconstructable literal/delta payload. False for `Binary files … differ`,
82
+ * which records THAT the bytes changed but carries none of them and cannot
83
+ * be replayed by `git apply`.
84
+ *
85
+ * The distinction is not cosmetic: the stub form still carries a correct
86
+ * `index <old>..<new>` line, so identity checks keyed on {@link indexNewHash}
87
+ * alone happily conclude "the recorded bytes match the live file" for a body
88
+ * that can no longer produce those bytes. Consumers deciding whether a patch
89
+ * can RECREATE a file must read this flag, never `isBinary` plus a hash.
90
+ */
91
+ hasBinaryDelta: boolean;
81
92
  /**
82
93
  * Old-side blob hash from the section's `index <old>..<new>` line
83
94
  * (possibly abbreviated). Undefined when the metadata zone carries no
@@ -1,17 +1,15 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { assert } from '../utils/assert.js';
3
3
  /**
4
- * Pure parsing functions for extracting information from patch files.
5
- * All functions are synchronous and operate on string content.
4
+ * Pure parsing functions for extracting information from patch files. All
5
+ * functions are synchronous and operate on string content.
6
6
  *
7
- * This module is the ONE unified-diff walker in the codebase. Before
8
- * {@link parseDiffSections} existed, six hand-rolled `diff --git` walkers
9
- * lived across patch-parse, patch-lint-diff, patch-registration-refs, and
10
- * patch-transform — with divergent CRLF handling (a CRLF-saved patch file
11
- * silently failed target-file matching and `\ No newline` detection on
12
- * Windows), no quoted-path support, and greedy path captures that
13
- * mis-split any path containing ` b/`. New diff-shaped parsing must build
14
- * on {@link parseDiffSections} rather than re-walking lines.
7
+ * This module is the ONE unified-diff walker in the codebase. Hand-rolled
8
+ * `diff --git` walkers diverge in ways that matter: CRLF handling (a
9
+ * CRLF-saved patch file silently fails target-file matching and
10
+ * `\ No newline` detection on Windows), quoted-path support, and greedy path
11
+ * captures that mis-split any path containing ` b/`. New diff-shaped parsing
12
+ * must build on {@link parseDiffSections} rather than re-walking lines.
15
13
  */
16
14
  /**
17
15
  * Extracts the order number from a patch filename.
@@ -89,9 +87,9 @@ function unquoteGitPath(quoted) {
89
87
  * (`diff --git "a/x y" "b/x y"`), and disambiguates the unquoted form for
90
88
  * paths that themselves contain ` b/`: for the overwhelmingly common
91
89
  * non-rename case the two paths are identical, so a symmetric split
92
- * (`a/<p> b/<p>`) is tried first — the previous greedy regex
93
- * (`a\/.+ b\/(.+)$`) split such lines at the LAST ` b/` and returned a
94
- * truncated path. Falls back to a non-greedy split for renames.
90
+ * (`a/<p> b/<p>`) is tried first — a greedy `a\/.+ b\/(.+)$` splits such
91
+ * lines at the LAST ` b/` and returns a truncated path. Falls back to a
92
+ * non-greedy split for renames.
95
93
  *
96
94
  * @returns null when the line is not a diff --git header.
97
95
  */
@@ -171,6 +169,7 @@ export function parseDiffSections(diffContent) {
171
169
  isNewFile: false,
172
170
  isDeletedFile: false,
173
171
  isBinary: false,
172
+ hasBinaryDelta: false,
174
173
  hunks: [],
175
174
  };
176
175
  sections.push(current);
@@ -188,7 +187,13 @@ export function parseDiffSections(diffContent) {
188
187
  current.isDeletedFile = true;
189
188
  continue;
190
189
  }
191
- if (line === 'GIT binary patch' || /^Binary files .* differ$/.test(line)) {
190
+ if (line === 'GIT binary patch') {
191
+ current.isBinary = true;
192
+ current.hasBinaryDelta = true;
193
+ continue;
194
+ }
195
+ if (/^Binary files .* differ$/.test(line)) {
196
+ // Informational form: binary, but with no payload to apply.
192
197
  current.isBinary = true;
193
198
  continue;
194
199
  }
@@ -43,8 +43,8 @@ export declare function isCategoryAllowedByConfig(config: FireForgeConfig, categ
43
43
  export declare function categoryRangeLabel(ranges: readonly PatchPolicyRange[], category: string): string;
44
44
  /**
45
45
  * Returns the configured range that contains `order` for `category`, or
46
- * null when no such range exists. Exported for the forward-import hint
47
- *, which suppresses ordinal suggestions the reorder policy
46
+ * null when no such range exists. Exported for the forward-import hint,
47
+ * which suppresses ordinal suggestions the reorder policy
48
48
  * would refuse.
49
49
  */
50
50
  export declare function categoryRangeForOrder(cfg: PatchPolicyConfig, category: string, order: number): PatchPolicyRange | null;
@@ -61,8 +61,8 @@ function reservedRangeForOrder(cfg, order) {
61
61
  }
62
62
  /**
63
63
  * Returns the configured range that contains `order` for `category`, or
64
- * null when no such range exists. Exported for the forward-import hint
65
- *, which suppresses ordinal suggestions the reorder policy
64
+ * null when no such range exists. Exported for the forward-import hint,
65
+ * which suppresses ordinal suggestions the reorder policy
66
66
  * would refuse.
67
67
  */
68
68
  export function categoryRangeForOrder(cfg, category, order) {
@@ -1,17 +1,16 @@
1
1
  /**
2
2
  * Extracts furnace-shaped registration references from a patch body.
3
3
  *
4
- * 2026-04-24 eval Finding 1: `export-all --exclude-furnace` can land a
5
- * patch that registers a furnace component (via edits to
6
- * `toolkit/content/customElements.js`, `toolkit/content/jar.mn`, or
7
- * `toolkit/locales/jar.mn`) without including the component's source
8
- * files in the patch. `fireforge verify` then reports "Verify clean" for
9
- * the broken queue. This module provides a pattern-scoped scan so
4
+ * `export-all --exclude-furnace` can land a patch that registers a furnace
5
+ * component (via edits to `toolkit/content/customElements.js`,
6
+ * `toolkit/content/jar.mn`, or `toolkit/locales/jar.mn`) without including
7
+ * the component's source files, and `fireforge verify` then reports "Verify
8
+ * clean" for the broken queue. This module provides a pattern-scoped scan so
10
9
  * `verify` can cross-check registrations against available file bodies.
11
10
  *
12
11
  * The scan is deliberately narrow: it only matches component-shaped
13
- * references (widget tag names, locale fluent names). Unrelated jar.mn
14
- * or customElements.js edits pass through without spurious warnings.
12
+ * references (widget tag names, locale fluent names). Unrelated jar.mn or
13
+ * customElements.js edits pass through without spurious warnings.
15
14
  */
16
15
  /**
17
16
  * A referenced engine path extracted from a registration hunk, together
@@ -2,18 +2,18 @@
2
2
  /**
3
3
  * Extracts furnace-shaped registration references from a patch body.
4
4
  *
5
- * 2026-04-24 eval Finding 1: `export-all --exclude-furnace` can land a
6
- * patch that registers a furnace component (via edits to
7
- * `toolkit/content/customElements.js`, `toolkit/content/jar.mn`, or
8
- * `toolkit/locales/jar.mn`) without including the component's source
9
- * files in the patch. `fireforge verify` then reports "Verify clean" for
10
- * the broken queue. This module provides a pattern-scoped scan so
5
+ * `export-all --exclude-furnace` can land a patch that registers a furnace
6
+ * component (via edits to `toolkit/content/customElements.js`,
7
+ * `toolkit/content/jar.mn`, or `toolkit/locales/jar.mn`) without including
8
+ * the component's source files, and `fireforge verify` then reports "Verify
9
+ * clean" for the broken queue. This module provides a pattern-scoped scan so
11
10
  * `verify` can cross-check registrations against available file bodies.
12
11
  *
13
12
  * The scan is deliberately narrow: it only matches component-shaped
14
- * references (widget tag names, locale fluent names). Unrelated jar.mn
15
- * or customElements.js edits pass through without spurious warnings.
13
+ * references (widget tag names, locale fluent names). Unrelated jar.mn or
14
+ * customElements.js edits pass through without spurious warnings.
16
15
  */
16
+ import { WIDGETS_DIR } from './furnace-constants.js';
17
17
  import { parseDiffSections } from './patch-parse.js';
18
18
  /** Canonical file paths that registration-shaped diffs touch. */
19
19
  const REGISTRATION_FILE_PATHS = new Set([
@@ -99,7 +99,7 @@ function extractTargetPathsFromRegistrationLine(sourceFile, added) {
99
99
  if (elementMatch?.[1] && elementMatch[2]) {
100
100
  const tag = elementMatch[1];
101
101
  const fileStem = elementMatch[2];
102
- return [`toolkit/content/widgets/${tag}/${fileStem}.mjs`];
102
+ return [`${WIDGETS_DIR}/${tag}/${fileStem}.mjs`];
103
103
  }
104
104
  return [];
105
105
  }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Reads a process's accumulated CPU seconds.
3
+ *
4
+ * Best-effort: a missing `ps`, an exited process, or an unparseable field
5
+ * yields `undefined`, which callers must render as "unknown" rather than as
6
+ * zero — reporting a live build as having used no CPU would invert the very
7
+ * diagnosis this exists for.
8
+ *
9
+ * @param pid - Process to inspect
10
+ * @returns Accumulated CPU seconds, or `undefined` when unmeasurable
11
+ */
12
+ export declare function readProcessCpuSeconds(pid: number): Promise<number | undefined>;
@@ -0,0 +1,52 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Accumulated-CPU-time probe for a single PID.
4
+ *
5
+ * Exists to answer the one question `kill(pid, 0)` cannot: a lock holder
6
+ * that EXISTS may still be wedged. A downstream fork running five concurrent
7
+ * sessions on one checkout diagnosed exactly this by hand — "a holder at
8
+ * near-zero CPU is hung, not working" — after a queue stalled behind a
9
+ * command that had consumed 0.3 s of CPU in eleven minutes.
10
+ *
11
+ * It is reported as EVIDENCE, never as a verdict. A legitimately slow holder
12
+ * can also sit at low CPU while blocked on I/O or on a child process, so
13
+ * nothing keys a refusal, a kill, or a lock steal off this number.
14
+ */
15
+ import { exec } from '../utils/process.js';
16
+ import { parsePsDuration } from '../utils/ps-duration.js';
17
+ /**
18
+ * Reads a process's accumulated CPU seconds.
19
+ *
20
+ * Best-effort: a missing `ps`, an exited process, or an unparseable field
21
+ * yields `undefined`, which callers must render as "unknown" rather than as
22
+ * zero — reporting a live build as having used no CPU would invert the very
23
+ * diagnosis this exists for.
24
+ *
25
+ * @param pid - Process to inspect
26
+ * @returns Accumulated CPU seconds, or `undefined` when unmeasurable
27
+ */
28
+ export async function readProcessCpuSeconds(pid) {
29
+ if (!Number.isSafeInteger(pid) || pid <= 0)
30
+ return undefined;
31
+ try {
32
+ if (process.platform === 'win32') {
33
+ const result = await exec('powershell.exe', [
34
+ '-NoProfile',
35
+ '-NonInteractive',
36
+ '-Command',
37
+ `(Get-Process -Id ${String(pid)} -ErrorAction SilentlyContinue).CPU`,
38
+ ]);
39
+ const seconds = Number.parseFloat(result.stdout.trim());
40
+ return Number.isFinite(seconds) ? seconds : undefined;
41
+ }
42
+ const result = await exec('ps', ['-p', String(pid), '-o', 'time=']);
43
+ const field = result.stdout.split(/\r?\n/).find((line) => line.trim().length > 0);
44
+ if (field === undefined)
45
+ return undefined;
46
+ const seconds = parsePsDuration(field);
47
+ return Number.isNaN(seconds) ? undefined : seconds;
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ }
@@ -6,16 +6,34 @@
6
6
  */
7
7
  import type { FirefoxProduct } from '../types/config.js';
8
8
  export type RebasePatchStatus = 'pending' | 'applied-clean' | 'applied-fuzz' | 'failed' | 'resolved' | 'skipped';
9
- export interface RebasePatchEntry {
9
+ /**
10
+ * One patch's outcome in a rebase session.
11
+ *
12
+ * A discriminated union on `status`, so a payload can only be present on the
13
+ * status it belongs to. A flat-optionals shape makes
14
+ * `{ status: 'resolved', error, conflictingFiles }` representable, and
15
+ * `rebase --continue` will write it — flipping the status without clearing
16
+ * the failure payload, then persisting that to the session file.
17
+ *
18
+ * The on-disk validator (`isValidPatchEntry`) is deliberately NOT tightened
19
+ * to match. The session file carries no schema version, so a stricter
20
+ * validator would reject an older file mid-rebase, where the only remedies
21
+ * discard the operator's in-progress conflict resolution. Instead
22
+ * {@link normalizeEntry} drops payload that does not belong to the status on
23
+ * read, so an older file loads and is corrected rather than refused.
24
+ */
25
+ export type RebasePatchEntry = {
10
26
  filename: string;
11
- status: RebasePatchStatus;
12
- /** Fuzz factor used when status is `applied-fuzz`. */
13
- fuzzFactor?: number;
14
- /** Error message when status is `failed`. */
27
+ } & ({
28
+ status: 'pending' | 'applied-clean' | 'skipped' | 'resolved';
29
+ } | {
30
+ status: 'applied-fuzz';
31
+ fuzzFactor: number;
32
+ } | {
33
+ status: 'failed';
15
34
  error?: string;
16
- /** Files that caused conflicts. */
17
35
  conflictingFiles?: string[];
18
- }
36
+ });
19
37
  export interface RebaseSession {
20
38
  /** ISO timestamp when the rebase started. */
21
39
  startedAt: string;
@@ -39,12 +57,11 @@ export interface RebaseSession {
39
57
  * discriminated-union form rather than collapsing three states into `null`.
40
58
  *
41
59
  * Distinguishing `present: false` from `present: true, valid: false` is
42
- * load-bearing. Before 0.41.0 both returned `null` while
43
- * {@link hasActiveRebaseSession} reported liveness from `pathExists` alone, so
44
- * an invalid session file wedged the operator in a closed cycle: `rebase` said
45
- * "already in progress — use --continue or --abort", and both of those said
46
- * "no rebase session in progress". No CLI path deleted the file and no message
47
- * named it, so the only escape was knowing to `rm` it by hand.
60
+ * load-bearing. Collapsing both to `null` while reporting liveness from
61
+ * `pathExists` alone wedges the operator in a closed cycle: `rebase` says
62
+ * "already in progress — use --continue or --abort", and both of those say
63
+ * "no rebase session in progress", with no CLI path deleting the file and no
64
+ * message naming it.
48
65
  */
49
66
  export type RebaseSessionRead = {
50
67
  present: false;
@@ -62,14 +79,15 @@ export declare function getRebaseSessionPath(projectRoot: string): string;
62
79
  /**
63
80
  * Reads the session file, reporting absent, valid, and corrupt as three
64
81
  * distinct outcomes. Never throws for a malformed file: `readJson` calls
65
- * `JSON.parse` with no guard of its own, so an interrupted write surfaced a
66
- * raw `SyntaxError` out of `--continue`/`--abort` before 0.41.0.
82
+ * `JSON.parse` with no guard of its own, so an interrupted write would
83
+ * otherwise surface a raw `SyntaxError` out of `--continue`/`--abort`.
67
84
  *
68
85
  * A single read determines liveness and validity: only ENOENT/ENOTDIR from
69
- * that read mean `absent`. A pathExists pre-probe both raced deletion (a file
70
- * removed between probe and read misreported an absent session as corrupt)
71
- * and swallowed EACCES (an unreadable `.fireforge/` misreported a session as
72
- * absent) — the same failure `readTreeMarkerState` fixed for tree markers.
86
+ * that read mean `absent`. A pathExists pre-probe both races deletion (a
87
+ * file removed between probe and read misreports an absent session as
88
+ * corrupt) and swallows EACCES (an unreadable `.fireforge/` misreports a
89
+ * session as absent) — the same failure `readTreeMarker` avoids for tree
90
+ * markers.
73
91
  */
74
92
  export declare function readRebaseSession(projectRoot: string): Promise<RebaseSessionRead>;
75
93
  /**
@@ -77,7 +95,7 @@ export declare function readRebaseSession(projectRoot: string): Promise<RebaseSe
77
95
  * file on disk is unusable. Callers that must tell those two apart — every
78
96
  * command that reports to an operator — should use {@link readRebaseSession}.
79
97
  */
80
- export declare function loadRebaseSession(projectRoot: string): Promise<RebaseSession | null>;
98
+ export declare function tryReadRebaseSession(projectRoot: string): Promise<RebaseSession | null>;
81
99
  /**
82
100
  * Persists a rebase session atomically.
83
101
  */
@@ -86,9 +104,3 @@ export declare function saveRebaseSession(projectRoot: string, session: RebaseSe
86
104
  * Removes the rebase session file.
87
105
  */
88
106
  export declare function clearRebaseSession(projectRoot: string): Promise<void>;
89
- /**
90
- * Returns `true` when a rebase session file exists on disk, valid or not.
91
- * Test-facing convenience — production code reads `readRebaseSession` once
92
- * instead, so liveness and validity come from the same read.
93
- */
94
- export declare function hasActiveRebaseSession(projectRoot: string): Promise<boolean>;
@@ -25,6 +25,31 @@ const REBASE_PATCH_STATUSES = [
25
25
  'skipped',
26
26
  ];
27
27
  /** Validates one `patches[]` element to its declared shape. */
28
+ /**
29
+ * Drops payload that does not belong to an entry's status.
30
+ *
31
+ * Applied on read so a session written by an older FireForge — where
32
+ * `rebase --continue` left `error`/`conflictingFiles` on a now-`resolved`
33
+ * entry — loads cleanly and is corrected, instead of being refused by a
34
+ * stricter validator mid-rebase.
35
+ *
36
+ * @param entry - A structurally valid entry, possibly carrying stale payload
37
+ * @returns The entry with only the fields its status permits
38
+ */
39
+ function normalizeEntry(entry) {
40
+ if (entry.status === 'applied-fuzz') {
41
+ return { filename: entry.filename, status: 'applied-fuzz', fuzzFactor: entry.fuzzFactor };
42
+ }
43
+ if (entry.status === 'failed') {
44
+ return {
45
+ filename: entry.filename,
46
+ status: 'failed',
47
+ ...(entry.error !== undefined ? { error: entry.error } : {}),
48
+ ...(entry.conflictingFiles !== undefined ? { conflictingFiles: entry.conflictingFiles } : {}),
49
+ };
50
+ }
51
+ return { filename: entry.filename, status: entry.status };
52
+ }
28
53
  function isValidPatchEntry(value) {
29
54
  if (!isObject(value))
30
55
  return false;
@@ -50,19 +75,19 @@ function isValidOptionalProduct(value) {
50
75
  * Validates a session file against the shape the rebase commands actually
51
76
  * consume — not merely against broad field types.
52
77
  *
53
- * The looser predicate this replaces checked six `typeof`s and nothing else,
54
- * which admitted sessions the resume path then acted on. `patches: [null]`
55
- * reached `patch-loop.ts`, which reads `.filename` off each entry and hands it
56
- * to `stampPatchVersions`. A `currentIndex` of `NaN` (legal for
57
- * `typeof === 'number'`) made the resume loop run zero iterations and report
58
- * success; a negative one indexed out of range in `continue.ts`. `toVersion:
59
- * ""` passed `isString` and was stamped verbatim onto every Furnace override's
60
- * `baseVersion`. `fromProduct`/`toProduct` were not checked at all despite
61
- * being typed `FirefoxProduct`.
78
+ * A looser predicate that checks six `typeof`s and nothing else admits
79
+ * sessions the resume path then acts on. `patches: [null]` reaches
80
+ * `patch-loop.ts`, which reads `.filename` off each entry and hands it to
81
+ * `stampPatchVersions`. A `currentIndex` of `NaN` (legal for
82
+ * `typeof === 'number'`) makes the resume loop run zero iterations and
83
+ * report success; a negative one indexes out of range in `continue.ts`.
84
+ * `toVersion: ""` passes `isString` and is stamped verbatim onto every
85
+ * Furnace override's `baseVersion`. `fromProduct`/`toProduct` go unchecked
86
+ * despite being typed `FirefoxProduct`.
62
87
  *
63
- * The session file is written only by `rebase/index.ts` from validated CLI and
64
- * config values, so every one of these is a corrupt-file or hand-edit case —
65
- * which is exactly the case this predicate exists to catch.
88
+ * The session file is written only by `rebase/index.ts` from validated CLI
89
+ * and config values, so every one of these is a corrupt-file or hand-edit
90
+ * case — which is exactly what this predicate exists to catch.
66
91
  */
67
92
  function isValidSession(data) {
68
93
  if (!isObject(data))
@@ -98,14 +123,15 @@ export function getRebaseSessionPath(projectRoot) {
98
123
  /**
99
124
  * Reads the session file, reporting absent, valid, and corrupt as three
100
125
  * distinct outcomes. Never throws for a malformed file: `readJson` calls
101
- * `JSON.parse` with no guard of its own, so an interrupted write surfaced a
102
- * raw `SyntaxError` out of `--continue`/`--abort` before 0.41.0.
126
+ * `JSON.parse` with no guard of its own, so an interrupted write would
127
+ * otherwise surface a raw `SyntaxError` out of `--continue`/`--abort`.
103
128
  *
104
129
  * A single read determines liveness and validity: only ENOENT/ENOTDIR from
105
- * that read mean `absent`. A pathExists pre-probe both raced deletion (a file
106
- * removed between probe and read misreported an absent session as corrupt)
107
- * and swallowed EACCES (an unreadable `.fireforge/` misreported a session as
108
- * absent) — the same failure `readTreeMarkerState` fixed for tree markers.
130
+ * that read mean `absent`. A pathExists pre-probe both races deletion (a
131
+ * file removed between probe and read misreports an absent session as
132
+ * corrupt) and swallows EACCES (an unreadable `.fireforge/` misreports a
133
+ * session as absent) — the same failure `readTreeMarker` avoids for tree
134
+ * markers.
109
135
  */
110
136
  export async function readRebaseSession(projectRoot) {
111
137
  const path = sessionPath(projectRoot);
@@ -122,14 +148,23 @@ export async function readRebaseSession(projectRoot) {
122
148
  if (!isValidSession(data)) {
123
149
  return { present: true, valid: false, reason: 'the file is not a valid rebase session' };
124
150
  }
125
- return { present: true, valid: true, session: data };
151
+ // Normalize on read: an older session can carry a failure's
152
+ // `error`/`conflictingFiles` on an entry whose status has since been
153
+ // flipped to `resolved`. Correcting it here is what lets the validator
154
+ // stay permissive, so an in-flight rebase from an older FireForge still
155
+ // loads.
156
+ return {
157
+ present: true,
158
+ valid: true,
159
+ session: { ...data, patches: data.patches.map(normalizeEntry) },
160
+ };
126
161
  }
127
162
  /**
128
163
  * Loads an existing rebase session, or returns `null` when none exists or the
129
164
  * file on disk is unusable. Callers that must tell those two apart — every
130
165
  * command that reports to an operator — should use {@link readRebaseSession}.
131
166
  */
132
- export async function loadRebaseSession(projectRoot) {
167
+ export async function tryReadRebaseSession(projectRoot) {
133
168
  const result = await readRebaseSession(projectRoot);
134
169
  return result.present && result.valid ? result.session : null;
135
170
  }
@@ -151,11 +186,3 @@ export async function clearRebaseSession(projectRoot) {
151
186
  await removeFile(path);
152
187
  }
153
188
  }
154
- /**
155
- * Returns `true` when a rebase session file exists on disk, valid or not.
156
- * Test-facing convenience — production code reads `readRebaseSession` once
157
- * instead, so liveness and validity come from the same read.
158
- */
159
- export async function hasActiveRebaseSession(projectRoot) {
160
- return pathExists(sessionPath(projectRoot));
161
- }