@hominis/fireforge 0.45.0 → 0.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (662) hide show
  1. package/CHANGELOG.md +409 -446
  2. package/dist/bin/fireforge.d.ts +1 -1
  3. package/dist/bin/fireforge.js +21 -15
  4. package/dist/build-info.json +4 -4
  5. package/dist/src/cli-usage-error.d.ts +46 -0
  6. package/dist/src/cli-usage-error.js +81 -0
  7. package/dist/src/cli.d.ts +7 -2
  8. package/dist/src/cli.js +47 -47
  9. package/dist/src/commands/bootstrap-checks.js +2 -1
  10. package/dist/src/commands/bootstrap.js +3 -4
  11. package/dist/src/commands/build.js +27 -17
  12. package/dist/src/commands/config.js +9 -9
  13. package/dist/src/commands/destructive-decision.d.ts +19 -0
  14. package/dist/src/commands/destructive-decision.js +31 -0
  15. package/dist/src/commands/discard.js +5 -5
  16. package/dist/src/commands/doctor-check-core.d.ts +4 -10
  17. package/dist/src/commands/doctor-check-core.js +0 -8
  18. package/dist/src/commands/doctor-external-toolchains.js +2 -2
  19. package/dist/src/commands/doctor-furnace-config-sync.d.ts +2 -2
  20. package/dist/src/commands/doctor-furnace-config-sync.js +20 -20
  21. package/dist/src/commands/doctor-furnace-jar.d.ts +1 -1
  22. package/dist/src/commands/doctor-furnace-jar.js +1 -1
  23. package/dist/src/commands/doctor-furnace.js +22 -22
  24. package/dist/src/commands/doctor-orphaned-harness.d.ts +10 -17
  25. package/dist/src/commands/doctor-orphaned-harness.js +14 -23
  26. package/dist/src/commands/doctor-patch-manifest.d.ts +2 -1
  27. package/dist/src/commands/doctor-patch-manifest.js +7 -6
  28. package/dist/src/commands/doctor-post-rebase-audit.js +3 -3
  29. package/dist/src/commands/doctor-source-pin.d.ts +12 -12
  30. package/dist/src/commands/doctor-source-pin.js +14 -14
  31. package/dist/src/commands/doctor-working-tree.d.ts +3 -3
  32. package/dist/src/commands/doctor-working-tree.js +3 -3
  33. package/dist/src/commands/doctor.d.ts +3 -1
  34. package/dist/src/commands/doctor.js +15 -16
  35. package/dist/src/commands/download.js +50 -37
  36. package/dist/src/commands/export-all.js +29 -12
  37. package/dist/src/commands/export-flow.d.ts +5 -5
  38. package/dist/src/commands/export-flow.js +29 -59
  39. package/dist/src/commands/export-placement-conflicts.d.ts +3 -3
  40. package/dist/src/commands/export-placement-conflicts.js +12 -12
  41. package/dist/src/commands/export-placement-gate.d.ts +2 -2
  42. package/dist/src/commands/export-placement-gate.js +7 -13
  43. package/dist/src/commands/export-placement-policy.d.ts +1 -1
  44. package/dist/src/commands/export-placement-policy.js +4 -6
  45. package/dist/src/commands/export-shared.d.ts +51 -32
  46. package/dist/src/commands/export-shared.js +17 -32
  47. package/dist/src/commands/export.js +14 -5
  48. package/dist/src/commands/furnace/apply.js +16 -16
  49. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  50. package/dist/src/commands/furnace/authoring-config.js +19 -0
  51. package/dist/src/commands/furnace/chrome-doc-remove.d.ts +1 -1
  52. package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
  53. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +14 -14
  54. package/dist/src/commands/furnace/chrome-doc-templates.js +17 -17
  55. package/dist/src/commands/furnace/chrome-doc-tests.d.ts +2 -2
  56. package/dist/src/commands/furnace/chrome-doc-tests.js +4 -4
  57. package/dist/src/commands/furnace/chrome-doc.d.ts +8 -8
  58. package/dist/src/commands/furnace/chrome-doc.js +18 -18
  59. package/dist/src/commands/furnace/create-browser-test.d.ts +1 -1
  60. package/dist/src/commands/furnace/create-browser-test.js +7 -12
  61. package/dist/src/commands/furnace/create-dry-run.d.ts +4 -4
  62. package/dist/src/commands/furnace/create-dry-run.js +6 -9
  63. package/dist/src/commands/furnace/create-features.d.ts +2 -2
  64. package/dist/src/commands/furnace/create-features.js +2 -2
  65. package/dist/src/commands/furnace/create-mochikit.d.ts +4 -4
  66. package/dist/src/commands/furnace/create-mochikit.js +7 -7
  67. package/dist/src/commands/furnace/create-templates.d.ts +31 -15
  68. package/dist/src/commands/furnace/create-templates.js +20 -19
  69. package/dist/src/commands/furnace/create-xpcshell.d.ts +5 -5
  70. package/dist/src/commands/furnace/create-xpcshell.js +8 -8
  71. package/dist/src/commands/furnace/create.d.ts +1 -1
  72. package/dist/src/commands/furnace/create.js +35 -29
  73. package/dist/src/commands/furnace/deploy.js +15 -6
  74. package/dist/src/commands/furnace/diff.js +7 -7
  75. package/dist/src/commands/furnace/index.js +1 -1
  76. package/dist/src/commands/furnace/init.js +17 -16
  77. package/dist/src/commands/furnace/list.js +2 -2
  78. package/dist/src/commands/furnace/override.d.ts +1 -1
  79. package/dist/src/commands/furnace/override.js +43 -34
  80. package/dist/src/commands/furnace/preview.js +17 -17
  81. package/dist/src/commands/furnace/refresh.js +15 -7
  82. package/dist/src/commands/furnace/remove-mutations.d.ts +9 -10
  83. package/dist/src/commands/furnace/remove-mutations.js +19 -19
  84. package/dist/src/commands/furnace/remove-state.d.ts +1 -1
  85. package/dist/src/commands/furnace/remove-state.js +1 -1
  86. package/dist/src/commands/furnace/remove.js +4 -8
  87. package/dist/src/commands/furnace/rename-browser-test.js +10 -15
  88. package/dist/src/commands/furnace/rename-xpcshell.d.ts +2 -2
  89. package/dist/src/commands/furnace/rename-xpcshell.js +3 -3
  90. package/dist/src/commands/furnace/rename.js +36 -36
  91. package/dist/src/commands/furnace/scan.js +3 -3
  92. package/dist/src/commands/furnace/status.js +19 -16
  93. package/dist/src/commands/furnace/sync.js +6 -6
  94. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  95. package/dist/src/commands/furnace/test-file-name.js +39 -0
  96. package/dist/src/commands/furnace/validate.js +11 -12
  97. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  98. package/dist/src/commands/furnace/validation-output.js +3 -8
  99. package/dist/src/commands/import.js +22 -29
  100. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  101. package/dist/src/commands/lint-per-patch.js +27 -12
  102. package/dist/src/commands/lint-per-run-checkjs.d.ts +14 -15
  103. package/dist/src/commands/lint-per-run-checkjs.js +14 -15
  104. package/dist/src/commands/lint-report.d.ts +3 -3
  105. package/dist/src/commands/lint-report.js +4 -4
  106. package/dist/src/commands/lint.d.ts +5 -4
  107. package/dist/src/commands/lint.js +36 -36
  108. package/dist/src/commands/manifest.d.ts +4 -4
  109. package/dist/src/commands/manifest.js +1 -1
  110. package/dist/src/commands/package.js +4 -6
  111. package/dist/src/commands/patch/compact.d.ts +2 -2
  112. package/dist/src/commands/patch/compact.js +19 -20
  113. package/dist/src/commands/patch/delete.d.ts +1 -1
  114. package/dist/src/commands/patch/delete.js +23 -33
  115. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  116. package/dist/src/commands/patch/entry-projection.js +24 -0
  117. package/dist/src/commands/patch/lint-ignore.d.ts +3 -12
  118. package/dist/src/commands/patch/lint-ignore.js +33 -44
  119. package/dist/src/commands/patch/move-files-create.d.ts +2 -2
  120. package/dist/src/commands/patch/move-files-create.js +29 -15
  121. package/dist/src/commands/patch/move-files-into.d.ts +2 -2
  122. package/dist/src/commands/patch/move-files-into.js +41 -37
  123. package/dist/src/commands/patch/move-files.js +5 -14
  124. package/dist/src/commands/patch/patch-context.d.ts +1 -1
  125. package/dist/src/commands/patch/rename.d.ts +4 -4
  126. package/dist/src/commands/patch/rename.js +35 -49
  127. package/dist/src/commands/patch/reorder.js +35 -48
  128. package/dist/src/commands/patch/split-plan.d.ts +7 -8
  129. package/dist/src/commands/patch/split-plan.js +10 -13
  130. package/dist/src/commands/patch/split.d.ts +2 -2
  131. package/dist/src/commands/patch/split.js +50 -48
  132. package/dist/src/commands/patch/staged-dependency-validate.d.ts +1 -1
  133. package/dist/src/commands/patch/staged-dependency-validate.js +3 -3
  134. package/dist/src/commands/patch/staged-dependency.d.ts +1 -1
  135. package/dist/src/commands/patch/staged-dependency.js +22 -28
  136. package/dist/src/commands/patch/tier.d.ts +2 -2
  137. package/dist/src/commands/patch/tier.js +20 -30
  138. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  139. package/dist/src/commands/patch-rename-map.js +36 -0
  140. package/dist/src/commands/pending-resolution.d.ts +15 -0
  141. package/dist/src/commands/pending-resolution.js +25 -0
  142. package/dist/src/commands/re-export-adjacent.d.ts +25 -3
  143. package/dist/src/commands/re-export-adjacent.js +40 -24
  144. package/dist/src/commands/re-export-bulk-scan.d.ts +3 -3
  145. package/dist/src/commands/re-export-bulk-scan.js +23 -27
  146. package/dist/src/commands/re-export-drift.d.ts +10 -10
  147. package/dist/src/commands/re-export-drift.js +33 -33
  148. package/dist/src/commands/re-export-files.d.ts +2 -3
  149. package/dist/src/commands/re-export-files.js +52 -44
  150. package/dist/src/commands/re-export-lint.d.ts +29 -30
  151. package/dist/src/commands/re-export-lint.js +36 -29
  152. package/dist/src/commands/re-export-options.d.ts +1 -1
  153. package/dist/src/commands/re-export-options.js +1 -1
  154. package/dist/src/commands/re-export-register.d.ts +1 -1
  155. package/dist/src/commands/re-export-register.js +1 -1
  156. package/dist/src/commands/re-export-scan.d.ts +1 -1
  157. package/dist/src/commands/re-export-scan.js +12 -9
  158. package/dist/src/commands/re-export-single.d.ts +23 -4
  159. package/dist/src/commands/re-export-single.js +39 -31
  160. package/dist/src/commands/re-export.js +35 -17
  161. package/dist/src/commands/rebase/abort.js +9 -14
  162. package/dist/src/commands/rebase/continue.js +20 -19
  163. package/dist/src/commands/rebase/index.d.ts +1 -1
  164. package/dist/src/commands/rebase/index.js +5 -5
  165. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  166. package/dist/src/commands/rebase/patch-loop.js +20 -24
  167. package/dist/src/commands/register.js +5 -5
  168. package/dist/src/commands/reset.js +1 -1
  169. package/dist/src/commands/resolve.d.ts +2 -2
  170. package/dist/src/commands/resolve.js +21 -13
  171. package/dist/src/commands/run.d.ts +0 -13
  172. package/dist/src/commands/run.js +38 -51
  173. package/dist/src/commands/setup-support.d.ts +16 -4
  174. package/dist/src/commands/setup-support.js +20 -20
  175. package/dist/src/commands/setup.js +4 -10
  176. package/dist/src/commands/source.js +9 -2
  177. package/dist/src/commands/status-check.d.ts +3 -3
  178. package/dist/src/commands/status-check.js +6 -6
  179. package/dist/src/commands/status-json.d.ts +14 -13
  180. package/dist/src/commands/status-json.js +9 -8
  181. package/dist/src/commands/status-output.d.ts +3 -3
  182. package/dist/src/commands/status-output.js +5 -5
  183. package/dist/src/commands/status-ownership.d.ts +3 -3
  184. package/dist/src/commands/status-ownership.js +3 -3
  185. package/dist/src/commands/status.js +25 -26
  186. package/dist/src/commands/test-build-phase.d.ts +3 -3
  187. package/dist/src/commands/test-build-phase.js +48 -24
  188. package/dist/src/commands/test-diagnose.d.ts +1 -1
  189. package/dist/src/commands/test-diagnose.js +20 -20
  190. package/dist/src/commands/test-modes.d.ts +4 -4
  191. package/dist/src/commands/test-modes.js +4 -5
  192. package/dist/src/commands/test-register.js +5 -3
  193. package/dist/src/commands/test-run.d.ts +11 -11
  194. package/dist/src/commands/test-run.js +40 -33
  195. package/dist/src/commands/test-stale-gate.d.ts +12 -12
  196. package/dist/src/commands/test-stale-gate.js +17 -17
  197. package/dist/src/commands/test-verdict.d.ts +19 -5
  198. package/dist/src/commands/test-verdict.js +26 -11
  199. package/dist/src/commands/test.d.ts +3 -3
  200. package/dist/src/commands/test.js +73 -36
  201. package/dist/src/commands/token-coverage.js +3 -3
  202. package/dist/src/commands/token-list.d.ts +26 -0
  203. package/dist/src/commands/token-list.js +153 -0
  204. package/dist/src/commands/token.js +26 -10
  205. package/dist/src/commands/tree.js +26 -21
  206. package/dist/src/commands/typecheck.d.ts +6 -6
  207. package/dist/src/commands/typecheck.js +16 -16
  208. package/dist/src/commands/verify.d.ts +3 -3
  209. package/dist/src/commands/verify.js +10 -10
  210. package/dist/src/commands/watch.js +11 -11
  211. package/dist/src/commands/wire.js +17 -16
  212. package/dist/src/core/ast-utils.d.ts +7 -7
  213. package/dist/src/core/ast-utils.js +8 -8
  214. package/dist/src/core/branding.d.ts +5 -12
  215. package/dist/src/core/branding.js +6 -5
  216. package/dist/src/core/browser-wire.d.ts +1 -1
  217. package/dist/src/core/browser-wire.js +10 -7
  218. package/dist/src/core/build-audit-platform.d.ts +32 -3
  219. package/dist/src/core/build-audit-platform.js +153 -13
  220. package/dist/src/core/build-audit-registration.d.ts +5 -5
  221. package/dist/src/core/build-audit-registration.js +10 -9
  222. package/dist/src/core/build-audit-resolve.d.ts +5 -5
  223. package/dist/src/core/build-audit-resolve.js +6 -6
  224. package/dist/src/core/build-audit-skip.d.ts +57 -0
  225. package/dist/src/core/build-audit-skip.js +112 -0
  226. package/dist/src/core/build-audit-transforms.d.ts +2 -2
  227. package/dist/src/core/build-audit-transforms.js +3 -3
  228. package/dist/src/core/build-audit.d.ts +21 -8
  229. package/dist/src/core/build-audit.js +142 -40
  230. package/dist/src/core/build-baseline-types.d.ts +19 -18
  231. package/dist/src/core/build-baseline-types.js +1 -1
  232. package/dist/src/core/build-baseline.d.ts +48 -31
  233. package/dist/src/core/build-baseline.js +20 -43
  234. package/dist/src/core/build-jar-escalation.d.ts +8 -8
  235. package/dist/src/core/build-jar-escalation.js +18 -17
  236. package/dist/src/core/build-overwrite-guard.d.ts +4 -4
  237. package/dist/src/core/build-overwrite-guard.js +14 -14
  238. package/dist/src/core/build-prepare.d.ts +4 -12
  239. package/dist/src/core/build-prepare.js +22 -26
  240. package/dist/src/core/config-mutate.js +4 -4
  241. package/dist/src/core/config-paths.d.ts +2 -6
  242. package/dist/src/core/config-paths.js +4 -2
  243. package/dist/src/core/config-state.d.ts +0 -6
  244. package/dist/src/core/config-state.js +3 -21
  245. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  246. package/dist/src/core/config-validate-build-audit.js +80 -0
  247. package/dist/src/core/config-validate-file-size.d.ts +1 -1
  248. package/dist/src/core/config-validate-file-size.js +3 -3
  249. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  250. package/dist/src/core/config-validate-patch-policy.js +3 -3
  251. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  252. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  253. package/dist/src/core/config-validate.js +22 -12
  254. package/dist/src/core/config.d.ts +12 -12
  255. package/dist/src/core/config.js +21 -25
  256. package/dist/src/core/coverage-extend.d.ts +16 -16
  257. package/dist/src/core/coverage-extend.js +20 -20
  258. package/dist/src/core/destructive.d.ts +14 -14
  259. package/dist/src/core/destructive.js +9 -9
  260. package/dist/src/core/diff-hunks.d.ts +2 -2
  261. package/dist/src/core/diff-hunks.js +7 -7
  262. package/dist/src/core/discard-baseline.d.ts +10 -10
  263. package/dist/src/core/discard-baseline.js +22 -23
  264. package/dist/src/core/display-state.d.ts +11 -11
  265. package/dist/src/core/display-state.js +12 -12
  266. package/dist/src/core/engine-changes.d.ts +2 -2
  267. package/dist/src/core/engine-changes.js +2 -2
  268. package/dist/src/core/engine-precondition.d.ts +5 -13
  269. package/dist/src/core/engine-precondition.js +5 -10
  270. package/dist/src/core/engine-session-lock.d.ts +18 -18
  271. package/dist/src/core/engine-session-lock.js +50 -54
  272. package/dist/src/core/export-moved-code-shape.d.ts +7 -7
  273. package/dist/src/core/export-moved-code-shape.js +10 -10
  274. package/dist/src/core/file-lock-owner.d.ts +103 -0
  275. package/dist/src/core/file-lock-owner.js +259 -0
  276. package/dist/src/core/file-lock.d.ts +14 -19
  277. package/dist/src/core/file-lock.js +90 -128
  278. package/dist/src/core/firefox-cache.d.ts +13 -1
  279. package/dist/src/core/firefox-cache.js +61 -36
  280. package/dist/src/core/firefox-download.d.ts +15 -0
  281. package/dist/src/core/firefox-download.js +38 -22
  282. package/dist/src/core/firefox-extract.d.ts +8 -7
  283. package/dist/src/core/firefox-extract.js +45 -27
  284. package/dist/src/core/firefox.d.ts +34 -36
  285. package/dist/src/core/firefox.js +13 -44
  286. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  287. package/dist/src/core/furnace-apply-dry-run.js +2 -8
  288. package/dist/src/core/furnace-apply-ftl.d.ts +5 -5
  289. package/dist/src/core/furnace-apply-ftl.js +8 -8
  290. package/dist/src/core/furnace-apply-helpers.d.ts +9 -20
  291. package/dist/src/core/furnace-apply-helpers.js +28 -39
  292. package/dist/src/core/furnace-apply-output.js +1 -1
  293. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +9 -9
  294. package/dist/src/core/furnace-apply-overwrite-warn.js +13 -11
  295. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +1 -1
  296. package/dist/src/core/furnace-apply-undeploy-actions.js +1 -1
  297. package/dist/src/core/furnace-apply.d.ts +4 -4
  298. package/dist/src/core/furnace-apply.js +50 -29
  299. package/dist/src/core/furnace-checksum-utils.d.ts +5 -5
  300. package/dist/src/core/furnace-checksum-utils.js +5 -5
  301. package/dist/src/core/furnace-config-custom.d.ts +1 -1
  302. package/dist/src/core/furnace-config-custom.js +2 -2
  303. package/dist/src/core/furnace-config-order.js +1 -1
  304. package/dist/src/core/furnace-config-tokens.d.ts +3 -3
  305. package/dist/src/core/furnace-config-tokens.js +3 -3
  306. package/dist/src/core/furnace-config-validate.js +2 -2
  307. package/dist/src/core/furnace-config.d.ts +5 -5
  308. package/dist/src/core/furnace-config.js +40 -53
  309. package/dist/src/core/furnace-constants.d.ts +6 -6
  310. package/dist/src/core/furnace-constants.js +9 -8
  311. package/dist/src/core/furnace-css-fragments.d.ts +6 -6
  312. package/dist/src/core/furnace-css-fragments.js +11 -11
  313. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  314. package/dist/src/core/furnace-dir-entry.js +21 -0
  315. package/dist/src/core/furnace-jsconfig.d.ts +7 -7
  316. package/dist/src/core/furnace-jsconfig.js +22 -20
  317. package/dist/src/core/furnace-operation.d.ts +22 -19
  318. package/dist/src/core/furnace-operation.js +38 -34
  319. package/dist/src/core/furnace-precondition.d.ts +10 -10
  320. package/dist/src/core/furnace-precondition.js +8 -8
  321. package/dist/src/core/furnace-refresh.d.ts +18 -6
  322. package/dist/src/core/furnace-refresh.js +5 -8
  323. package/dist/src/core/furnace-registration-ast.d.ts +2 -3
  324. package/dist/src/core/furnace-registration-ast.js +9 -12
  325. package/dist/src/core/furnace-registration-remove.d.ts +4 -4
  326. package/dist/src/core/furnace-registration-remove.js +6 -6
  327. package/dist/src/core/furnace-registration-validate.d.ts +2 -2
  328. package/dist/src/core/furnace-registration-validate.js +2 -2
  329. package/dist/src/core/furnace-registration.d.ts +7 -6
  330. package/dist/src/core/furnace-registration.js +29 -36
  331. package/dist/src/core/furnace-rollback.d.ts +1 -1
  332. package/dist/src/core/furnace-rollback.js +5 -5
  333. package/dist/src/core/furnace-scanner.js +3 -6
  334. package/dist/src/core/furnace-stale-export.d.ts +17 -16
  335. package/dist/src/core/furnace-stale-export.js +16 -15
  336. package/dist/src/core/furnace-staleness.d.ts +2 -2
  337. package/dist/src/core/furnace-staleness.js +2 -2
  338. package/dist/src/core/furnace-state-persist.d.ts +12 -12
  339. package/dist/src/core/furnace-state-persist.js +10 -10
  340. package/dist/src/core/furnace-step-errors.d.ts +6 -6
  341. package/dist/src/core/furnace-step-errors.js +6 -6
  342. package/dist/src/core/furnace-validate-accessibility.js +14 -14
  343. package/dist/src/core/furnace-validate-compatibility.js +9 -9
  344. package/dist/src/core/furnace-validate-registration.d.ts +2 -2
  345. package/dist/src/core/furnace-validate-registration.js +18 -20
  346. package/dist/src/core/furnace-validate-structure.d.ts +1 -1
  347. package/dist/src/core/furnace-validate-structure.js +4 -4
  348. package/dist/src/core/furnace-validate.js +9 -9
  349. package/dist/src/core/furnace-version-drift.d.ts +5 -5
  350. package/dist/src/core/furnace-version-drift.js +5 -5
  351. package/dist/src/core/git-base.d.ts +6 -4
  352. package/dist/src/core/git-base.js +29 -7
  353. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  354. package/dist/src/core/git-diff-new-file.js +119 -0
  355. package/dist/src/core/git-diff.d.ts +13 -13
  356. package/dist/src/core/git-diff.js +121 -189
  357. package/dist/src/core/git-file-ops.d.ts +6 -13
  358. package/dist/src/core/git-file-ops.js +11 -35
  359. package/dist/src/core/git-readonly-index.d.ts +12 -14
  360. package/dist/src/core/git-readonly-index.js +12 -16
  361. package/dist/src/core/git-status.d.ts +9 -2
  362. package/dist/src/core/git-status.js +23 -11
  363. package/dist/src/core/git.d.ts +3 -9
  364. package/dist/src/core/git.js +28 -57
  365. package/dist/src/core/harness-orphans.d.ts +17 -17
  366. package/dist/src/core/harness-orphans.js +28 -27
  367. package/dist/src/core/history-log.d.ts +19 -0
  368. package/dist/src/core/history-log.js +29 -0
  369. package/dist/src/core/license-headers.d.ts +19 -18
  370. package/dist/src/core/license-headers.js +29 -28
  371. package/dist/src/core/mach-build-artifacts.d.ts +21 -21
  372. package/dist/src/core/mach-build-artifacts.js +23 -23
  373. package/dist/src/core/mach-error-hints.d.ts +5 -10
  374. package/dist/src/core/mach-error-hints.js +14 -14
  375. package/dist/src/core/mach-known-noise-filter.d.ts +42 -38
  376. package/dist/src/core/mach-known-noise-filter.js +57 -56
  377. package/dist/src/core/mach-mozconfig.d.ts +6 -6
  378. package/dist/src/core/mach-mozconfig.js +17 -16
  379. package/dist/src/core/mach-python.d.ts +3 -0
  380. package/dist/src/core/mach-python.js +13 -7
  381. package/dist/src/core/mach-resource-shim.d.ts +24 -12
  382. package/dist/src/core/mach-resource-shim.js +124 -21
  383. package/dist/src/core/mach.d.ts +59 -73
  384. package/dist/src/core/mach.js +47 -115
  385. package/dist/src/core/marionette-port.d.ts +15 -30
  386. package/dist/src/core/marionette-port.js +46 -89
  387. package/dist/src/core/marionette-preflight.d.ts +6 -6
  388. package/dist/src/core/marionette-preflight.js +32 -50
  389. package/dist/src/core/markdown-table.d.ts +9 -9
  390. package/dist/src/core/markdown-table.js +14 -14
  391. package/dist/src/core/mochitest-server-port.d.ts +8 -8
  392. package/dist/src/core/mochitest-server-port.js +17 -17
  393. package/dist/src/core/module-resolution-preflight.d.ts +8 -8
  394. package/dist/src/core/module-resolution-preflight.js +11 -11
  395. package/dist/src/core/moz-manifest-helpers.d.ts +7 -7
  396. package/dist/src/core/moz-manifest-helpers.js +7 -7
  397. package/dist/src/core/moz-manifest-register.d.ts +1 -1
  398. package/dist/src/core/moz-manifest-register.js +2 -2
  399. package/dist/src/core/moz-manifest-rules.js +46 -42
  400. package/dist/src/core/moz-manifest-tokenizers.js +1 -1
  401. package/dist/src/core/ownership-table.d.ts +5 -5
  402. package/dist/src/core/ownership-table.js +7 -7
  403. package/dist/src/core/parser-fallback.d.ts +3 -3
  404. package/dist/src/core/parser-fallback.js +3 -3
  405. package/dist/src/core/patch-apply-fuzz.d.ts +5 -5
  406. package/dist/src/core/patch-apply-fuzz.js +13 -13
  407. package/dist/src/core/patch-apply.d.ts +13 -14
  408. package/dist/src/core/patch-apply.js +36 -42
  409. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  410. package/dist/src/core/patch-export-coverage.js +1 -30
  411. package/dist/src/core/patch-export-update.d.ts +21 -4
  412. package/dist/src/core/patch-export-update.js +7 -6
  413. package/dist/src/core/patch-export.d.ts +20 -30
  414. package/dist/src/core/patch-export.js +55 -77
  415. package/dist/src/core/patch-files.d.ts +0 -4
  416. package/dist/src/core/patch-files.js +0 -11
  417. package/dist/src/core/patch-identifier-suggest.d.ts +1 -1
  418. package/dist/src/core/patch-identifier-suggest.js +4 -4
  419. package/dist/src/core/patch-lint-binary.d.ts +12 -18
  420. package/dist/src/core/patch-lint-cache.d.ts +18 -2
  421. package/dist/src/core/patch-lint-cache.js +16 -19
  422. package/dist/src/core/patch-lint-checkjs.d.ts +55 -38
  423. package/dist/src/core/patch-lint-checkjs.js +78 -64
  424. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +8 -9
  425. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -11
  426. package/dist/src/core/patch-lint-creators.d.ts +5 -9
  427. package/dist/src/core/patch-lint-creators.js +3 -3
  428. package/dist/src/core/patch-lint-cross.d.ts +27 -19
  429. package/dist/src/core/patch-lint-cross.js +67 -110
  430. package/dist/src/core/patch-lint-css.d.ts +1 -1
  431. package/dist/src/core/patch-lint-css.js +15 -11
  432. package/dist/src/core/patch-lint-diff-tag.d.ts +5 -5
  433. package/dist/src/core/patch-lint-diff-tag.js +5 -5
  434. package/dist/src/core/patch-lint-diff.d.ts +4 -4
  435. package/dist/src/core/patch-lint-diff.js +4 -4
  436. package/dist/src/core/patch-lint-file-size.d.ts +3 -3
  437. package/dist/src/core/patch-lint-file-size.js +6 -6
  438. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  439. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  440. package/dist/src/core/patch-lint-jsdoc.d.ts +2 -2
  441. package/dist/src/core/patch-lint-jsdoc.js +46 -46
  442. package/dist/src/core/patch-lint-module-registration.d.ts +3 -9
  443. package/dist/src/core/patch-lint-module-registration.js +1 -1
  444. package/dist/src/core/patch-lint-mozbuild.d.ts +1 -1
  445. package/dist/src/core/patch-lint-mozbuild.js +10 -10
  446. package/dist/src/core/patch-lint-observer.d.ts +3 -3
  447. package/dist/src/core/patch-lint-observer.js +7 -7
  448. package/dist/src/core/patch-lint-ownership.d.ts +6 -6
  449. package/dist/src/core/patch-lint-ownership.js +6 -6
  450. package/dist/src/core/patch-lint-prettier.d.ts +6 -6
  451. package/dist/src/core/patch-lint-prettier.js +7 -7
  452. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  453. package/dist/src/core/patch-lint-queue-types.js +12 -0
  454. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  455. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  456. package/dist/src/core/patch-lint-unmanaged-companion.d.ts +23 -23
  457. package/dist/src/core/patch-lint-unmanaged-companion.js +26 -26
  458. package/dist/src/core/patch-lint.d.ts +41 -35
  459. package/dist/src/core/patch-lint.js +52 -49
  460. package/dist/src/core/patch-lock.d.ts +1 -1
  461. package/dist/src/core/patch-lock.js +11 -11
  462. package/dist/src/core/patch-manifest-consistency.d.ts +7 -7
  463. package/dist/src/core/patch-manifest-consistency.js +16 -17
  464. package/dist/src/core/patch-manifest-files-affected.d.ts +1 -1
  465. package/dist/src/core/patch-manifest-files-affected.js +3 -3
  466. package/dist/src/core/patch-manifest-io.d.ts +14 -14
  467. package/dist/src/core/patch-manifest-io.js +35 -34
  468. package/dist/src/core/patch-manifest-query.d.ts +1 -1
  469. package/dist/src/core/patch-manifest-query.js +4 -4
  470. package/dist/src/core/patch-manifest-resolve.d.ts +5 -5
  471. package/dist/src/core/patch-manifest-resolve.js +7 -7
  472. package/dist/src/core/patch-manifest-validate.js +3 -3
  473. package/dist/src/core/patch-manifest.d.ts +2 -2
  474. package/dist/src/core/patch-manifest.js +2 -2
  475. package/dist/src/core/patch-parse.d.ts +32 -21
  476. package/dist/src/core/patch-parse.js +40 -15
  477. package/dist/src/core/patch-policy.js +15 -27
  478. package/dist/src/core/patch-registration-refs.d.ts +4 -4
  479. package/dist/src/core/patch-registration-refs.js +7 -7
  480. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  481. package/dist/src/core/patch-source-metadata.js +0 -4
  482. package/dist/src/core/patch-transform.d.ts +8 -16
  483. package/dist/src/core/patch-transform.js +13 -23
  484. package/dist/src/core/process-cpu-time.d.ts +1 -1
  485. package/dist/src/core/process-cpu-time.js +6 -6
  486. package/dist/src/core/rebase-session.d.ts +9 -15
  487. package/dist/src/core/rebase-session.js +13 -25
  488. package/dist/src/core/register-browser-content.js +3 -2
  489. package/dist/src/core/register-browser-test.js +2 -1
  490. package/dist/src/core/register-module.js +2 -1
  491. package/dist/src/core/register-result.d.ts +1 -1
  492. package/dist/src/core/register-result.js +1 -1
  493. package/dist/src/core/register-scaffold.d.ts +2 -2
  494. package/dist/src/core/register-scaffold.js +8 -7
  495. package/dist/src/core/register-shared-css.d.ts +3 -3
  496. package/dist/src/core/register-shared-css.js +6 -5
  497. package/dist/src/core/register-xpcshell-test.d.ts +2 -2
  498. package/dist/src/core/register-xpcshell-test.js +2 -2
  499. package/dist/src/core/run-log-redact.d.ts +38 -0
  500. package/dist/src/core/run-log-redact.js +69 -0
  501. package/dist/src/core/run-log.d.ts +21 -10
  502. package/dist/src/core/run-log.js +76 -27
  503. package/dist/src/core/shared-ftl.d.ts +2 -2
  504. package/dist/src/core/shared-ftl.js +6 -6
  505. package/dist/src/core/signal-critical.d.ts +2 -2
  506. package/dist/src/core/signal-critical.js +4 -6
  507. package/dist/src/core/smoke-patterns.d.ts +5 -6
  508. package/dist/src/core/smoke-patterns.js +15 -20
  509. package/dist/src/core/state-file.d.ts +15 -4
  510. package/dist/src/core/state-file.js +14 -1
  511. package/dist/src/core/status-binary.d.ts +8 -8
  512. package/dist/src/core/status-binary.js +12 -12
  513. package/dist/src/core/status-classify.d.ts +17 -17
  514. package/dist/src/core/status-classify.js +13 -12
  515. package/dist/src/core/test-changed-prefs.d.ts +12 -12
  516. package/dist/src/core/test-changed-prefs.js +40 -25
  517. package/dist/src/core/test-harness-crash.d.ts +39 -39
  518. package/dist/src/core/test-harness-crash.js +69 -69
  519. package/dist/src/core/test-harness-output.d.ts +7 -7
  520. package/dist/src/core/test-harness-output.js +7 -7
  521. package/dist/src/core/test-harness-verdict-notes.d.ts +13 -14
  522. package/dist/src/core/test-harness-verdict-notes.js +4 -4
  523. package/dist/src/core/test-path-scope.d.ts +8 -8
  524. package/dist/src/core/test-path-scope.js +11 -11
  525. package/dist/src/core/test-stale-check.d.ts +22 -23
  526. package/dist/src/core/test-stale-check.js +31 -30
  527. package/dist/src/core/test-stale-symlink.d.ts +1 -1
  528. package/dist/src/core/test-stale-symlink.js +1 -1
  529. package/dist/src/core/test-stall-triage.d.ts +9 -9
  530. package/dist/src/core/test-stall-triage.js +22 -22
  531. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  532. package/dist/src/core/test-xpcshell-retry.js +14 -11
  533. package/dist/src/core/token-category.d.ts +35 -2
  534. package/dist/src/core/token-category.js +67 -58
  535. package/dist/src/core/token-coverage.js +4 -5
  536. package/dist/src/core/token-dark-mode.d.ts +9 -9
  537. package/dist/src/core/token-dark-mode.js +13 -25
  538. package/dist/src/core/token-docs.d.ts +1 -1
  539. package/dist/src/core/token-inventory.d.ts +56 -0
  540. package/dist/src/core/token-inventory.js +109 -0
  541. package/dist/src/core/token-manager.d.ts +4 -4
  542. package/dist/src/core/token-manager.js +22 -22
  543. package/dist/src/core/token-scaffold.d.ts +4 -4
  544. package/dist/src/core/token-scaffold.js +4 -4
  545. package/dist/src/core/token-variant.d.ts +14 -14
  546. package/dist/src/core/token-variant.js +52 -53
  547. package/dist/src/core/toolchain-preflight.d.ts +15 -15
  548. package/dist/src/core/toolchain-preflight.js +23 -23
  549. package/dist/src/core/tree-cow.d.ts +3 -5
  550. package/dist/src/core/tree-cow.js +8 -8
  551. package/dist/src/core/tree-guard.d.ts +3 -3
  552. package/dist/src/core/tree-guard.js +23 -23
  553. package/dist/src/core/tree-store.d.ts +15 -15
  554. package/dist/src/core/tree-store.js +39 -38
  555. package/dist/src/core/typecheck-shim.d.ts +16 -15
  556. package/dist/src/core/typecheck-shim.js +26 -25
  557. package/dist/src/core/typecheck.d.ts +12 -12
  558. package/dist/src/core/typecheck.js +33 -33
  559. package/dist/src/core/wire-destroy.d.ts +2 -2
  560. package/dist/src/core/wire-destroy.js +5 -5
  561. package/dist/src/core/wire-dom-fragment.d.ts +3 -3
  562. package/dist/src/core/wire-dom-fragment.js +12 -23
  563. package/dist/src/core/wire-init.d.ts +2 -2
  564. package/dist/src/core/wire-init.js +7 -7
  565. package/dist/src/core/wire-subscript.d.ts +1 -1
  566. package/dist/src/core/wire-subscript.js +3 -3
  567. package/dist/src/core/wire-utils.d.ts +9 -9
  568. package/dist/src/core/wire-utils.js +16 -28
  569. package/dist/src/core/xpcshell-appdir.d.ts +20 -20
  570. package/dist/src/core/xpcshell-appdir.js +28 -30
  571. package/dist/src/core/xpcshell-profile-dir.d.ts +3 -3
  572. package/dist/src/core/xpcshell-profile-dir.js +7 -7
  573. package/dist/src/errors/base.d.ts +69 -16
  574. package/dist/src/errors/base.js +95 -26
  575. package/dist/src/errors/build.d.ts +6 -6
  576. package/dist/src/errors/build.js +26 -34
  577. package/dist/src/errors/codes.d.ts +10 -10
  578. package/dist/src/errors/codes.js +10 -10
  579. package/dist/src/errors/config.d.ts +0 -14
  580. package/dist/src/errors/config.js +13 -34
  581. package/dist/src/errors/download.d.ts +13 -0
  582. package/dist/src/errors/download.js +53 -24
  583. package/dist/src/errors/furnace.d.ts +2 -2
  584. package/dist/src/errors/furnace.js +8 -9
  585. package/dist/src/errors/git.d.ts +9 -9
  586. package/dist/src/errors/git.js +21 -31
  587. package/dist/src/errors/patch.d.ts +3 -3
  588. package/dist/src/errors/patch.js +11 -13
  589. package/dist/src/errors/rebase.d.ts +1 -1
  590. package/dist/src/errors/rebase.js +8 -8
  591. package/dist/src/errors/run.d.ts +1 -1
  592. package/dist/src/errors/run.js +1 -1
  593. package/dist/src/index.d.ts +4 -4
  594. package/dist/src/index.js +4 -4
  595. package/dist/src/types/commands/options.d.ts +155 -200
  596. package/dist/src/types/commands/patches.d.ts +19 -19
  597. package/dist/src/types/commands/project.d.ts +3 -3
  598. package/dist/src/types/config.d.ts +52 -19
  599. package/dist/src/types/furnace.d.ts +17 -17
  600. package/dist/src/types/index.d.ts +0 -1
  601. package/dist/src/types/json.d.ts +3 -3
  602. package/dist/src/types/json.js +2 -2
  603. package/dist/src/types/typecheck.d.ts +3 -3
  604. package/dist/src/utils/assert.d.ts +4 -16
  605. package/dist/src/utils/assert.js +7 -24
  606. package/dist/src/utils/build-info.d.ts +6 -6
  607. package/dist/src/utils/build-info.js +6 -6
  608. package/dist/src/utils/child-env.d.ts +7 -6
  609. package/dist/src/utils/child-env.js +5 -5
  610. package/dist/src/utils/concurrency.d.ts +2 -2
  611. package/dist/src/utils/concurrency.js +2 -2
  612. package/dist/src/utils/elapsed.js +10 -1
  613. package/dist/src/utils/errors.d.ts +8 -8
  614. package/dist/src/utils/errors.js +8 -8
  615. package/dist/src/utils/fs.d.ts +1 -1
  616. package/dist/src/utils/fs.js +15 -20
  617. package/dist/src/utils/hash.d.ts +12 -0
  618. package/dist/src/utils/hash.js +16 -0
  619. package/dist/src/utils/line-dispatch.d.ts +15 -0
  620. package/dist/src/utils/line-dispatch.js +48 -0
  621. package/dist/src/utils/logger.d.ts +4 -4
  622. package/dist/src/utils/logger.js +11 -11
  623. package/dist/src/utils/machine-output.js +2 -2
  624. package/dist/src/utils/options.d.ts +14 -20
  625. package/dist/src/utils/options.js +26 -27
  626. package/dist/src/utils/package-root.js +5 -14
  627. package/dist/src/utils/paths.d.ts +4 -4
  628. package/dist/src/utils/paths.js +4 -4
  629. package/dist/src/utils/platform.d.ts +0 -4
  630. package/dist/src/utils/process-group.d.ts +10 -9
  631. package/dist/src/utils/process-group.js +37 -36
  632. package/dist/src/utils/process.d.ts +70 -55
  633. package/dist/src/utils/process.js +129 -179
  634. package/dist/src/utils/ps-duration.d.ts +4 -4
  635. package/dist/src/utils/ps-duration.js +9 -9
  636. package/dist/src/utils/regex.d.ts +4 -4
  637. package/dist/src/utils/regex.js +7 -7
  638. package/dist/src/utils/sleep.d.ts +26 -0
  639. package/dist/src/utils/sleep.js +31 -0
  640. package/dist/src/utils/stdio-drain.d.ts +7 -7
  641. package/dist/src/utils/stdio-drain.js +8 -8
  642. package/dist/src/utils/validation.d.ts +12 -49
  643. package/dist/src/utils/validation.js +26 -36
  644. package/docs/README.md +2 -2
  645. package/docs/configuration.md +163 -69
  646. package/docs/exit-codes.md +66 -49
  647. package/docs/furnace.md +20 -19
  648. package/docs/lifecycle-invariants.md +414 -247
  649. package/docs/machine-output.md +58 -34
  650. package/docs/patch-workflow.md +48 -44
  651. package/docs/run-logs.md +72 -28
  652. package/docs/testing.md +105 -98
  653. package/docs/verification-trees.md +23 -22
  654. package/package.json +7 -6
  655. package/dist/src/commands/rebase.d.ts +0 -4
  656. package/dist/src/commands/rebase.js +0 -5
  657. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  658. package/dist/src/core/patch-artifact-normalize.js +0 -24
  659. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  660. package/dist/src/core/patch-lint-reexports.js +0 -8
  661. package/dist/src/core/wire-targets.d.ts +0 -7
  662. package/dist/src/core/wire-targets.js +0 -8
@@ -4,8 +4,9 @@ import { constants as osConstants } from 'node:os';
4
4
  import { StringDecoder } from 'node:string_decoder';
5
5
  import { ExecTimeoutError } from '../errors/base.js';
6
6
  import { buildChildEnv } from './child-env.js';
7
+ import { dispatchCompleteLines } from './line-dispatch.js';
7
8
  import { killProcessTree, sweepProcessGroup } from './process-group.js';
8
- // 50 MB cap per stream to prevent OOM — large toolchain builds (e.g. Firefox, Chromium)
9
+ // 50 MB cap per stream to prevent OOM. Large toolchain builds (e.g. Firefox, Chromium)
9
10
  // can easily blow past this, so we truncate rather than let the buffer grow unbounded.
10
11
  // Callers for whom truncation is unacceptable (e.g. the archive-safety preflight
11
12
  // scanning a full `tar -tvf` listing) must check `wasTruncated` and fail hard.
@@ -57,12 +58,25 @@ function buildSignalFromTimeout(timeout) {
57
58
  * {@link ExecTimeoutError} naming the command and budget instead of Node's
58
59
  * opaque `AbortError: The operation was aborted`.
59
60
  */
60
- function toExecRejection(error, command, args, timeout) {
61
+ function toExecRejection(error, command, args, timeout, output) {
61
62
  if (timeout !== undefined && error.name === 'AbortError') {
62
- return new ExecTimeoutError(command, args, timeout, error);
63
+ return new ExecTimeoutError(command, args, timeout, error, output);
63
64
  }
64
65
  return error;
65
66
  }
67
+ /**
68
+ * Snapshot of what two collectors hold, for carrying onto a timeout
69
+ * rejection. Without it a `git add -A` killed at its budget rejected with
70
+ * nothing but the budget: the partial stdout/stderr that said what git was
71
+ * doing when it died was already collected and simply dropped.
72
+ */
73
+ function collectedOutput(out, err) {
74
+ return {
75
+ stdout: out.getText(),
76
+ stderr: err.getText(),
77
+ truncated: out.wasTruncated() || err.wasTruncated(),
78
+ };
79
+ }
66
80
  function exitCodeFromClose(code, signal) {
67
81
  if (code !== null) {
68
82
  return code;
@@ -95,7 +109,7 @@ export async function exec(command, args, options = {}) {
95
109
  child.stdout.on('data', out.onData);
96
110
  child.stderr.on('data', err.onData);
97
111
  child.on('error', (error) => {
98
- reject(toExecRejection(error, command, args, options.timeout));
112
+ reject(toExecRejection(error, command, args, options.timeout, collectedOutput(out, err)));
99
113
  });
100
114
  child.on('close', (code, signal) => {
101
115
  resolve({
@@ -109,48 +123,48 @@ export async function exec(command, args, options = {}) {
109
123
  });
110
124
  }
111
125
  /**
112
- * Executes a command and streams its output.
113
- * @param command - Command to execute
114
- * @param args - Command arguments
115
- * @param options - Execution options
116
- * @returns Exit code of the process
126
+ * The shared body of every spawn wrapper that outlives its call: detach
127
+ * decision, shutdown forwarding, closure tracking, process-group sweep and
128
+ * typed timeout rejection.
129
+ *
130
+ * `execStream`, `execInherit` and `execInheritCapture` each hand-rolled this
131
+ * sequence, and the copies drifted: `execStream` was the one that omitted
132
+ * {@link trackChildClosure}, so the bin signal handler did not wait for
133
+ * `runMachStream`'s children on Ctrl+C and the forwarder's SIGTERM → grace →
134
+ * SIGKILL escalation could not finish before the parent exited. That was the
135
+ * exact failure the tracking set was introduced to fix, left unwired in one
136
+ * copy. Keeping the sequence in one place is what stops that recurring.
137
+ *
138
+ * `exec` deliberately stays outside: it neither forwards signals nor tracks
139
+ * closure, because it is for short-lived helpers that no one Ctrl+Cs through.
140
+ * `execSmokeRun` also stays outside: its deadline owns the SIGTERM → grace →
141
+ * SIGKILL escalation directly, so it shares no forwarder with the others.
142
+ *
143
+ * @param spec - The variant's stdio, stream wiring, grace budget and result shape
144
+ * @returns Whatever `spec.result` builds from the child's `close` event
117
145
  */
118
- export async function execStream(command, args, options = {}) {
146
+ async function spawnTracked(spec) {
147
+ const { command, args, options } = spec;
119
148
  return new Promise((resolve, reject) => {
120
149
  const usesProcessGroup = options.processGroup === true && process.platform !== 'win32';
121
- const child = spawn(command, args, {
150
+ const child = spec.spawnChild({
122
151
  cwd: options.cwd,
123
152
  env: buildChildEnv(options),
124
- stdio: ['ignore', 'pipe', 'pipe'],
125
153
  signal: buildSignalFromTimeout(options.timeout),
126
154
  detached: usesProcessGroup,
127
155
  });
128
156
  const groupPid = usesProcessGroup ? child.pid : undefined;
129
- const stdoutDecoder = new StringDecoder('utf8');
130
- const stderrDecoder = new StringDecoder('utf8');
131
- child.stdout.on('data', (data) => {
132
- const chunk = stdoutDecoder.write(data);
133
- if (chunk.length > 0)
134
- options.onStdout?.(chunk);
135
- });
136
- child.stderr.on('data', (data) => {
137
- const chunk = stderrDecoder.write(data);
138
- if (chunk.length > 0)
139
- options.onStderr?.(chunk);
140
- });
141
- // A detached group leader no longer receives the terminal's Ctrl+C, so
142
- // the forwarder is mandatory (not just graceful-UX) when processGroup
143
- // is set. Group-aware: kills route to the whole tree.
144
- const forwarder = options.processGroup === true
145
- ? installGracefulShutdownForwarder(child, 1500, (signal) => {
157
+ spec.attach?.(child);
158
+ // Group-aware kills route to the whole tree. A non-group child is killed
159
+ // directly by the forwarder's own `child.kill`.
160
+ const killTarget = options.processGroup === true
161
+ ? (signal) => {
146
162
  killProcessTree(child, signal, usesProcessGroup);
147
- })
163
+ }
164
+ : undefined;
165
+ const forwarder = spec.forwardWithoutProcessGroup || options.processGroup === true
166
+ ? installGracefulShutdownForwarder(child, spec.graceMs, killTarget)
148
167
  : undefined;
149
- // Closure tracking: this was the one wrapper that omitted it, so the bin
150
- // signal handler did not wait for `runMachStream`'s children on Ctrl+C
151
- // and the forwarder's SIGTERM → grace → SIGKILL escalation above could
152
- // not finish before the parent exited — the exact failure the tracking
153
- // set was introduced to fix, left unwired here.
154
168
  const closure = trackChildClosure();
155
169
  child.on('error', (error) => {
156
170
  // Abort/startup failure: make sure a partially-started tree does not
@@ -161,13 +175,13 @@ export async function execStream(command, args, options = {}) {
161
175
  }
162
176
  forwarder?.dispose();
163
177
  closure.settle();
164
- reject(toExecRejection(error, command, args, options.timeout));
178
+ reject(toExecRejection(error, command, args, options.timeout, spec.rejectionOutput?.()));
165
179
  });
166
180
  child.on('close', (code, signal) => {
167
181
  forwarder?.dispose();
168
182
  const finish = () => {
169
183
  closure.settle();
170
- resolve(exitCodeFromClose(code, signal));
184
+ resolve(spec.result(code, signal));
171
185
  };
172
186
  if (groupPid !== undefined) {
173
187
  sweepProcessGroup(groupPid).then(finish, finish);
@@ -178,6 +192,41 @@ export async function execStream(command, args, options = {}) {
178
192
  });
179
193
  });
180
194
  }
195
+ /**
196
+ * Executes a command and streams its output.
197
+ * @param command - Command to execute
198
+ * @param args - Command arguments
199
+ * @param options - Execution options
200
+ * @returns Exit code of the process
201
+ */
202
+ export async function execStream(command, args, options = {}) {
203
+ return spawnTracked({
204
+ command,
205
+ args,
206
+ options,
207
+ graceMs: 1500,
208
+ // A detached group leader no longer receives the terminal's Ctrl+C, so
209
+ // the forwarder is mandatory (not just graceful-UX) when processGroup
210
+ // is set. A plain streamed child does not get one.
211
+ forwardWithoutProcessGroup: false,
212
+ spawnChild: (base) => spawn(command, args, { ...base, stdio: ['ignore', 'pipe', 'pipe'] }),
213
+ attach: (child) => {
214
+ const stdoutDecoder = new StringDecoder('utf8');
215
+ const stderrDecoder = new StringDecoder('utf8');
216
+ child.stdout.on('data', (data) => {
217
+ const chunk = stdoutDecoder.write(data);
218
+ if (chunk.length > 0)
219
+ options.onStdout?.(chunk);
220
+ });
221
+ child.stderr.on('data', (data) => {
222
+ const chunk = stderrDecoder.write(data);
223
+ if (chunk.length > 0)
224
+ options.onStderr?.(chunk);
225
+ });
226
+ },
227
+ result: exitCodeFromClose,
228
+ });
229
+ }
181
230
  /**
182
231
  * Close-promises of children whose shutdown the CLI must wait for when a
183
232
  * termination signal arrives. A bin signal handler that calls `process.exit`
@@ -194,7 +243,7 @@ const activeChildClosures = new Set();
194
243
  * Exported for the one legitimate spawn outside this module:
195
244
  * `core/marionette-preflight.ts` keeps its own `spawn` because it needs the
196
245
  * live `ChildProcess` mid-run, returns while the child is still running, and
197
- * aborts on a TCP byte rather than a deadline — none of which any wrapper
246
+ * aborts on a TCP byte rather than a deadline, none of which any wrapper
198
247
  * here can express. It still needs the shutdown contract, and a second
199
248
  * hand-rolled copy is what lets a Ctrl+C orphan the whole mach → Firefox
200
249
  * tree.
@@ -203,7 +252,7 @@ const activeChildClosures = new Set();
203
252
  * Registers a child whose shutdown {@link waitForActiveChildShutdown} must
204
253
  * wait for, and returns the handle that deregisters it.
205
254
  *
206
- * @returns `settle()` — call from both the child's `close` and `error`
255
+ * @returns `settle()`. Call from both the child's `close` and `error`
207
256
  * handlers so a dead child never holds the shutdown wait open
208
257
  */
209
258
  export function trackChildClosure() {
@@ -242,7 +291,7 @@ export async function waitForActiveChildShutdown(timeoutMs) {
242
291
  * Graceful shutdown: when the FireForge process receives SIGINT/SIGTERM, the
243
292
  * signal is forwarded to the child as SIGTERM and a short grace timer (default
244
293
  * 1500ms) runs before escalating to SIGKILL. A second matching signal during
245
- * the grace period triggers an immediate SIGKILL — matching the usual
294
+ * the grace period triggers an immediate SIGKILL, matching the usual
246
295
  * "hit Ctrl-C again to force-quit" UX. Without this, Firefox's AsyncShutdown
247
296
  * / profileBeforeChange blockers (which flush in-memory state to disk) can be
248
297
  * racing the OS child-exit path, losing the last few seconds of edits.
@@ -253,51 +302,21 @@ export async function waitForActiveChildShutdown(timeoutMs) {
253
302
  * @returns Exit code of the process
254
303
  */
255
304
  export async function execInherit(command, args, options = {}) {
256
- return new Promise((resolve, reject) => {
257
- const usesProcessGroup = options.processGroup === true && process.platform !== 'win32';
258
- const child = spawn(command, args, {
259
- cwd: options.cwd,
260
- env: buildChildEnv(options),
261
- stdio: 'inherit',
262
- signal: buildSignalFromTimeout(options.timeout),
263
- detached: usesProcessGroup,
264
- });
265
- const groupPid = usesProcessGroup ? child.pid : undefined;
266
- const graceMs = options.shutdownGraceMs ?? 1500;
267
- const { dispose } = installGracefulShutdownForwarder(child, graceMs, options.processGroup === true
268
- ? (signal) => {
269
- killProcessTree(child, signal, usesProcessGroup);
270
- }
271
- : undefined);
272
- const closure = trackChildClosure();
273
- child.on('error', (error) => {
274
- if (options.processGroup === true) {
275
- killProcessTree(child, 'SIGKILL', usesProcessGroup);
276
- }
277
- dispose();
278
- closure.settle();
279
- reject(toExecRejection(error, command, args, options.timeout));
280
- });
281
- child.on('close', (code, signal) => {
282
- dispose();
283
- const finish = () => {
284
- closure.settle();
285
- resolve(exitCodeFromClose(code, signal));
286
- };
287
- if (groupPid !== undefined) {
288
- sweepProcessGroup(groupPid).then(finish, finish);
289
- }
290
- else {
291
- finish();
292
- }
293
- });
305
+ return spawnTracked({
306
+ command,
307
+ args,
308
+ options,
309
+ graceMs: options.shutdownGraceMs ?? 1500,
310
+ forwardWithoutProcessGroup: true,
311
+ spawnChild: (base) => spawn(command, args, { ...base, stdio: 'inherit' }),
312
+ result: exitCodeFromClose,
294
313
  });
295
314
  }
296
315
  /**
297
316
  * Wires parent-process SIGINT/SIGTERM to a child: first signal → child.kill
298
- * (SIGTERM) + grace timer; second matching signal → immediate SIGKILL; grace
299
- * timer expiry → SIGKILL. Returns a `dispose()` that clears the listeners and
300
- * any outstanding timer. Callers must invoke `dispose()` from both the child's
317
+ * (SIGTERM) + grace timer. A second matching signal → immediate SIGKILL.
318
+ * Grace timer expiry → SIGKILL. Returns a `dispose()` that clears the
319
+ * listeners and any outstanding timer. Callers must invoke `dispose()` from both the child's
301
320
  * `close` and `error` handlers so the process does not accumulate signal
302
321
  * listeners across repeated spawns.
303
322
  */
@@ -318,7 +337,7 @@ export function installGracefulShutdownForwarder(child, graceMs, killTarget) {
318
337
  sendKill('SIGKILL');
319
338
  }
320
339
  catch {
321
- // Child is already gone — nothing to do.
340
+ // Child is already gone, nothing to do.
322
341
  }
323
342
  };
324
343
  const handleSignal = (signal) => {
@@ -365,54 +384,27 @@ export function installGracefulShutdownForwarder(child, graceMs, killTarget) {
365
384
  * @returns Execution result with stdout, stderr, and exit code
366
385
  */
367
386
  export async function execInheritCapture(command, args, options = {}) {
368
- return new Promise((resolve, reject) => {
369
- const usesProcessGroup = options.processGroup === true && process.platform !== 'win32';
370
- const child = spawn(command, args, {
371
- cwd: options.cwd,
372
- env: buildChildEnv(options),
373
- stdio: ['inherit', 'pipe', 'pipe'],
374
- signal: buildSignalFromTimeout(options.timeout),
375
- detached: usesProcessGroup,
376
- });
377
- const groupPid = usesProcessGroup ? child.pid : undefined;
378
- const out = createStreamCollector(options.mirror?.stdout ?? process.stdout);
379
- const err = createStreamCollector(options.mirror?.stderr ?? process.stderr);
380
- child.stdout.on('data', out.onData);
381
- child.stderr.on('data', err.onData);
382
- const graceMs = options.shutdownGraceMs ?? 1500;
383
- const { dispose } = installGracefulShutdownForwarder(child, graceMs, options.processGroup === true
384
- ? (signal) => {
385
- killProcessTree(child, signal, usesProcessGroup);
386
- }
387
- : undefined);
388
- const closure = trackChildClosure();
389
- child.on('error', (error) => {
390
- if (options.processGroup === true) {
391
- killProcessTree(child, 'SIGKILL', usesProcessGroup);
392
- }
393
- dispose();
394
- closure.settle();
395
- reject(toExecRejection(error, command, args, options.timeout));
396
- });
397
- child.on('close', (code, signal) => {
398
- dispose();
399
- const finish = () => {
400
- closure.settle();
401
- resolve({
402
- stdout: out.getText(),
403
- stderr: err.getText(),
404
- exitCode: exitCodeFromClose(code, signal),
405
- stdoutTruncated: out.wasTruncated(),
406
- stderrTruncated: err.wasTruncated(),
407
- });
408
- };
409
- if (groupPid !== undefined) {
410
- sweepProcessGroup(groupPid).then(finish, finish);
411
- }
412
- else {
413
- finish();
414
- }
415
- });
387
+ const out = createStreamCollector(options.mirror?.stdout ?? process.stdout);
388
+ const err = createStreamCollector(options.mirror?.stderr ?? process.stderr);
389
+ return spawnTracked({
390
+ command,
391
+ args,
392
+ options,
393
+ graceMs: options.shutdownGraceMs ?? 1500,
394
+ forwardWithoutProcessGroup: true,
395
+ spawnChild: (base) => spawn(command, args, { ...base, stdio: ['inherit', 'pipe', 'pipe'] }),
396
+ attach: (child) => {
397
+ child.stdout.on('data', out.onData);
398
+ child.stderr.on('data', err.onData);
399
+ },
400
+ result: (code, signal) => ({
401
+ stdout: out.getText(),
402
+ stderr: err.getText(),
403
+ exitCode: exitCodeFromClose(code, signal),
404
+ stdoutTruncated: out.wasTruncated(),
405
+ stderrTruncated: err.wasTruncated(),
406
+ }),
407
+ rejectionOutput: () => collectedOutput(out, err),
416
408
  });
417
409
  }
418
410
  /**
@@ -430,9 +422,10 @@ export async function execInheritCapture(command, args, options = {}) {
430
422
  *
431
423
  * Windows fallback: `detached: true` does not create an equivalent group
432
424
  * there, so we kill the descendant tree with `taskkill /pid <pid> /T /F`
433
- * instead. taskkill has no graceful-signal equivalent — both the deadline
434
- * and the grace re-invocation are forced kills, so Windows children get
435
- * no shutdown window (best-effort; Windows is untested, see README).
425
+ * instead. taskkill has no graceful-signal equivalent, so both the deadline
426
+ * and the grace re-invocation are forced kills and Windows children get
427
+ * no shutdown window. This is best-effort and Windows is untested, see
428
+ * README.
436
429
  * `child.kill()` is still sent as a direct-child fallback in case
437
430
  * taskkill itself is unavailable.
438
431
  */
@@ -481,8 +474,8 @@ export async function execSmokeRun(command, args, options) {
481
474
  }, options.smokeTimeoutMs);
482
475
  deadlineTimer.unref();
483
476
  // Forward parent SIGINT/SIGTERM to the whole child process group. The
484
- // smoke child is a group leader (detached), so it does NOT receive the
485
- // terminal's Ctrl+C SIGINT — without this forwarder, the parent exits
477
+ // smoke child is a group leader (detached), so it does not receive the
478
+ // terminal's Ctrl+C SIGINT. Without this forwarder, the parent exits
486
479
  // and the entire mach → firefox tree is orphaned. Second signal (or
487
480
  // grace expiry) escalates to a group SIGKILL.
488
481
  const forwardedSignals = new Set();
@@ -542,48 +535,6 @@ export async function execSmokeRun(command, args, options) {
542
535
  });
543
536
  });
544
537
  }
545
- /**
546
- * Cap on the partial-line tail kept between chunks. A child emitting one
547
- * enormous line with no terminator (minified JS dumped to stderr, a raw
548
- * binary blob) would otherwise grow the buffer without bound — the 50 MB cap
549
- * guards the collector, not these line buffers. When the tail exceeds the
550
- * cap it is dispatched as a synthetic (oversized) line so the matchers still
551
- * see its content, then the buffer resets.
552
- */
553
- const MAX_PARTIAL_LINE_SIZE = 1024 * 1024;
554
- /**
555
- * Drains complete lines from `buffer`, dispatching each to `cb`. Treats
556
- * `\n`, `\r\n`, and lone `\r` as line terminators — the lone-`\r` case
557
- * matters for progress-bar style output (mach, cargo) that repaints a line
558
- * with carriage returns and never sends a newline, which otherwise
559
- * accumulates indefinitely. A single trailing `\r` is held back since it may
560
- * be the first half of a `\r\n` pair split across chunks. Returns the
561
- * remaining partial line — callers keep accumulating into it.
562
- */
563
- function dispatchCompleteLines(buffer, cb) {
564
- let searchFrom = 0;
565
- for (;;) {
566
- const nl = buffer.indexOf('\n', searchFrom);
567
- const cr = buffer.indexOf('\r', searchFrom);
568
- const idx = nl === -1 ? cr : cr === -1 ? nl : Math.min(nl, cr);
569
- if (idx === -1)
570
- break;
571
- if (buffer[idx] === '\r' && idx === buffer.length - 1) {
572
- // Possible first half of a chunk-split \r\n — wait for the next chunk.
573
- break;
574
- }
575
- const line = buffer.slice(0, idx);
576
- const terminatorLength = buffer[idx] === '\r' && buffer[idx + 1] === '\n' ? 2 : 1;
577
- buffer = buffer.slice(idx + terminatorLength);
578
- searchFrom = 0;
579
- cb?.(line);
580
- }
581
- if (buffer.length > MAX_PARTIAL_LINE_SIZE) {
582
- cb?.(buffer);
583
- return '';
584
- }
585
- return buffer;
586
- }
587
538
  /**
588
539
  * Finds an executable in the system PATH.
589
540
  * @param name - Name of the executable
@@ -602,8 +553,7 @@ export async function findExecutable(name) {
602
553
  }
603
554
  return undefined;
604
555
  }
605
- catch (error) {
606
- void error;
556
+ catch {
607
557
  return undefined;
608
558
  }
609
559
  }
@@ -3,9 +3,9 @@
3
3
  *
4
4
  * Two different columns share one grammar, which is why this lives apart
5
5
  * from either consumer: `time=` (accumulated CPU) and `etime=` (elapsed
6
- * wall clock). Reading them with one parser is deliberate — a second
7
- * hand-rolled copy is how the two would drift, and the darwin dialect is
8
- * the half that is easy to get wrong.
6
+ * wall clock). One parser handles both: a second hand-rolled copy is how
7
+ * the two would drift, and the darwin dialect is the half that is easy to
8
+ * get wrong.
9
9
  *
10
10
  * Dialects:
11
11
  * - linux `[dd-]hh:mm:ss` (e.g. `26-03:14:12`, `03:14:12`)
@@ -13,7 +13,7 @@
13
13
  * wrapping (e.g. `38412:07.55`)
14
14
  *
15
15
  * Returns `NaN` for anything else, so callers must treat an unrecognized
16
- * shape as "unknown" rather than as zero — a browser reported as 0 seconds
16
+ * shape as "unknown" rather than as zero. A browser reported as 0 seconds
17
17
  * old would be exactly the wrong attribution.
18
18
  */
19
19
  /**
@@ -4,9 +4,9 @@
4
4
  *
5
5
  * Two different columns share one grammar, which is why this lives apart
6
6
  * from either consumer: `time=` (accumulated CPU) and `etime=` (elapsed
7
- * wall clock). Reading them with one parser is deliberate — a second
8
- * hand-rolled copy is how the two would drift, and the darwin dialect is
9
- * the half that is easy to get wrong.
7
+ * wall clock). One parser handles both: a second hand-rolled copy is how
8
+ * the two would drift, and the darwin dialect is the half that is easy to
9
+ * get wrong.
10
10
  *
11
11
  * Dialects:
12
12
  * - linux `[dd-]hh:mm:ss` (e.g. `26-03:14:12`, `03:14:12`)
@@ -14,7 +14,7 @@
14
14
  * wrapping (e.g. `38412:07.55`)
15
15
  *
16
16
  * Returns `NaN` for anything else, so callers must treat an unrecognized
17
- * shape as "unknown" rather than as zero — a browser reported as 0 seconds
17
+ * shape as "unknown" rather than as zero. A browser reported as 0 seconds
18
18
  * old would be exactly the wrong attribution.
19
19
  */
20
20
  /**
@@ -34,14 +34,14 @@ export function parsePsDuration(value) {
34
34
  }
35
35
  const parts = rest.split(':');
36
36
  if (parts.length === 3) {
37
- // hh:mm:ss (linux; seconds may not carry fractions but tolerate them)
37
+ // hh:mm:ss on linux. Seconds may not carry fractions but tolerate them.
38
38
  const [h, m, s] = parts.map(Number);
39
39
  if ([h, m, s].some((n) => Number.isNaN(n)))
40
40
  return NaN;
41
41
  return days * 86400 + (h ?? 0) * 3600 + (m ?? 0) * 60 + (s ?? 0);
42
42
  }
43
43
  if (parts.length === 2) {
44
- // mm:ss.cc (darwin — the minutes field accumulates without wrapping)
44
+ // mm:ss.cc on darwin. The minutes field accumulates without wrapping.
45
45
  const [m, s] = parts.map(Number);
46
46
  if ([m, s].some((n) => Number.isNaN(n)))
47
47
  return NaN;
@@ -58,13 +58,13 @@ export function formatPsDuration(seconds) {
58
58
  if (Number.isNaN(seconds) || seconds < 0)
59
59
  return undefined;
60
60
  if (seconds < 60)
61
- return `${String(Math.floor(seconds))}s`;
61
+ return `${Math.floor(seconds)}s`;
62
62
  if (seconds < 3600) {
63
63
  const m = Math.floor(seconds / 60);
64
64
  const s = Math.floor(seconds % 60);
65
- return `${String(m)}m${String(s).padStart(2, '0')}s`;
65
+ return `${m}m${String(s).padStart(2, '0')}s`;
66
66
  }
67
67
  const h = Math.floor(seconds / 3600);
68
68
  const m = Math.floor((seconds % 3600) / 60);
69
- return `${String(h)}h${String(m).padStart(2, '0')}m`;
69
+ return `${h}h${String(m).padStart(2, '0')}m`;
70
70
  }
@@ -9,16 +9,16 @@ export declare function hasRawCssColors(content: string): boolean;
9
9
  /**
10
10
  * Strips JS single-line and multi-line comments from source code, replacing
11
11
  * comment bytes with spaces (newlines inside block comments are kept) so
12
- * both character offsets AND line numbers are preserved. String literals are
12
+ * both character offsets and line numbers are preserved. String literals are
13
13
  * preserved intact.
14
14
  *
15
- * Uses acorn's tokenizer, which understands regex literals — a pure-regex
15
+ * Uses acorn's tokenizer, which understands regex literals. A pure-regex
16
16
  * implementation does not model them, so two adjacent slashes inside a regex
17
17
  * (the tail of `/https?:\/\//`) read as a `//` line comment and the rest of
18
18
  * the line is blanked, corrupting whatever lint pass consumes the stripped
19
19
  * text. Firefox chrome sources contain preprocessor directives (`#ifdef`)
20
- * and other constructs acorn cannot tokenize; those fall back to the legacy
21
- * regex strip, which handles strings but NOT regex literals.
20
+ * and other constructs acorn cannot tokenize. Those fall back to the legacy
21
+ * regex strip, which handles strings but not regex literals.
22
22
  */
23
23
  export declare function stripJsComments(source: string): string;
24
24
  /**
@@ -25,16 +25,16 @@ export function hasRawCssColors(content) {
25
25
  /**
26
26
  * Strips JS single-line and multi-line comments from source code, replacing
27
27
  * comment bytes with spaces (newlines inside block comments are kept) so
28
- * both character offsets AND line numbers are preserved. String literals are
28
+ * both character offsets and line numbers are preserved. String literals are
29
29
  * preserved intact.
30
30
  *
31
- * Uses acorn's tokenizer, which understands regex literals — a pure-regex
31
+ * Uses acorn's tokenizer, which understands regex literals. A pure-regex
32
32
  * implementation does not model them, so two adjacent slashes inside a regex
33
33
  * (the tail of `/https?:\/\//`) read as a `//` line comment and the rest of
34
34
  * the line is blanked, corrupting whatever lint pass consumes the stripped
35
35
  * text. Firefox chrome sources contain preprocessor directives (`#ifdef`)
36
- * and other constructs acorn cannot tokenize; those fall back to the legacy
37
- * regex strip, which handles strings but NOT regex literals.
36
+ * and other constructs acorn cannot tokenize. Those fall back to the legacy
37
+ * regex strip, which handles strings but not regex literals.
38
38
  */
39
39
  export function stripJsComments(source) {
40
40
  const comments = [];
@@ -47,7 +47,7 @@ export function stripJsComments(source) {
47
47
  comments.push({ start, end });
48
48
  },
49
49
  });
50
- // Drive the tokenizer to EOF; we only care about onComment callbacks.
50
+ // Drive the tokenizer to EOF. We only care about onComment callbacks.
51
51
  for (;;) {
52
52
  const token = t.getToken();
53
53
  if (token.type === acornTokTypes.eof)
@@ -71,8 +71,8 @@ export function stripJsComments(source) {
71
71
  }
72
72
  /**
73
73
  * Legacy pure-regex comment strip, used only when acorn cannot tokenize
74
- * the source (preprocessor directives, syntax errors). Strings survive;
75
- * regex literals are NOT modeled, so `//` inside a regex blanks the rest
74
+ * the source (preprocessor directives, syntax errors). Strings survive.
75
+ * Regex literals are not modeled, so `//` inside a regex blanks the rest
76
76
  * of the line.
77
77
  */
78
78
  function stripJsCommentsLegacy(source) {
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The single awaitable delay used across FireForge.
3
+ *
4
+ * Five modules had grown a private copy of this (`sleep`, `delay`,
5
+ * `sweepDelay`) and four more sites inlined
6
+ * `new Promise((resolve) => setTimeout(resolve, ms))`. The copies disagreed on
7
+ * the one property that actually matters (whether the pending timer holds
8
+ * the event loop open) and the disagreement was invisible at each call site.
9
+ * Both behaviours now live here, and callers state which one they need.
10
+ *
11
+ * The default is a ref'd timer, because the dangerous mistake is the other
12
+ * one: an unref'd delay awaited between a SIGTERM and its escalation lets Node
13
+ * exit mid-grace and skip the escalation entirely.
14
+ *
15
+ * Built on the global `setTimeout` rather than `node:timers/promises`: the
16
+ * suites that drive retry and grace-period logic use vitest fake timers,
17
+ * which patch the global but not the promisified module, so the tidier
18
+ * import would make every timing path untestable.
19
+ * @param ms - Milliseconds to wait.
20
+ * @param options - Pass `unref: true` when a pending delay must not keep the
21
+ * process alive (background probes, best-effort retries during shutdown).
22
+ * @returns A promise that resolves once the delay has elapsed.
23
+ */
24
+ export declare function sleep(ms: number, options?: {
25
+ unref?: boolean;
26
+ }): Promise<void>;
@@ -0,0 +1,31 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * The single awaitable delay used across FireForge.
4
+ *
5
+ * Five modules had grown a private copy of this (`sleep`, `delay`,
6
+ * `sweepDelay`) and four more sites inlined
7
+ * `new Promise((resolve) => setTimeout(resolve, ms))`. The copies disagreed on
8
+ * the one property that actually matters (whether the pending timer holds
9
+ * the event loop open) and the disagreement was invisible at each call site.
10
+ * Both behaviours now live here, and callers state which one they need.
11
+ *
12
+ * The default is a ref'd timer, because the dangerous mistake is the other
13
+ * one: an unref'd delay awaited between a SIGTERM and its escalation lets Node
14
+ * exit mid-grace and skip the escalation entirely.
15
+ *
16
+ * Built on the global `setTimeout` rather than `node:timers/promises`: the
17
+ * suites that drive retry and grace-period logic use vitest fake timers,
18
+ * which patch the global but not the promisified module, so the tidier
19
+ * import would make every timing path untestable.
20
+ * @param ms - Milliseconds to wait.
21
+ * @param options - Pass `unref: true` when a pending delay must not keep the
22
+ * process alive (background probes, best-effort retries during shutdown).
23
+ * @returns A promise that resolves once the delay has elapsed.
24
+ */
25
+ export function sleep(ms, options) {
26
+ return new Promise((resolve) => {
27
+ const timer = setTimeout(resolve, ms);
28
+ if (options?.unref === true)
29
+ timer.unref();
30
+ });
31
+ }