@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
@@ -3,14 +3,13 @@ export interface BuildIdentity {
3
3
  shortCommit: string;
4
4
  dirty: boolean;
5
5
  /**
6
- * Content hash of the uncommitted diff, present only for a DIRTY
7
- * installed build. Two packs from the same HEAD with
8
- * different uncommitted content share `shortCommit` and the `.dirty`
9
- * marker, so without this they report byte-identical `--version`
10
- * strings — which is what made a field report against
11
- * `0.41.0+g<sha>.dirty` impossible to trace back to a specific pack.
12
- * Absent in a git checkout: identity there comes from live git, and
13
- * hashing the full diff on every `--version` call is not worth it.
6
+ * Content hash of the uncommitted diff, present only for a DIRTY installed
7
+ * build. Two packs from the same HEAD with different uncommitted content
8
+ * share `shortCommit` and the `.dirty` marker, so without this they report
9
+ * byte-identical `--version` strings and a report against
10
+ * `<semver>+g<sha>.dirty` cannot be traced back to a specific pack. Absent
11
+ * in a git checkout: identity there comes from live git, and hashing the
12
+ * full diff on every `--version` call is not worth it.
14
13
  */
15
14
  dirtyHash?: string;
16
15
  }
@@ -1,20 +1,21 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
- * Build identity for `--version`. Three distinct builds
4
- * shipped as plain "0.41.0" — consumers had to fingerprint tarballs by
5
- * sha256 to know which build a claim was verified against. `--version`
6
- * now reports `<semver>+g<short-sha>[.dirty[.<content-hash>]]`:
3
+ * Build identity for `--version`.
7
4
  *
8
- * - In a git checkout (dev runs via tsx), identity comes from live git —
9
- * a stale `dist/build-info.json` from an old build must never win.
10
- * - In an installed package, it comes from `dist/build-info.json`,
11
- * stamped by scripts/generate-build-info.mjs at build/pack time.
5
+ * Distinct builds shipping as the same plain semver leave consumers
6
+ * fingerprinting tarballs by sha256 to know which build a claim was verified
7
+ * against. `--version` reports `<semver>+g<short-sha>[.dirty[.<content-hash>]]`:
8
+ *
9
+ * - In a git checkout (dev runs via tsx), identity comes from live git — a
10
+ * stale `dist/build-info.json` from an old build must never win.
11
+ * - In an installed package, it comes from `dist/build-info.json`, stamped
12
+ * by scripts/generate-build-info.mjs at build/pack time.
12
13
  * - When neither yields an identity, `--version` degrades to the plain
13
14
  * semver. Reading identity must never throw.
14
15
  *
15
- * Deliberately NOT wired into the per-patch lint cache key: identity
16
- * would churn the cache on every commit at the same semver for no
17
- * correctness gain (`getPackageVersion` stays the cache input).
16
+ * Deliberately NOT wired into the per-patch lint cache key: identity would
17
+ * churn the cache on every commit at the same semver for no correctness gain
18
+ * (`getPackageVersion` stays the cache input).
18
19
  */
19
20
  import { execFileSync } from 'node:child_process';
20
21
  import { existsSync, readFileSync } from 'node:fs';
@@ -20,9 +20,8 @@ export declare function toError(error: unknown): Error;
20
20
  * The check is deliberately structural rather than `error instanceof Error`:
21
21
  * a plain object carrying `.code` — exactly the shape {@link toError} exists
22
22
  * to normalise — reaches errno consumers through rejected promises and
23
- * cross-realm throws. Nine hand-rolled copies of this predicate existed
24
- * before 0.41.0, four of them (in `utils/fs.ts`) gated on `instanceof Error`
25
- * and so misclassified that shape as "no code".
23
+ * cross-realm throws, and an `instanceof Error` gate misclassifies it as "no
24
+ * code".
26
25
  */
27
26
  export declare function getNodeErrorCode(error: unknown): string | undefined;
28
27
  /**
@@ -32,10 +31,9 @@ export declare function getNodeErrorCode(error: unknown): string | undefined;
32
31
  * Only `ESRCH` ("no such process") means dead. **`EPERM` means ALIVE**: the
33
32
  * process exists but is owned by another uid, which happens routinely with
34
33
  * root-owned builds, `sudo`, shared CI runners and container UID mismatches.
35
- * Two copies of this predicate (`tree-store.ts`, `doctor-furnace.ts`) read
36
- * EPERM as dead before 0.41.0, and both gated a recursive delete — a live
37
- * build's tree clone and a live furnace lock were removed out from under
38
- * their owner. Any other errno is treated as "unknown, assume alive", which
39
- * is the safe direction for every caller: it refuses rather than destroys.
34
+ * Reading EPERM as dead in a predicate that gates a recursive delete removes
35
+ * a live build's tree clone or a live furnace lock out from under its owner.
36
+ * Any other errno is treated as "unknown, assume alive", the safe direction
37
+ * for every caller: it refuses rather than destroys.
40
38
  */
41
39
  export declare function isProcessAlive(pid: number): boolean;
@@ -32,9 +32,8 @@ export function toError(error) {
32
32
  * The check is deliberately structural rather than `error instanceof Error`:
33
33
  * a plain object carrying `.code` — exactly the shape {@link toError} exists
34
34
  * to normalise — reaches errno consumers through rejected promises and
35
- * cross-realm throws. Nine hand-rolled copies of this predicate existed
36
- * before 0.41.0, four of them (in `utils/fs.ts`) gated on `instanceof Error`
37
- * and so misclassified that shape as "no code".
35
+ * cross-realm throws, and an `instanceof Error` gate misclassifies it as "no
36
+ * code".
38
37
  */
39
38
  export function getNodeErrorCode(error) {
40
39
  if (typeof error === 'object' &&
@@ -52,11 +51,10 @@ export function getNodeErrorCode(error) {
52
51
  * Only `ESRCH` ("no such process") means dead. **`EPERM` means ALIVE**: the
53
52
  * process exists but is owned by another uid, which happens routinely with
54
53
  * root-owned builds, `sudo`, shared CI runners and container UID mismatches.
55
- * Two copies of this predicate (`tree-store.ts`, `doctor-furnace.ts`) read
56
- * EPERM as dead before 0.41.0, and both gated a recursive delete — a live
57
- * build's tree clone and a live furnace lock were removed out from under
58
- * their owner. Any other errno is treated as "unknown, assume alive", which
59
- * is the safe direction for every caller: it refuses rather than destroys.
54
+ * Reading EPERM as dead in a predicate that gates a recursive delete removes
55
+ * a live build's tree clone or a live furnace lock out from under its owner.
56
+ * Any other errno is treated as "unknown, assume alive", the safe direction
57
+ * for every caller: it refuses rather than destroys.
60
58
  */
61
59
  export function isProcessAlive(pid) {
62
60
  try {
@@ -12,6 +12,25 @@ export declare function setMachineOutputMode(enabled: boolean): void;
12
12
  export declare function isMachineOutputMode(): boolean;
13
13
  /** Seals (or unseals) stdout after a machine-readable final line. */
14
14
  export declare function setStdoutSealed(sealed: boolean): void;
15
+ /**
16
+ * True when a machine-readable payload already owns stdout.
17
+ *
18
+ * The CLI error boundary consults this before emitting a `--json` error
19
+ * envelope: a command that wrote its payload and THEN refused (e.g.
20
+ * `status --json --fail-on`) must not have a second JSON document appended,
21
+ * which would break the "exactly one document" half of the contract in
22
+ * docs/machine-output.md.
23
+ */
24
+ export declare function isStdoutSealed(): boolean;
25
+ /**
26
+ * Checks if verbose mode is enabled.
27
+ *
28
+ * Exported so the CLI error boundary can decide whether to walk an error's
29
+ * `cause` chain.
30
+ *
31
+ * @returns True if verbose mode is enabled
32
+ */
33
+ export declare function isVerbose(): boolean;
15
34
  /**
16
35
  * Displays a verbose/debug message (only shown if verbose mode is enabled).
17
36
  * @param message - Message to display
@@ -49,13 +68,13 @@ export declare const NOTICE_PREFIX = "[FireForge] NOTICE:";
49
68
  *
50
69
  * These lines are not warnings about the operator's code — they explain a
51
70
  * decision FireForge just made (escalating an incremental build to a full
52
- * one, sharding a run, skipping a step). Emitted through `info` they were
71
+ * one, sharding a run, skipping a step). Emitted through `info` they are
53
72
  * silently dropped by agent-facing output filters that keep only warnings
54
73
  * and errors, leaving a multi-minute build with no explanation and reading
55
74
  * as a hang. There are very few such lines and they are exactly the ones a
56
- * non-interactive operator needs, so they ride the warning channel and
57
- * carry {@link NOTICE_PREFIX} so a reader can still tell them apart from a
58
- * real warning.
75
+ * non-interactive operator needs, so they ride the warning channel and carry
76
+ * {@link NOTICE_PREFIX} so a reader can still tell them apart from a real
77
+ * warning.
59
78
  */
60
79
  export declare function notice(message: string): void;
61
80
  /** Logs an error message. */
@@ -80,12 +99,10 @@ export declare function cancel(message: string): void;
80
99
  * Checks whether a prompt result represents a user cancellation.
81
100
  *
82
101
  * Narrows to `symbol` — clack returns its cancel sentinel as a symbol and
83
- * `p.isCancel` is itself a type predicate. This wrapper erased that narrowing
84
- * by returning plain `boolean` until 0.41.0, which forced eleven `as string`
85
- * / `as PatchCategory` / `as OverrideType` casts across six command modules
86
- * on the *non*-cancelled branch, where the value is already known not to be
87
- * the sentinel. Keep the predicate form: it is the single highest-leverage
88
- * signature in the codebase for escape-hatch count.
102
+ * `p.isCancel` is itself a type predicate. Returning plain `boolean` erases
103
+ * that narrowing and forces `as string` / `as PatchCategory` casts across
104
+ * the command modules on the *non*-cancelled branch, where the value is
105
+ * already known not to be the sentinel. Keep the predicate form.
89
106
  */
90
107
  export declare function isCancel(value: unknown): value is symbol;
91
108
  /** Displays a titled note block for follow-up details. */
@@ -9,11 +9,9 @@ let verboseMode = false;
9
9
  * In machine mode, stdout belongs EXCLUSIVELY to the machine-readable
10
10
  * payload: all human-facing diagnostics (intro/outro banners, info, warn,
11
11
  * error, steps, notes) are routed to stderr as plain unstyled lines.
12
- * Before this mode existed, clack's log helpers wrote styled warnings to
13
- * stdout *before* the JSON body — a truncated-directory warning during
14
- * `status --json` broke every `JSON.parse(stdout)` consumer, and error
15
- * objects on the machine contract were followed by a styled duplicate on
16
- * the same stream.
12
+ * Otherwise clack's log helpers write styled warnings to stdout *before* the
13
+ * JSON body — a truncated-directory warning during `status --json` breaks
14
+ * every `JSON.parse(stdout)` consumer.
17
15
  */
18
16
  let machineOutputMode = false;
19
17
  /**
@@ -50,6 +48,18 @@ let stdoutSealed = false;
50
48
  export function setStdoutSealed(sealed) {
51
49
  stdoutSealed = sealed;
52
50
  }
51
+ /**
52
+ * True when a machine-readable payload already owns stdout.
53
+ *
54
+ * The CLI error boundary consults this before emitting a `--json` error
55
+ * envelope: a command that wrote its payload and THEN refused (e.g.
56
+ * `status --json --fail-on`) must not have a second JSON document appended,
57
+ * which would break the "exactly one document" half of the contract in
58
+ * docs/machine-output.md.
59
+ */
60
+ export function isStdoutSealed() {
61
+ return stdoutSealed;
62
+ }
53
63
  /** True when human output must avoid stdout. */
54
64
  function routeToStderr() {
55
65
  return machineOutputMode || stdoutSealed;
@@ -60,9 +70,13 @@ function writeDiagnostic(prefix, message) {
60
70
  }
61
71
  /**
62
72
  * Checks if verbose mode is enabled.
73
+ *
74
+ * Exported so the CLI error boundary can decide whether to walk an error's
75
+ * `cause` chain.
76
+ *
63
77
  * @returns True if verbose mode is enabled
64
78
  */
65
- function isVerbose() {
79
+ export function isVerbose() {
66
80
  return verboseMode;
67
81
  }
68
82
  /**
@@ -128,13 +142,13 @@ export const NOTICE_PREFIX = '[FireForge] NOTICE:';
128
142
  *
129
143
  * These lines are not warnings about the operator's code — they explain a
130
144
  * decision FireForge just made (escalating an incremental build to a full
131
- * one, sharding a run, skipping a step). Emitted through `info` they were
145
+ * one, sharding a run, skipping a step). Emitted through `info` they are
132
146
  * silently dropped by agent-facing output filters that keep only warnings
133
147
  * and errors, leaving a multi-minute build with no explanation and reading
134
148
  * as a hang. There are very few such lines and they are exactly the ones a
135
- * non-interactive operator needs, so they ride the warning channel and
136
- * carry {@link NOTICE_PREFIX} so a reader can still tell them apart from a
137
- * real warning.
149
+ * non-interactive operator needs, so they ride the warning channel and carry
150
+ * {@link NOTICE_PREFIX} so a reader can still tell them apart from a real
151
+ * warning.
138
152
  */
139
153
  export function notice(message) {
140
154
  if (routeToStderr()) {
@@ -238,12 +252,10 @@ export function cancel(message) {
238
252
  * Checks whether a prompt result represents a user cancellation.
239
253
  *
240
254
  * Narrows to `symbol` — clack returns its cancel sentinel as a symbol and
241
- * `p.isCancel` is itself a type predicate. This wrapper erased that narrowing
242
- * by returning plain `boolean` until 0.41.0, which forced eleven `as string`
243
- * / `as PatchCategory` / `as OverrideType` casts across six command modules
244
- * on the *non*-cancelled branch, where the value is already known not to be
245
- * the sentinel. Keep the predicate form: it is the single highest-leverage
246
- * signature in the codebase for escape-hatch count.
255
+ * `p.isCancel` is itself a type predicate. Returning plain `boolean` erases
256
+ * that narrowing and forces `as string` / `as PatchCategory` casts across
257
+ * the command modules on the *non*-cancelled branch, where the value is
258
+ * already known not to be the sentinel. Keep the predicate form.
247
259
  */
248
260
  export function isCancel(value) {
249
261
  return p.isCancel(value);
@@ -0,0 +1,12 @@
1
+ import { ExitCode } from '../errors/codes.js';
2
+ /** Current envelope version. Bump only with a documented migration. */
3
+ export declare const MACHINE_OUTPUT_SCHEMA_VERSION = 1;
4
+ /**
5
+ * Writes the machine-readable error envelope to stdout and throws the
6
+ * sentinel that carries the exit code to the entrypoint. Never returns.
7
+ *
8
+ * @param code - Stable machine-readable reason tag, e.g. `engine-missing`
9
+ * @param message - Human-readable explanation, identical to the non-JSON text
10
+ * @param exitCode - Exit code to terminate with
11
+ */
12
+ export declare function emitMachineError(code: string, message: string, exitCode?: ExitCode): never;
@@ -0,0 +1,33 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * The `--json` envelope contract, in one place.
4
+ *
5
+ * Without a single owner, machine-readable output drifts into unrelated
6
+ * shapes: two commands sharing a `schemaVersion: 1` envelope can still
7
+ * disagree on failure, so a scripted consumer gets a parseable refusal from
8
+ * one and a bare non-zero exit from the other.
9
+ *
10
+ * The contract, documented in `docs/machine-output.md`:
11
+ * 1. Success writes exactly one JSON document to stdout.
12
+ * 2. Failure writes exactly one `{ schemaVersion, error, code }` document
13
+ * to stdout, then exits non-zero.
14
+ * 3. Machine mode is engaged BEFORE any output, so every diagnostic —
15
+ * including one rendered later by `withErrorHandling` — routes to
16
+ * stderr and cannot corrupt the payload.
17
+ */
18
+ import { CommandError } from '../errors/base.js';
19
+ import { ExitCode } from '../errors/codes.js';
20
+ /** Current envelope version. Bump only with a documented migration. */
21
+ export const MACHINE_OUTPUT_SCHEMA_VERSION = 1;
22
+ /**
23
+ * Writes the machine-readable error envelope to stdout and throws the
24
+ * sentinel that carries the exit code to the entrypoint. Never returns.
25
+ *
26
+ * @param code - Stable machine-readable reason tag, e.g. `engine-missing`
27
+ * @param message - Human-readable explanation, identical to the non-JSON text
28
+ * @param exitCode - Exit code to terminate with
29
+ */
30
+ export function emitMachineError(code, message, exitCode = ExitCode.GENERAL_ERROR) {
31
+ process.stdout.write(`${JSON.stringify({ schemaVersion: MACHINE_OUTPUT_SCHEMA_VERSION, error: message, code })}\n`);
32
+ throw new CommandError(exitCode);
33
+ }
@@ -1,9 +1,18 @@
1
1
  /**
2
2
  * Filters an object to only include keys whose values are not undefined.
3
3
  * Designed for use with exactOptionalPropertyTypes — the result can be
4
- * spread into typed option objects without assigning undefined to optional properties.
4
+ * spread into typed option objects without assigning undefined to optional
5
+ * properties.
6
+ *
7
+ * Constrained to `object`, not `Record<string, unknown>`: a named interface
8
+ * has no index signature, so the narrower constraint rejects exactly the
9
+ * annotations this helper is most useful with. Nothing in the body needs the
10
+ * index signature — the key walk goes through `Object.keys`.
11
+ *
12
+ * @param obj - Object to filter
13
+ * @returns The same object with every `undefined`-valued key removed
5
14
  */
6
- export declare function pickDefined<T extends Record<string, unknown>>(obj: T): {
15
+ export declare function pickDefined<T extends object>(obj: T): {
7
16
  [K in keyof T]+?: Exclude<T[K], undefined>;
8
17
  };
9
18
  import type { Command } from 'commander';
@@ -15,11 +24,10 @@ import type { Command } from 'commander';
15
24
  * as argument-validation failures; anything else re-throws out of
16
25
  * `parseAsync`, BYPASSING `withErrorHandling` entirely and landing in the
17
26
  * bin's `main().catch` as an unformatted `Fatal error: …` dump with exit 1.
18
- * That was the observed behavior for `run --smoke-exit abc` (plain Error),
19
- * `test --harness-retries 99` (GeneralError), and `export --order 0`
20
- * (FireForge InvalidArgumentError, whose `code` is a numeric ExitCode).
21
- * Every `.argParser()`/option-parser callback must be wrapped with this
22
- * helper (or throw commander's InvalidArgumentError directly).
27
+ * That covers a plain Error, a `GeneralError`, and even a FireForge
28
+ * `InvalidArgumentError`, whose `code` is a numeric ExitCode. Every
29
+ * `.argParser()`/option-parser callback must be wrapped with this helper (or
30
+ * throw commander's InvalidArgumentError directly).
23
31
  */
24
32
  export declare function commanderArgParser<T>(parse: (raw: string) => T): (raw: string) => T;
25
33
  /**
@@ -30,23 +38,32 @@ export declare function commanderArgParser<T>(parse: (raw: string) => T): (raw:
30
38
  * .option('--mach-arg <arg>', 'Repeatable.', ...stringListOption())
31
39
  * ```
32
40
  *
33
- * Seven call sites open-coded this, each with its own `[] as string[]`
34
- * default — commander types that slot as `unknown`, so every one carried the
35
- * cast. Two of the seven also mutated the accumulator in place rather than
36
- * returning a new array. Returning a fresh array per call additionally means
37
- * the default cannot be shared between two `.option()` registrations.
41
+ * Commander types the default slot as `unknown`, so every open-coded copy
42
+ * carries an `[] as string[]` cast, and it is easy to mutate the accumulator
43
+ * in place instead of returning a new array. Returning a fresh array per
44
+ * call also means the default cannot be shared between two `.option()`
45
+ * registrations.
38
46
  */
39
47
  export declare function stringListOption(): [(value: string, previous: string[]) => string[], string[]];
48
+ /** Environment variable naming a standing wait budget for the whole session. */
49
+ export declare const WAIT_LOCK_ENV_VAR = "FIREFORGE_WAIT_LOCK";
40
50
  /**
41
51
  * Resolves the parsed `--wait-lock [seconds]` option value into a wait budget
42
52
  * in seconds for `withEngineSessionLock`:
43
- * - absent (`undefined`) → `undefined` (legacy ~1 s fail-fast),
53
+ * - absent (`undefined`) → {@link WAIT_LOCK_ENV_VAR} if set, else `undefined`
54
+ * (the ~1 s fail-fast),
44
55
  * - bare flag (`true`) → {@link DEFAULT_WAIT_LOCK_SECONDS},
45
56
  * - explicit value → integer validated into 1..3600.
46
57
  *
58
+ * The flag always wins over the environment: the environment expresses a
59
+ * default for a session, and an invocation that names a budget has said
60
+ * something more specific than the session did.
61
+ *
47
62
  * Accepts the raw string too, so it doubles as the option's arg parser (wrap
48
63
  * with {@link commanderArgParser} so failures surface through commander's
49
- * invalid-argument channel).
64
+ * invalid-argument channel). In that role the value is never `undefined`, so
65
+ * the environment fallback cannot leak into flag PARSING — only into flag
66
+ * ABSENCE, which is where it belongs.
50
67
  */
51
68
  export declare function resolveWaitLockSeconds(value: string | number | boolean | undefined): number | undefined;
52
69
  /**
@@ -2,19 +2,28 @@
2
2
  /**
3
3
  * Filters an object to only include keys whose values are not undefined.
4
4
  * Designed for use with exactOptionalPropertyTypes — the result can be
5
- * spread into typed option objects without assigning undefined to optional properties.
5
+ * spread into typed option objects without assigning undefined to optional
6
+ * properties.
7
+ *
8
+ * Constrained to `object`, not `Record<string, unknown>`: a named interface
9
+ * has no index signature, so the narrower constraint rejects exactly the
10
+ * annotations this helper is most useful with. Nothing in the body needs the
11
+ * index signature — the key walk goes through `Object.keys`.
12
+ *
13
+ * @param obj - Object to filter
14
+ * @returns The same object with every `undefined`-valued key removed
6
15
  */
7
16
  export function pickDefined(obj) {
8
17
  const result = {};
9
- for (const key of Object.keys(obj)) {
10
- if (obj[key] !== undefined) {
11
- result[key] = obj[key];
18
+ for (const [key, value] of Object.entries(obj)) {
19
+ if (value !== undefined) {
20
+ result[key] = value;
12
21
  }
13
22
  }
14
23
  return result;
15
24
  }
16
25
  import { InvalidArgumentError as CommanderInvalidArgumentError } from 'commander';
17
- import { GeneralError } from '../errors/base.js';
26
+ import { GeneralError, InvalidArgumentError } from '../errors/base.js';
18
27
  import { toError } from './errors.js';
19
28
  /**
20
29
  * Wraps an option-argument parser so its failures surface through
@@ -24,11 +33,10 @@ import { toError } from './errors.js';
24
33
  * as argument-validation failures; anything else re-throws out of
25
34
  * `parseAsync`, BYPASSING `withErrorHandling` entirely and landing in the
26
35
  * bin's `main().catch` as an unformatted `Fatal error: …` dump with exit 1.
27
- * That was the observed behavior for `run --smoke-exit abc` (plain Error),
28
- * `test --harness-retries 99` (GeneralError), and `export --order 0`
29
- * (FireForge InvalidArgumentError, whose `code` is a numeric ExitCode).
30
- * Every `.argParser()`/option-parser callback must be wrapped with this
31
- * helper (or throw commander's InvalidArgumentError directly).
36
+ * That covers a plain Error, a `GeneralError`, and even a FireForge
37
+ * `InvalidArgumentError`, whose `code` is a numeric ExitCode. Every
38
+ * `.argParser()`/option-parser callback must be wrapped with this helper (or
39
+ * throw commander's InvalidArgumentError directly).
32
40
  */
33
41
  export function commanderArgParser(parse) {
34
42
  return (raw) => {
@@ -48,31 +56,81 @@ export function commanderArgParser(parse) {
48
56
  * .option('--mach-arg <arg>', 'Repeatable.', ...stringListOption())
49
57
  * ```
50
58
  *
51
- * Seven call sites open-coded this, each with its own `[] as string[]`
52
- * default — commander types that slot as `unknown`, so every one carried the
53
- * cast. Two of the seven also mutated the accumulator in place rather than
54
- * returning a new array. Returning a fresh array per call additionally means
55
- * the default cannot be shared between two `.option()` registrations.
59
+ * Commander types the default slot as `unknown`, so every open-coded copy
60
+ * carries an `[] as string[]` cast, and it is easy to mutate the accumulator
61
+ * in place instead of returning a new array. Returning a fresh array per
62
+ * call also means the default cannot be shared between two `.option()`
63
+ * registrations.
56
64
  */
57
65
  export function stringListOption() {
58
66
  return [(value, previous) => [...previous, value], []];
59
67
  }
60
68
  /** Wait budget applied when `--wait-lock` is passed without a value. */
61
69
  const DEFAULT_WAIT_LOCK_SECONDS = 60;
70
+ /** Environment variable naming a standing wait budget for the whole session. */
71
+ export const WAIT_LOCK_ENV_VAR = 'FIREFORGE_WAIT_LOCK';
72
+ /** Bounds shared by the flag and the environment variable. */
73
+ const MIN_WAIT_LOCK_SECONDS = 1;
74
+ const MAX_WAIT_LOCK_SECONDS = 3600;
75
+ /**
76
+ * Parses a wait budget from text, or undefined when it is not an integer in
77
+ * range. Shared by the flag and {@link WAIT_LOCK_ENV_VAR} so the two cannot
78
+ * drift on what they accept.
79
+ */
80
+ function parseWaitLockSeconds(raw) {
81
+ const n = Number.parseInt(raw, 10);
82
+ if (!Number.isFinite(n) || n < MIN_WAIT_LOCK_SECONDS || n > MAX_WAIT_LOCK_SECONDS) {
83
+ return undefined;
84
+ }
85
+ return n;
86
+ }
87
+ /**
88
+ * The standing wait budget from {@link WAIT_LOCK_ENV_VAR}, or undefined when
89
+ * it is unset or empty.
90
+ *
91
+ * Exists because a wait budget is a property of the SESSION, not of the
92
+ * invocation: a checkout worked by several concurrent agent sessions has a
93
+ * held lock as its normal state, and an unattended loop would otherwise have
94
+ * to thread `--wait-lock` through every command it issues — the one place
95
+ * it is easiest to forget, and the failure only shows up once the loop has
96
+ * already produced a tail of refusals.
97
+ *
98
+ * An unusable value is a usage error rather than a silent fall-back to
99
+ * fail-fast: the operator set it precisely because they did not want to
100
+ * discover the budget at refusal time.
101
+ */
102
+ function waitLockSecondsFromEnv() {
103
+ const raw = process.env[WAIT_LOCK_ENV_VAR]?.trim();
104
+ if (raw === undefined || raw.length === 0) {
105
+ return undefined;
106
+ }
107
+ const parsed = parseWaitLockSeconds(raw);
108
+ if (parsed === undefined) {
109
+ throw new InvalidArgumentError(`${WAIT_LOCK_ENV_VAR} must be an integer in ${String(MIN_WAIT_LOCK_SECONDS)}..${String(MAX_WAIT_LOCK_SECONDS)} (got "${raw}")`, WAIT_LOCK_ENV_VAR);
110
+ }
111
+ return parsed;
112
+ }
62
113
  /**
63
114
  * Resolves the parsed `--wait-lock [seconds]` option value into a wait budget
64
115
  * in seconds for `withEngineSessionLock`:
65
- * - absent (`undefined`) → `undefined` (legacy ~1 s fail-fast),
116
+ * - absent (`undefined`) → {@link WAIT_LOCK_ENV_VAR} if set, else `undefined`
117
+ * (the ~1 s fail-fast),
66
118
  * - bare flag (`true`) → {@link DEFAULT_WAIT_LOCK_SECONDS},
67
119
  * - explicit value → integer validated into 1..3600.
68
120
  *
121
+ * The flag always wins over the environment: the environment expresses a
122
+ * default for a session, and an invocation that names a budget has said
123
+ * something more specific than the session did.
124
+ *
69
125
  * Accepts the raw string too, so it doubles as the option's arg parser (wrap
70
126
  * with {@link commanderArgParser} so failures surface through commander's
71
- * invalid-argument channel).
127
+ * invalid-argument channel). In that role the value is never `undefined`, so
128
+ * the environment fallback cannot leak into flag PARSING — only into flag
129
+ * ABSENCE, which is where it belongs.
72
130
  */
73
131
  export function resolveWaitLockSeconds(value) {
74
132
  if (value === undefined || value === false) {
75
- return undefined;
133
+ return waitLockSecondsFromEnv();
76
134
  }
77
135
  if (value === true) {
78
136
  return DEFAULT_WAIT_LOCK_SECONDS;
@@ -80,23 +138,21 @@ export function resolveWaitLockSeconds(value) {
80
138
  if (typeof value === 'number') {
81
139
  return value;
82
140
  }
83
- const n = Number.parseInt(value, 10);
84
- if (!Number.isFinite(n) || n < 1 || n > 3600) {
85
- throw new GeneralError(`--wait-lock must be an integer in 1..3600 (got "${value}")`);
141
+ const parsed = parseWaitLockSeconds(value);
142
+ if (parsed === undefined) {
143
+ throw new GeneralError(`--wait-lock must be an integer in ${String(MIN_WAIT_LOCK_SECONDS)}..${String(MAX_WAIT_LOCK_SECONDS)} (got "${value}")`);
86
144
  }
87
- return n;
145
+ return parsed;
88
146
  }
89
147
  /**
90
148
  * Registers `--wait-lock [seconds]` on a command that takes NO lock, so a
91
149
  * scripted sequence can blanket-append the flag without hitting a usage
92
- * error.
150
+ * error — a command that rejects it with "unknown option" kills the whole
151
+ * sequence with a usage error instead of a lock message.
93
152
  *
94
- * `export` / `re-export` / `build` / `test` / `patch move-files` all
95
- * accept the flag; `status` and `patch staged-dependency` used to reject
96
- * it with "unknown option", which killed the whole sequence with a usage
97
- * error instead of a lock message. The flag is accepted and ignored here —
98
- * never silently repurposed — and the help text says so, so nobody reads
99
- * its presence as evidence that this command waits for anything.
153
+ * The flag is accepted and ignored here — never silently repurposed — and
154
+ * the help text says so, so nobody reads its presence as evidence that this
155
+ * command waits for anything.
100
156
  *
101
157
  * The value is parsed and validated exactly as the honoring variant does:
102
158
  * `--wait-lock nonsense` must still be a usage error everywhere, or the
@@ -134,5 +190,5 @@ export function ensureWaitLockOptionEverywhere(command) {
134
190
  * `waitLockSeconds` for `withEngineSessionLock`/`withPatchDirectoryLock`.
135
191
  */
136
192
  export function addWaitLockOption(command) {
137
- return command.option('--wait-lock [seconds]', 'Wait up to this many seconds (default 60) for another FireForge command to release the contended lock, instead of failing on the default budget', commanderArgParser((raw) => resolveWaitLockSeconds(raw)));
193
+ return command.option('--wait-lock [seconds]', `Wait up to this many seconds (default 60) for another FireForge command to release the contended lock, instead of failing on the default budget. Set ${WAIT_LOCK_ENV_VAR} to apply a budget to every command in a session`, commanderArgParser((raw) => resolveWaitLockSeconds(raw)));
138
194
  }
@@ -4,15 +4,17 @@ export declare function normalizePathSlashes(path: string): string;
4
4
  * Strips a leading `engine/` (or `engine\\`) segment from a user-supplied
5
5
  * path so the same command invocation accepts both repo-root-relative paths
6
6
  * (`engine/browser/base/content/foo.js`) and engine-relative paths
7
- * (`browser/base/content/foo.js`). The match is case-insensitive because
8
- * default macOS and Windows filesystems treat `Engine/` and `engine/` as
9
- * the same directory; a literal lowercase-only check previously left `mach`
10
- * / the manifest writers resolving against a wrongly-cased prefix. Leading
11
- * whitespace is ignored so tab-completed inputs don't slip past the strip.
7
+ * (`browser/base/content/foo.js`).
12
8
  *
13
- * The return value is trimmed of the same leading whitespace when the
14
- * prefix matched, and otherwise passed through verbatim — callers that
15
- * care about internal whitespace can trim on their side.
9
+ * The match is case-insensitive because default macOS and Windows
10
+ * filesystems treat `Engine/` and `engine/` as the same directory; a literal
11
+ * lowercase-only check leaves `mach` and the manifest writers resolving
12
+ * against a wrongly-cased prefix. Leading whitespace is ignored so
13
+ * tab-completed inputs do not slip past the strip.
14
+ *
15
+ * The return value is trimmed of that leading whitespace when the prefix
16
+ * matched, and otherwise passed through verbatim — callers that care about
17
+ * internal whitespace can trim on their side.
16
18
  *
17
19
  * @param filePath Path as provided by the user
18
20
  * @returns Path relative to the engine directory (or the original when the
@@ -18,15 +18,17 @@ export function normalizePathSlashes(path) {
18
18
  * Strips a leading `engine/` (or `engine\\`) segment from a user-supplied
19
19
  * path so the same command invocation accepts both repo-root-relative paths
20
20
  * (`engine/browser/base/content/foo.js`) and engine-relative paths
21
- * (`browser/base/content/foo.js`). The match is case-insensitive because
22
- * default macOS and Windows filesystems treat `Engine/` and `engine/` as
23
- * the same directory; a literal lowercase-only check previously left `mach`
24
- * / the manifest writers resolving against a wrongly-cased prefix. Leading
25
- * whitespace is ignored so tab-completed inputs don't slip past the strip.
21
+ * (`browser/base/content/foo.js`).
26
22
  *
27
- * The return value is trimmed of the same leading whitespace when the
28
- * prefix matched, and otherwise passed through verbatim — callers that
29
- * care about internal whitespace can trim on their side.
23
+ * The match is case-insensitive because default macOS and Windows
24
+ * filesystems treat `Engine/` and `engine/` as the same directory; a literal
25
+ * lowercase-only check leaves `mach` and the manifest writers resolving
26
+ * against a wrongly-cased prefix. Leading whitespace is ignored so
27
+ * tab-completed inputs do not slip past the strip.
28
+ *
29
+ * The return value is trimmed of that leading whitespace when the prefix
30
+ * matched, and otherwise passed through verbatim — callers that care about
31
+ * internal whitespace can trim on their side.
30
32
  *
31
33
  * @param filePath Path as provided by the user
32
34
  * @returns Path relative to the engine directory (or the original when the