@hominis/fireforge 0.44.4 → 0.46.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 (378) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/dist/bin/fireforge.js +6 -0
  3. package/dist/build-info.json +4 -4
  4. package/dist/src/cli-usage-error.d.ts +46 -0
  5. package/dist/src/cli-usage-error.js +81 -0
  6. package/dist/src/cli.d.ts +5 -1
  7. package/dist/src/cli.js +20 -21
  8. package/dist/src/commands/bootstrap.js +1 -2
  9. package/dist/src/commands/build.js +15 -6
  10. package/dist/src/commands/destructive-decision.d.ts +19 -0
  11. package/dist/src/commands/destructive-decision.js +31 -0
  12. package/dist/src/commands/doctor-check-core.d.ts +0 -6
  13. package/dist/src/commands/doctor-check-core.js +0 -8
  14. package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
  15. package/dist/src/commands/doctor-orphaned-harness.js +4 -12
  16. package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
  17. package/dist/src/commands/doctor-source-pin.d.ts +29 -0
  18. package/dist/src/commands/doctor-source-pin.js +81 -0
  19. package/dist/src/commands/doctor.d.ts +2 -0
  20. package/dist/src/commands/doctor.js +9 -8
  21. package/dist/src/commands/download.js +35 -24
  22. package/dist/src/commands/export-all.js +21 -4
  23. package/dist/src/commands/export-flow.js +15 -60
  24. package/dist/src/commands/export-placement-conflicts.js +3 -3
  25. package/dist/src/commands/export-placement-gate.js +2 -8
  26. package/dist/src/commands/export-placement-policy.js +3 -5
  27. package/dist/src/commands/export-shared.d.ts +42 -23
  28. package/dist/src/commands/export-shared.js +4 -19
  29. package/dist/src/commands/export.js +10 -1
  30. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  31. package/dist/src/commands/furnace/authoring-config.js +19 -0
  32. package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
  33. package/dist/src/commands/furnace/create-browser-test.js +3 -8
  34. package/dist/src/commands/furnace/create-dry-run.js +2 -5
  35. package/dist/src/commands/furnace/create-templates.d.ts +17 -1
  36. package/dist/src/commands/furnace/create-templates.js +2 -1
  37. package/dist/src/commands/furnace/create.js +25 -19
  38. package/dist/src/commands/furnace/deploy.js +11 -2
  39. package/dist/src/commands/furnace/init.js +2 -1
  40. package/dist/src/commands/furnace/override.js +28 -19
  41. package/dist/src/commands/furnace/refresh.js +9 -1
  42. package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
  43. package/dist/src/commands/furnace/remove-mutations.js +0 -1
  44. package/dist/src/commands/furnace/remove.js +2 -6
  45. package/dist/src/commands/furnace/rename-browser-test.js +1 -7
  46. package/dist/src/commands/furnace/rename.js +29 -29
  47. package/dist/src/commands/furnace/status.js +12 -9
  48. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  49. package/dist/src/commands/furnace/test-file-name.js +39 -0
  50. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  51. package/dist/src/commands/furnace/validation-output.js +3 -8
  52. package/dist/src/commands/import.js +2 -9
  53. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  54. package/dist/src/commands/lint-per-patch.js +19 -4
  55. package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
  56. package/dist/src/commands/lint.d.ts +2 -1
  57. package/dist/src/commands/lint.js +1 -1
  58. package/dist/src/commands/manifest.js +1 -1
  59. package/dist/src/commands/package.js +2 -4
  60. package/dist/src/commands/patch/compact.js +14 -15
  61. package/dist/src/commands/patch/delete.js +15 -25
  62. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  63. package/dist/src/commands/patch/entry-projection.js +24 -0
  64. package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
  65. package/dist/src/commands/patch/lint-ignore.js +19 -29
  66. package/dist/src/commands/patch/move-files-create.js +24 -10
  67. package/dist/src/commands/patch/move-files-into.js +38 -37
  68. package/dist/src/commands/patch/move-files.js +4 -13
  69. package/dist/src/commands/patch/rename.js +27 -41
  70. package/dist/src/commands/patch/reorder.js +25 -38
  71. package/dist/src/commands/patch/split-plan.d.ts +4 -5
  72. package/dist/src/commands/patch/split-plan.js +4 -11
  73. package/dist/src/commands/patch/split.js +45 -43
  74. package/dist/src/commands/patch/staged-dependency.js +16 -22
  75. package/dist/src/commands/patch/tier.js +15 -25
  76. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  77. package/dist/src/commands/patch-rename-map.js +36 -0
  78. package/dist/src/commands/pending-resolution.d.ts +15 -0
  79. package/dist/src/commands/pending-resolution.js +25 -0
  80. package/dist/src/commands/re-export-adjacent.d.ts +32 -2
  81. package/dist/src/commands/re-export-adjacent.js +51 -10
  82. package/dist/src/commands/re-export-bulk-scan.js +6 -10
  83. package/dist/src/commands/re-export-drift.d.ts +8 -0
  84. package/dist/src/commands/re-export-drift.js +65 -4
  85. package/dist/src/commands/re-export-files.d.ts +2 -3
  86. package/dist/src/commands/re-export-files.js +38 -32
  87. package/dist/src/commands/re-export-lint.d.ts +3 -4
  88. package/dist/src/commands/re-export-lint.js +9 -2
  89. package/dist/src/commands/re-export-options.js +19 -5
  90. package/dist/src/commands/re-export-register.js +5 -1
  91. package/dist/src/commands/re-export-scan.js +8 -8
  92. package/dist/src/commands/re-export-single.d.ts +22 -3
  93. package/dist/src/commands/re-export-single.js +19 -11
  94. package/dist/src/commands/re-export.js +44 -27
  95. package/dist/src/commands/rebase/abort.js +3 -8
  96. package/dist/src/commands/rebase/continue.js +12 -12
  97. package/dist/src/commands/rebase/index.js +2 -2
  98. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  99. package/dist/src/commands/rebase/patch-loop.js +2 -7
  100. package/dist/src/commands/resolve.js +18 -10
  101. package/dist/src/commands/run.d.ts +0 -13
  102. package/dist/src/commands/run.js +12 -25
  103. package/dist/src/commands/setup-support.d.ts +16 -4
  104. package/dist/src/commands/setup-support.js +16 -16
  105. package/dist/src/commands/setup.js +3 -9
  106. package/dist/src/commands/source.js +8 -1
  107. package/dist/src/commands/status-check.js +2 -2
  108. package/dist/src/commands/status-output.js +2 -2
  109. package/dist/src/commands/status.js +3 -4
  110. package/dist/src/commands/test-build-phase.d.ts +2 -2
  111. package/dist/src/commands/test-build-phase.js +33 -8
  112. package/dist/src/commands/test-diagnose.js +14 -5
  113. package/dist/src/commands/test-modes.d.ts +2 -2
  114. package/dist/src/commands/test-modes.js +0 -1
  115. package/dist/src/commands/test-register.js +6 -3
  116. package/dist/src/commands/test-run.js +29 -21
  117. package/dist/src/commands/test-stale-gate.js +8 -5
  118. package/dist/src/commands/test-verdict.d.ts +16 -2
  119. package/dist/src/commands/test-verdict.js +18 -3
  120. package/dist/src/commands/test.js +51 -7
  121. package/dist/src/commands/token-list.d.ts +26 -0
  122. package/dist/src/commands/token-list.js +153 -0
  123. package/dist/src/commands/token.js +17 -1
  124. package/dist/src/commands/tree.js +7 -2
  125. package/dist/src/commands/typecheck.js +9 -9
  126. package/dist/src/commands/wire.js +4 -3
  127. package/dist/src/core/branding.d.ts +0 -7
  128. package/dist/src/core/branding.js +3 -2
  129. package/dist/src/core/browser-wire.js +6 -3
  130. package/dist/src/core/build-audit-platform.d.ts +29 -0
  131. package/dist/src/core/build-audit-platform.js +140 -0
  132. package/dist/src/core/build-audit-registration.js +3 -2
  133. package/dist/src/core/build-audit-skip.d.ts +57 -0
  134. package/dist/src/core/build-audit-skip.js +112 -0
  135. package/dist/src/core/build-audit.d.ts +15 -2
  136. package/dist/src/core/build-audit.js +154 -12
  137. package/dist/src/core/build-baseline.d.ts +45 -28
  138. package/dist/src/core/build-baseline.js +6 -29
  139. package/dist/src/core/build-jar-escalation.d.ts +64 -0
  140. package/dist/src/core/build-jar-escalation.js +128 -0
  141. package/dist/src/core/build-overwrite-guard.js +1 -1
  142. package/dist/src/core/build-prepare.d.ts +5 -2
  143. package/dist/src/core/build-prepare.js +34 -6
  144. package/dist/src/core/config-paths.d.ts +2 -6
  145. package/dist/src/core/config-paths.js +4 -2
  146. package/dist/src/core/config-state.d.ts +0 -6
  147. package/dist/src/core/config-state.js +1 -19
  148. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  149. package/dist/src/core/config-validate-build-audit.js +80 -0
  150. package/dist/src/core/config-validate-file-size.d.ts +12 -0
  151. package/dist/src/core/config-validate-file-size.js +65 -0
  152. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  153. package/dist/src/core/config-validate-patch-policy.js +3 -3
  154. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  155. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  156. package/dist/src/core/config-validate.js +20 -3
  157. package/dist/src/core/config.d.ts +2 -2
  158. package/dist/src/core/config.js +11 -15
  159. package/dist/src/core/coverage-extend.js +4 -4
  160. package/dist/src/core/discard-baseline.js +7 -7
  161. package/dist/src/core/engine-precondition.d.ts +1 -9
  162. package/dist/src/core/engine-precondition.js +3 -8
  163. package/dist/src/core/engine-session-lock.js +13 -17
  164. package/dist/src/core/export-moved-code-shape.js +2 -2
  165. package/dist/src/core/file-lock-owner.d.ts +103 -0
  166. package/dist/src/core/file-lock-owner.js +259 -0
  167. package/dist/src/core/file-lock.d.ts +2 -7
  168. package/dist/src/core/file-lock.js +40 -91
  169. package/dist/src/core/firefox-cache.d.ts +13 -1
  170. package/dist/src/core/firefox-cache.js +59 -34
  171. package/dist/src/core/firefox-download.d.ts +15 -0
  172. package/dist/src/core/firefox-download.js +37 -21
  173. package/dist/src/core/firefox-extract.d.ts +6 -5
  174. package/dist/src/core/firefox-extract.js +36 -18
  175. package/dist/src/core/firefox.d.ts +30 -32
  176. package/dist/src/core/firefox.js +5 -36
  177. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  178. package/dist/src/core/furnace-apply-dry-run.js +1 -7
  179. package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
  180. package/dist/src/core/furnace-apply-helpers.js +8 -19
  181. package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
  182. package/dist/src/core/furnace-apply.d.ts +1 -1
  183. package/dist/src/core/furnace-apply.js +30 -10
  184. package/dist/src/core/furnace-config.d.ts +1 -1
  185. package/dist/src/core/furnace-config.js +23 -36
  186. package/dist/src/core/furnace-constants.js +3 -2
  187. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  188. package/dist/src/core/furnace-dir-entry.js +21 -0
  189. package/dist/src/core/furnace-operation.d.ts +2 -0
  190. package/dist/src/core/furnace-operation.js +3 -1
  191. package/dist/src/core/furnace-refresh.d.ts +18 -6
  192. package/dist/src/core/furnace-refresh.js +3 -6
  193. package/dist/src/core/furnace-registration-ast.d.ts +0 -1
  194. package/dist/src/core/furnace-registration-ast.js +0 -3
  195. package/dist/src/core/furnace-registration.d.ts +2 -1
  196. package/dist/src/core/furnace-registration.js +22 -29
  197. package/dist/src/core/furnace-scanner.js +3 -6
  198. package/dist/src/core/furnace-state-persist.d.ts +2 -2
  199. package/dist/src/core/furnace-validate-registration.js +5 -7
  200. package/dist/src/core/git-base.d.ts +3 -1
  201. package/dist/src/core/git-base.js +25 -3
  202. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  203. package/dist/src/core/git-diff-new-file.js +119 -0
  204. package/dist/src/core/git-diff.d.ts +9 -9
  205. package/dist/src/core/git-diff.js +88 -157
  206. package/dist/src/core/git-file-ops.d.ts +0 -7
  207. package/dist/src/core/git-file-ops.js +4 -28
  208. package/dist/src/core/git-readonly-index.d.ts +0 -2
  209. package/dist/src/core/git-readonly-index.js +0 -4
  210. package/dist/src/core/git-status.d.ts +7 -0
  211. package/dist/src/core/git-status.js +21 -9
  212. package/dist/src/core/git.d.ts +0 -6
  213. package/dist/src/core/git.js +14 -43
  214. package/dist/src/core/harness-orphans.d.ts +73 -0
  215. package/dist/src/core/harness-orphans.js +187 -0
  216. package/dist/src/core/history-log.d.ts +19 -0
  217. package/dist/src/core/history-log.js +29 -0
  218. package/dist/src/core/mach-error-hints.d.ts +0 -5
  219. package/dist/src/core/mach-error-hints.js +1 -1
  220. package/dist/src/core/mach-known-noise-filter.d.ts +25 -3
  221. package/dist/src/core/mach-known-noise-filter.js +31 -10
  222. package/dist/src/core/mach-mozconfig.js +2 -1
  223. package/dist/src/core/mach-python.d.ts +2 -0
  224. package/dist/src/core/mach-python.js +12 -7
  225. package/dist/src/core/mach-resource-shim.d.ts +13 -1
  226. package/dist/src/core/mach-resource-shim.js +112 -9
  227. package/dist/src/core/mach.d.ts +46 -53
  228. package/dist/src/core/mach.js +31 -83
  229. package/dist/src/core/marionette-port.d.ts +0 -17
  230. package/dist/src/core/marionette-port.js +12 -58
  231. package/dist/src/core/marionette-preflight.js +9 -27
  232. package/dist/src/core/mochitest-server-port.js +3 -3
  233. package/dist/src/core/moz-manifest-rules.js +37 -33
  234. package/dist/src/core/patch-apply-fuzz.js +4 -4
  235. package/dist/src/core/patch-apply.d.ts +4 -5
  236. package/dist/src/core/patch-apply.js +55 -32
  237. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  238. package/dist/src/core/patch-export-coverage.js +1 -30
  239. package/dist/src/core/patch-export-update.d.ts +18 -1
  240. package/dist/src/core/patch-export-update.js +4 -3
  241. package/dist/src/core/patch-export.d.ts +11 -21
  242. package/dist/src/core/patch-export.js +44 -66
  243. package/dist/src/core/patch-files.d.ts +0 -4
  244. package/dist/src/core/patch-files.js +0 -11
  245. package/dist/src/core/patch-lint-binary.d.ts +6 -12
  246. package/dist/src/core/patch-lint-cache.d.ts +17 -1
  247. package/dist/src/core/patch-lint-cache.js +3 -6
  248. package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
  249. package/dist/src/core/patch-lint-checkjs.js +43 -25
  250. package/dist/src/core/patch-lint-creators.d.ts +2 -6
  251. package/dist/src/core/patch-lint-cross.d.ts +16 -8
  252. package/dist/src/core/patch-lint-cross.js +49 -92
  253. package/dist/src/core/patch-lint-css.js +10 -6
  254. package/dist/src/core/patch-lint-file-size.d.ts +52 -0
  255. package/dist/src/core/patch-lint-file-size.js +53 -0
  256. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  257. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  258. package/dist/src/core/patch-lint-jsdoc.js +36 -36
  259. package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
  260. package/dist/src/core/patch-lint-prettier.d.ts +36 -0
  261. package/dist/src/core/patch-lint-prettier.js +126 -0
  262. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  263. package/dist/src/core/patch-lint-queue-types.js +12 -0
  264. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  265. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  266. package/dist/src/core/patch-lint-unmanaged-companion.d.ts +106 -0
  267. package/dist/src/core/patch-lint-unmanaged-companion.js +214 -0
  268. package/dist/src/core/patch-lint.d.ts +14 -9
  269. package/dist/src/core/patch-lint.js +27 -27
  270. package/dist/src/core/patch-lock.js +3 -3
  271. package/dist/src/core/patch-manifest-io.js +8 -7
  272. package/dist/src/core/patch-parse.d.ts +19 -8
  273. package/dist/src/core/patch-parse.js +25 -0
  274. package/dist/src/core/patch-policy.js +15 -27
  275. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  276. package/dist/src/core/patch-source-metadata.js +0 -4
  277. package/dist/src/core/patch-transform.d.ts +21 -8
  278. package/dist/src/core/patch-transform.js +38 -10
  279. package/dist/src/core/process-cpu-time.js +1 -1
  280. package/dist/src/core/rebase-session.d.ts +2 -8
  281. package/dist/src/core/rebase-session.js +5 -17
  282. package/dist/src/core/register-browser-content.js +3 -2
  283. package/dist/src/core/register-browser-test.js +2 -1
  284. package/dist/src/core/register-module.js +2 -1
  285. package/dist/src/core/register-scaffold.js +5 -4
  286. package/dist/src/core/register-shared-css.js +2 -1
  287. package/dist/src/core/run-log-redact.d.ts +38 -0
  288. package/dist/src/core/run-log-redact.js +69 -0
  289. package/dist/src/core/run-log.d.ts +13 -2
  290. package/dist/src/core/run-log.js +67 -18
  291. package/dist/src/core/signal-critical.js +2 -4
  292. package/dist/src/core/smoke-patterns.d.ts +0 -1
  293. package/dist/src/core/smoke-patterns.js +18 -11
  294. package/dist/src/core/state-file.d.ts +12 -1
  295. package/dist/src/core/state-file.js +14 -1
  296. package/dist/src/core/status-classify.js +2 -1
  297. package/dist/src/core/test-changed-prefs.js +26 -11
  298. package/dist/src/core/test-harness-crash.js +61 -48
  299. package/dist/src/core/test-harness-verdict-notes.d.ts +55 -0
  300. package/dist/src/core/test-harness-verdict-notes.js +48 -0
  301. package/dist/src/core/test-stale-check.d.ts +23 -8
  302. package/dist/src/core/test-stale-check.js +77 -4
  303. package/dist/src/core/test-stall-triage.js +37 -19
  304. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  305. package/dist/src/core/test-xpcshell-retry.js +14 -11
  306. package/dist/src/core/token-category.d.ts +33 -0
  307. package/dist/src/core/token-category.js +56 -48
  308. package/dist/src/core/token-dark-mode.js +2 -14
  309. package/dist/src/core/token-inventory.d.ts +56 -0
  310. package/dist/src/core/token-inventory.js +109 -0
  311. package/dist/src/core/token-manager.d.ts +1 -1
  312. package/dist/src/core/token-manager.js +1 -1
  313. package/dist/src/core/token-variant.js +21 -22
  314. package/dist/src/core/toolchain-preflight.js +1 -1
  315. package/dist/src/core/tree-cow.d.ts +0 -2
  316. package/dist/src/core/tree-cow.js +2 -2
  317. package/dist/src/core/tree-store.js +4 -5
  318. package/dist/src/core/typecheck.js +2 -2
  319. package/dist/src/core/wire-dom-fragment.js +6 -17
  320. package/dist/src/core/wire-utils.js +5 -17
  321. package/dist/src/core/xpcshell-appdir.js +2 -4
  322. package/dist/src/errors/base.d.ts +53 -1
  323. package/dist/src/errors/base.js +79 -11
  324. package/dist/src/errors/build.d.ts +28 -0
  325. package/dist/src/errors/build.js +51 -10
  326. package/dist/src/errors/config.d.ts +0 -14
  327. package/dist/src/errors/config.js +13 -34
  328. package/dist/src/errors/download.d.ts +13 -0
  329. package/dist/src/errors/download.js +53 -24
  330. package/dist/src/errors/furnace.js +8 -9
  331. package/dist/src/errors/git.d.ts +8 -8
  332. package/dist/src/errors/git.js +20 -30
  333. package/dist/src/errors/patch.js +8 -10
  334. package/dist/src/errors/rebase.js +7 -7
  335. package/dist/src/types/commands/options.d.ts +96 -123
  336. package/dist/src/types/commands/patches.d.ts +17 -6
  337. package/dist/src/types/config.d.ts +75 -2
  338. package/dist/src/types/index.d.ts +0 -1
  339. package/dist/src/utils/assert.d.ts +3 -15
  340. package/dist/src/utils/assert.js +1 -18
  341. package/dist/src/utils/build-info.d.ts +1 -1
  342. package/dist/src/utils/child-env.d.ts +2 -1
  343. package/dist/src/utils/elapsed.js +10 -1
  344. package/dist/src/utils/fs.d.ts +4 -2
  345. package/dist/src/utils/fs.js +11 -14
  346. package/dist/src/utils/hash.d.ts +12 -0
  347. package/dist/src/utils/hash.js +16 -0
  348. package/dist/src/utils/line-dispatch.d.ts +15 -0
  349. package/dist/src/utils/line-dispatch.js +48 -0
  350. package/dist/src/utils/options.d.ts +1 -7
  351. package/dist/src/utils/options.js +6 -7
  352. package/dist/src/utils/package-root.js +3 -12
  353. package/dist/src/utils/platform.d.ts +0 -4
  354. package/dist/src/utils/process-group.d.ts +2 -1
  355. package/dist/src/utils/process-group.js +20 -19
  356. package/dist/src/utils/process.d.ts +40 -26
  357. package/dist/src/utils/process.js +115 -166
  358. package/dist/src/utils/ps-duration.js +3 -3
  359. package/dist/src/utils/sleep.d.ts +26 -0
  360. package/dist/src/utils/sleep.js +31 -0
  361. package/dist/src/utils/validation.d.ts +2 -38
  362. package/dist/src/utils/validation.js +7 -16
  363. package/docs/configuration.md +136 -1
  364. package/docs/exit-codes.md +34 -20
  365. package/docs/lifecycle-invariants.md +18 -12
  366. package/docs/machine-output.md +31 -8
  367. package/docs/patch-workflow.md +2 -1
  368. package/docs/run-logs.md +44 -4
  369. package/docs/testing.md +70 -11
  370. package/package.json +8 -7
  371. package/dist/src/commands/rebase.d.ts +0 -4
  372. package/dist/src/commands/rebase.js +0 -5
  373. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  374. package/dist/src/core/patch-artifact-normalize.js +0 -24
  375. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  376. package/dist/src/core/patch-lint-reexports.js +0 -8
  377. package/dist/src/core/wire-targets.d.ts +0 -7
  378. package/dist/src/core/wire-targets.js +0 -8
@@ -8,6 +8,8 @@ import { parseObject } from '../utils/parse.js';
8
8
  import { isContainedRelativePath, isExplicitAbsolutePath } from '../utils/paths.js';
9
9
  import { describeProductVersionIncompatibility, FIREFOX_PRODUCTS, isObject, isValidAppId, isValidFirefoxCandidate, isValidFirefoxProduct, isValidFirefoxVersion, isValidProjectLicense, PROJECT_LICENSES, } from '../utils/validation.js';
10
10
  import { SUPPORTED_CONFIG_ROOT_KEYS } from './config-paths.js';
11
+ import { parseBuildAuditConfig } from './config-validate-build-audit.js';
12
+ import { parsePatchLintFileSizeThresholds } from './config-validate-file-size.js';
11
13
  import { parsePatchPolicyBlock } from './config-validate-patch-policy.js';
12
14
  import { parseExternalToolchainsBlock, parseTestBlock } from './config-validate-test-toolchains.js';
13
15
  /**
@@ -53,7 +55,8 @@ function parseIdentityFields(rec) {
53
55
  /**
54
56
  * Parses and validates the required `firefox` block: version shape,
55
57
  * product allowlist, product/version cross-compatibility, and the
56
- * optional sha256 digest (normalized to lowercase).
58
+ * optional sha256 digest (normalized to lowercase), optional
59
+ * `allowUnverifiedDownload` opt-out, optional candidate directory.
57
60
  */
58
61
  function parseFirefoxBlock(rec) {
59
62
  let firefoxRec;
@@ -84,6 +87,10 @@ function parseFirefoxBlock(rec) {
84
87
  if (firefoxSha256 !== undefined && !/^[a-f0-9]{64}$/i.test(firefoxSha256)) {
85
88
  throw new ConfigError('Config field "firefox.sha256" must be a 64-character SHA-256 hex digest');
86
89
  }
90
+ const allowUnverifiedDownload = firefoxRec.raw('allowUnverifiedDownload');
91
+ if (allowUnverifiedDownload !== undefined && typeof allowUnverifiedDownload !== 'boolean') {
92
+ throw new ConfigError('Config field "firefox.allowUnverifiedDownload" must be a boolean');
93
+ }
87
94
  const firefoxCandidate = optionalConfigString(firefoxRec, 'candidate', 'firefox.candidate');
88
95
  if (firefoxCandidate !== undefined && !isValidFirefoxCandidate(firefoxCandidate)) {
89
96
  throw new ConfigError('Config field "firefox.candidate" must look like "buildN" (e.g. "build2")');
@@ -92,6 +99,7 @@ function parseFirefoxBlock(rec) {
92
99
  version: firefoxVersion,
93
100
  product: firefoxProduct,
94
101
  ...(firefoxSha256 !== undefined ? { sha256: firefoxSha256.toLowerCase() } : {}),
102
+ ...(allowUnverifiedDownload !== undefined ? { allowUnverifiedDownload } : {}),
95
103
  ...(firefoxCandidate !== undefined ? { candidate: firefoxCandidate } : {}),
96
104
  };
97
105
  }
@@ -154,6 +162,10 @@ export function validateConfig(data) {
154
162
  const firefox = parseFirefoxBlock(rec);
155
163
  const config = { ...identity, firefox };
156
164
  parseBuildBlock(rec, config);
165
+ const buildAuditRaw = rec.raw('buildAudit');
166
+ if (buildAuditRaw !== undefined) {
167
+ config.buildAudit = parseBuildAuditConfig(buildAuditRaw);
168
+ }
157
169
  parseTestBlock(rec, config);
158
170
  parseExternalToolchainsBlock(rec, config);
159
171
  parseWireBlock(rec, config);
@@ -323,6 +335,7 @@ function optionalPatchLintBoolean(rec, key) {
323
335
  }
324
336
  /** Severity-gate fields, each parsed and assigned identically. */
325
337
  const PATCH_LINT_SEVERITY_GATE_KEYS = [
338
+ 'prettier',
326
339
  'jsdocClassMethods',
327
340
  'testAssertionFloor',
328
341
  'chromeScriptJsDoc',
@@ -359,6 +372,10 @@ function parsePatchLintBlock(rec) {
359
372
  }
360
373
  out.rawColorAllowlist = rawColorAllowlist;
361
374
  }
375
+ const fileSizeThresholds = rec.raw('fileSizeThresholds');
376
+ if (fileSizeThresholds !== undefined) {
377
+ out.fileSizeThresholds = parsePatchLintFileSizeThresholds(fileSizeThresholds);
378
+ }
362
379
  for (const key of PATCH_LINT_SEVERITY_GATE_KEYS) {
363
380
  const gate = parseSeverityGate(rec.raw(key), `patchLint.${key}`);
364
381
  if (gate !== undefined)
@@ -419,10 +436,10 @@ function parseTypecheckBlock(rec) {
419
436
  for (let i = 0; i < projectsRaw.length; i++) {
420
437
  const entry = projectsRaw[i];
421
438
  if (typeof entry !== 'string' || entry.trim() === '') {
422
- throw new ConfigError(`Config field "typecheck.projects[${String(i)}]" must be a non-empty string`);
439
+ throw new ConfigError(`Config field "typecheck.projects[${i}]" must be a non-empty string`);
423
440
  }
424
441
  if (!isContainedRelativePath(entry)) {
425
- throw new ConfigError(`Config field "typecheck.projects[${String(i)}]" must be a project-relative path`);
442
+ throw new ConfigError(`Config field "typecheck.projects[${i}]" must be a project-relative path`);
426
443
  }
427
444
  projects.push(entry);
428
445
  }
@@ -10,8 +10,8 @@
10
10
  import type { FireForgeConfig } from '../types/config.js';
11
11
  import type { JsonObject } from '../types/json.js';
12
12
  export { mutateConfig } from './config-mutate.js';
13
- export { CONFIG_FILENAME, FIREFORGE_DIR, getProjectPaths, STATE_FILENAME, SUPPORTED_CONFIG_PATHS, SUPPORTED_CONFIG_ROOT_KEYS, } from './config-paths.js';
14
- export { loadState, saveState, updateState } from './config-state.js';
13
+ export { FIREFORGE_DIR, getProjectPaths, SUPPORTED_CONFIG_PATHS, SUPPORTED_CONFIG_ROOT_KEYS, } from './config-paths.js';
14
+ export { loadState, updateState } from './config-state.js';
15
15
  export { validateConfig } from './config-validate.js';
16
16
  /**
17
17
  * Checks if a fireforge.json exists in the given directory.
@@ -18,20 +18,10 @@ import { validateConfig } from './config-validate.js';
18
18
  import { createSiblingLockPath, withFileLock } from './file-lock.js';
19
19
  // ---- re-exports ----
20
20
  export { mutateConfig } from './config-mutate.js';
21
- export { CONFIG_FILENAME, FIREFORGE_DIR, getProjectPaths, STATE_FILENAME, SUPPORTED_CONFIG_PATHS, SUPPORTED_CONFIG_ROOT_KEYS, } from './config-paths.js';
22
- export { loadState, saveState, updateState } from './config-state.js';
21
+ export { FIREFORGE_DIR, getProjectPaths, SUPPORTED_CONFIG_PATHS, SUPPORTED_CONFIG_ROOT_KEYS, } from './config-paths.js';
22
+ export { loadState, updateState } from './config-state.js';
23
23
  export { validateConfig } from './config-validate.js';
24
24
  // ---- config I/O (stays here because it bridges paths + validation) ----
25
- /**
26
- * Config-file existence probe.
27
- *
28
- * Uses {@link pathExistsStrict} deliberately: a permission error probing
29
- * `fireforge.json` must propagate rather than read as "no config here",
30
- * which is what plain `pathExists` would report.
31
- */
32
- async function configPathExists(path) {
33
- return fsUtils.pathExistsStrict(path);
34
- }
35
25
  /**
36
26
  * Checks if a fireforge.json exists in the given directory.
37
27
  * @param root - Root directory to check
@@ -39,7 +29,9 @@ async function configPathExists(path) {
39
29
  */
40
30
  export async function configExists(root) {
41
31
  const paths = getProjectPaths(root);
42
- return configPathExists(paths.config);
32
+ // Strict probe: a permission error on `fireforge.json` must propagate
33
+ // rather than read as "no config here", which plain `pathExists` reports.
34
+ return fsUtils.pathExistsStrict(paths.config);
43
35
  }
44
36
  /**
45
37
  * Loads and validates the fireforge.json configuration.
@@ -49,7 +41,9 @@ export async function configExists(root) {
49
41
  */
50
42
  export async function loadConfig(root) {
51
43
  const paths = getProjectPaths(root);
52
- if (!(await configPathExists(paths.config))) {
44
+ // Strict probe: a permission error on `fireforge.json` must propagate
45
+ // rather than read as "no config here", which plain `pathExists` reports.
46
+ if (!(await fsUtils.pathExistsStrict(paths.config))) {
53
47
  throw new ConfigNotFoundError(paths.config);
54
48
  }
55
49
  try {
@@ -81,7 +75,9 @@ export async function loadConfig(root) {
81
75
  */
82
76
  export async function loadRawConfigDocument(root) {
83
77
  const paths = getProjectPaths(root);
84
- if (!(await configPathExists(paths.config))) {
78
+ // Strict probe: a permission error on `fireforge.json` must propagate
79
+ // rather than read as "no config here", which plain `pathExists` reports.
80
+ if (!(await fsUtils.pathExistsStrict(paths.config))) {
85
81
  throw new ConfigNotFoundError(paths.config);
86
82
  }
87
83
  try {
@@ -34,10 +34,10 @@
34
34
  * staging stale while extend still vouches for it. Plain REPLACE has no such
35
35
  * window, because the earlier scope simply stops being covered.
36
36
  */
37
- import { createHash } from 'node:crypto';
38
37
  import { readFile } from 'node:fs/promises';
39
38
  import { join } from 'node:path';
40
39
  import { getNodeErrorCode, toError } from '../utils/errors.js';
40
+ import { sha256Hex } from '../utils/hash.js';
41
41
  import { verbose } from '../utils/logger.js';
42
42
  import { DELETED_FILE_FINGERPRINT, } from './build-baseline-types.js';
43
43
  import { getHead, isMissingHeadError } from './git.js';
@@ -144,7 +144,7 @@ export function formatExtendCoverageRefusal(result) {
144
144
  `longer anchored. ${remedy}`);
145
145
  case 'fingerprint-diverged':
146
146
  return ('--extend-coverage refused: ' +
147
- `${String(result.detail.length)} packageable file(s) the recorded build staged have changed ` +
147
+ `${result.detail.length} packageable file(s) the recorded build staged have changed ` +
148
148
  `since (${formatDivergedPaths(result.detail)}). Extending would vouch for the earlier scope ` +
149
149
  `while obj-*/_tests/ still holds its stale staging. ${remedy}`);
150
150
  case 'mozconfig-changed':
@@ -156,7 +156,7 @@ export function formatExtendCoverageRefusal(result) {
156
156
  function formatDivergedPaths(paths) {
157
157
  const head = paths.slice(0, MAX_DIVERGED_REPORTED).join(', ');
158
158
  const extra = paths.length - Math.min(paths.length, MAX_DIVERGED_REPORTED);
159
- return extra > 0 ? `${head}, … (+${String(extra)} more)` : head;
159
+ return extra > 0 ? `${head}, … (+${extra} more)` : head;
160
160
  }
161
161
  /**
162
162
  * Hex-encoded SHA-256 of an engine-relative file, matching the digest the
@@ -168,7 +168,7 @@ function formatDivergedPaths(paths) {
168
168
  export async function hashEngineFile(engineDir, relPath) {
169
169
  try {
170
170
  const buffer = await readFile(join(engineDir, relPath));
171
- return createHash('sha256').update(buffer).digest('hex');
171
+ return sha256Hex(buffer);
172
172
  }
173
173
  catch (error) {
174
174
  if (getNodeErrorCode(error) === 'ENOENT') {
@@ -226,22 +226,22 @@ export function summarizeDiscardBaselines(plans, succeeded) {
226
226
  const deleted = plans.filter((p) => p.kind === 'patch-deleted').length;
227
227
  const unmanaged = plans.filter((p) => p.kind === 'unmanaged').length;
228
228
  if (patchBacked + created + deleted === 0) {
229
- return `${String(succeeded)} file(s) restored to upstream state`;
229
+ return `${succeeded} file(s) restored to upstream state`;
230
230
  }
231
231
  const parts = [];
232
232
  if (patchBacked > 0)
233
- parts.push(`${String(patchBacked)} to patch baseline`);
233
+ parts.push(`${patchBacked} to patch baseline`);
234
234
  if (created > 0)
235
- parts.push(`${String(created)} re-materialized`);
235
+ parts.push(`${created} re-materialized`);
236
236
  if (deleted > 0)
237
- parts.push(`${String(deleted)} removed per patch baseline`);
237
+ parts.push(`${deleted} removed per patch baseline`);
238
238
  if (unmanaged > 0)
239
- parts.push(`${String(unmanaged)} to upstream state`);
240
- return `${String(succeeded)} file(s) restored: ${parts.join(', ')}`;
239
+ parts.push(`${unmanaged} to upstream state`);
240
+ return `${succeeded} file(s) restored: ${parts.join(', ')}`;
241
241
  }
242
242
  /** Warn text for a multi-owner (conflicted) restore. */
243
243
  export function describeConflictWarning(plan) {
244
- return (`${plan.entry.file} is claimed by ${String(plan.owners.length)} patches ` +
244
+ return (`${plan.entry.file} is claimed by ${plan.owners.length} patches ` +
245
245
  `(${plan.owners.join(', ')}). Restored the cumulative baseline; run ` +
246
246
  '"fireforge status --ownership" to resolve ownership.');
247
247
  }
@@ -19,13 +19,6 @@ export interface EngineGitReadyOptions {
19
19
  */
20
20
  unbornHeadSuffix?: string;
21
21
  }
22
- /**
23
- * Error factory for callers whose domain uses a class other than
24
- * `GeneralError`. The furnace family raises `FurnaceError`, which carries
25
- * its own `userMessage` and exit code 9, so a helper hardcoding
26
- * `GeneralError` would silently change both.
27
- */
28
- export type PreconditionErrorFactory = (message: string) => Error;
29
22
  /**
30
23
  * Asserts only that the engine checkout EXISTS.
31
24
  *
@@ -36,9 +29,8 @@ export type PreconditionErrorFactory = (message: string) => Error;
36
29
  * would refuse runs that work today.
37
30
  *
38
31
  * @param engineDir - Absolute path to the engine checkout
39
- * @param errorFactory - Optional domain-specific error constructor
40
32
  */
41
- export declare function assertEngineExists(engineDir: string, errorFactory?: PreconditionErrorFactory): Promise<void>;
33
+ export declare function assertEngineExists(engineDir: string): Promise<void>;
42
34
  /**
43
35
  * Asserts that `engineDir` exists, is a git repository, and has a baseline
44
36
  * commit — in that order, so the operator gets the most specific applicable
@@ -20,8 +20,6 @@ const UNBORN_HEAD_REMEDIATION = 'Engine repository has no baseline commit yet
20
20
  // No terminating period: the suffix below supplies its own punctuation, and
21
21
  // hardcoding one here rendered as "…cleanly., then retry the rebase."
22
22
  'Re-run "fireforge download --force" to recreate the baseline repository cleanly';
23
- /** Default factory: the message every non-furnace command already used. */
24
- const defaultErrorFactory = (message) => new GeneralError(message);
25
23
  /**
26
24
  * Asserts only that the engine checkout EXISTS.
27
25
  *
@@ -32,11 +30,10 @@ const defaultErrorFactory = (message) => new GeneralError(message);
32
30
  * would refuse runs that work today.
33
31
  *
34
32
  * @param engineDir - Absolute path to the engine checkout
35
- * @param errorFactory - Optional domain-specific error constructor
36
33
  */
37
- export async function assertEngineExists(engineDir, errorFactory = defaultErrorFactory) {
34
+ export async function assertEngineExists(engineDir) {
38
35
  if (!(await pathExists(engineDir))) {
39
- throw errorFactory('Firefox source not found. Run "fireforge download" first.');
36
+ throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
40
37
  }
41
38
  }
42
39
  /**
@@ -48,9 +45,7 @@ export async function assertEngineExists(engineDir, errorFactory = defaultErrorF
48
45
  * @param options - Optional per-command message tailoring
49
46
  */
50
47
  export async function assertEngineGitReady(engineDir, options = {}) {
51
- if (!(await pathExists(engineDir))) {
52
- throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
53
- }
48
+ await assertEngineExists(engineDir);
54
49
  if (!(await isGitRepository(engineDir))) {
55
50
  throw new GeneralError('Engine directory is not a git repository. Run "fireforge download" to initialize.');
56
51
  }
@@ -3,6 +3,7 @@ import { join } from 'node:path';
3
3
  import { InconclusiveVerdictError } from '../errors/base.js';
4
4
  import { toError } from '../utils/errors.js';
5
5
  import { info, warn } from '../utils/logger.js';
6
+ import { sleep } from '../utils/sleep.js';
6
7
  import { readLockStatus, withFileLock } from './file-lock.js';
7
8
  import { git } from './git-base.js';
8
9
  const ENGINE_SESSION_LOCK_PATH = join('.fireforge', 'engine-session.lock');
@@ -21,19 +22,19 @@ const MAX_WAIT_LOCK_MS = 3600 * 1000;
21
22
  * owner file degrades to the anonymous form.
22
23
  */
23
24
  function formatWaitProgressLine(waitedMs, timeoutMs, holder, queue) {
24
- const progress = `${String(Math.round(waitedMs / 1000))}s of up to ${String(Math.round(timeoutMs / 1000))}s`;
25
+ const progress = `${Math.round(waitedMs / 1000)}s of up to ${Math.round(timeoutMs / 1000)}s`;
25
26
  // Queue position: under several concurrent sessions the wait
26
27
  // line alone left operators inferring their place from `ps`.
27
28
  const position = queue === undefined || queue.depth === 0
28
29
  ? ''
29
30
  : queue.ahead === 0
30
- ? ` You are next in a queue of ${String(queue.depth)}.`
31
- : ` ${String(queue.ahead)} ahead of you (queue of ${String(queue.depth)}).`;
31
+ ? ` You are next in a queue of ${queue.depth}.`
32
+ : ` ${queue.ahead} ahead of you (queue of ${queue.depth}).`;
32
33
  if (holder === undefined) {
33
34
  return `Waiting for the FireForge engine lock — ${progress}.${position}`;
34
35
  }
35
36
  const details = holder.metadata.length > 0 ? ` (${holder.metadata.join(', ')})` : '';
36
- return `Waiting for the FireForge engine lock held by PID ${String(holder.pid)}${details} — ${progress}.${position}`;
37
+ return `Waiting for the FireForge engine lock held by PID ${holder.pid}${details} — ${progress}.${position}`;
37
38
  }
38
39
  /**
39
40
  * Formats the notice printed when the wait budget is EXTENDED because the
@@ -45,10 +46,10 @@ function formatWaitProgressLine(waitedMs, timeoutMs, holder, queue) {
45
46
  * number they typed would otherwise conclude the timeout is broken.
46
47
  */
47
48
  export function formatWaitExtendedLine(ahead, budgetMs, requestedMs) {
48
- const position = ahead === 0 ? 'you are now next' : `${String(ahead)} still ahead of you`;
49
+ const position = ahead === 0 ? 'you are now next' : `${ahead} still ahead of you`;
49
50
  return (`The queue advanced (${position}), so the engine-lock wait was extended to ` +
50
- `${String(Math.round(budgetMs / 1000))}s total (you asked for ` +
51
- `${String(Math.round(requestedMs / 1000))}s). A queue that stops moving still ` +
51
+ `${Math.round(budgetMs / 1000)}s total (you asked for ` +
52
+ `${Math.round(requestedMs / 1000)}s). A queue that stops moving still ` +
52
53
  `gives up on the budget you asked for.`);
53
54
  }
54
55
  /**
@@ -128,15 +129,15 @@ export function formatEngineSessionLockStatus(snapshot, holderCpuSeconds) {
128
129
  }
129
130
  const elapsed = snapshot.heldForMs === undefined
130
131
  ? 'unknown duration'
131
- : `${String(Math.round(snapshot.heldForMs / 1000))}s`;
132
+ : `${Math.round(snapshot.heldForMs / 1000)}s`;
132
133
  const holder = snapshot.holder;
133
134
  const identity = holder === undefined
134
135
  ? 'held by an unidentified process (no readable owner record)'
135
- : `held by PID ${String(holder.pid)}${holder.alive ? '' : ' (NOT RUNNING — stale lock)'}` +
136
+ : `held by PID ${holder.pid}${holder.alive ? '' : ' (NOT RUNNING — stale lock)'}` +
136
137
  (holder.metadata.length > 0 ? ` (${holder.metadata.join(', ')})` : '');
137
138
  const lines = [
138
139
  `Engine session lock: ${identity}, for ${elapsed}.`,
139
- `Queue depth: ${String(snapshot.queueDepth)} waiter(s).`,
140
+ `Queue depth: ${snapshot.queueDepth} waiter(s).`,
140
141
  ];
141
142
  // Liveness answers "does the process exist"; it does not answer "is it
142
143
  // getting anywhere". A holder alive for minutes having used a fraction of
@@ -182,7 +183,7 @@ export async function snapshotEngineGeneration(engineDir) {
182
183
  // and is reported immediately.
183
184
  if (!isIndexLockError(error) || attempt === GENERATION_PROBE_ATTEMPTS - 1)
184
185
  break;
185
- await delay(GENERATION_PROBE_RETRY_MS);
186
+ await sleep(GENERATION_PROBE_RETRY_MS);
186
187
  }
187
188
  }
188
189
  return `${UNAVAILABLE_PREFIX}${toError(lastError).message}`;
@@ -191,11 +192,6 @@ export async function snapshotEngineGeneration(engineDir) {
191
192
  const GENERATION_PROBE_ATTEMPTS = 3;
192
193
  /** Pause between generation-probe attempts. */
193
194
  const GENERATION_PROBE_RETRY_MS = 250;
194
- function delay(ms) {
195
- return new Promise((resolveDelay) => {
196
- setTimeout(resolveDelay, ms);
197
- });
198
- }
199
195
  /**
200
196
  * True when a git failure is the transient index-lock contention shape
201
197
  * (`Unable to create '…/.git/index.lock': File exists`) rather than a
@@ -290,7 +286,7 @@ export function describeEngineGenerationDelta(before, after) {
290
286
  return;
291
287
  const head = records.slice(0, DELTA_ENTRY_LIMIT);
292
288
  const truncated = records.length - head.length;
293
- lines.push(`${label}: ${head.join(', ')}${truncated > 0 ? `, … (+${String(truncated)} more)` : ''}`);
289
+ lines.push(`${label}: ${head.join(', ')}${truncated > 0 ? `, … (+${truncated} more)` : ''}`);
294
290
  };
295
291
  render('Working-tree entries that appeared', appeared);
296
292
  render('Working-tree entries that went away', disappeared);
@@ -114,14 +114,14 @@ export function formatAdoptThenSplitRemedy(overlaps, newPatchName, insertionOrde
114
114
  const suggestedName = newPatchName.replace(/^\d+-/, '').replace(/\.patch$/, '');
115
115
  return [
116
116
  '',
117
- `This looks like a new-file + moved-code slice: ${String(strongest.sharedLines)} substantive ` +
117
+ `This looks like a new-file + moved-code slice: ${strongest.sharedLines} substantive ` +
118
118
  `line(s) the new file(s) would add are still carried by ${strongest.sourcePatch}. ` +
119
119
  'Exporting them as their own patch cannot pass cross-patch lint while the source patch ' +
120
120
  'still owns the same code, and re-exporting cannot adopt them while they are unmanaged — ' +
121
121
  'each guard is right, and together they dead-end.',
122
122
  'Working sequence (adopt, then split):',
123
123
  ` 1. fireforge re-export ${strongest.sourcePatch} --scan ${fileArgs}`,
124
- ` 2. fireforge patch move-files ${strongest.sourcePatch} ${suggestedName} --create --order ${String(insertionOrder)} ${moveArgs}`,
124
+ ` 2. fireforge patch move-files ${strongest.sourcePatch} ${suggestedName} --create --order ${insertionOrder} ${moveArgs}`,
125
125
  'Step 1 adopts the new files into the patch that already owns the moved lines; step 2 ' +
126
126
  'splits them back out as one transaction, so the queue is never in a state where two ' +
127
127
  'patches own the same code.',
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Filename of a lock directory's owner record, relative to the lock dir.
3
+ *
4
+ * Re-exported by `file-lock.ts` so external readers (`tree-store.ts`) share
5
+ * the constant instead of re-spelling `'pid'`; the file *format* is
6
+ * documented on {@link readLockOwner}.
7
+ */
8
+ export declare const LOCK_PID_FILE = "pid";
9
+ /**
10
+ * Owner record read back from a lock directory's PID file.
11
+ *
12
+ * `token` is the per-acquisition UUID written on line 2 of the PID file,
13
+ * `acquiredAtMs` the wall-clock time the record was written (diagnostic),
14
+ * and `startTick` the writer's boot-relative start tick (liveness). Records
15
+ * from releases before a field was introduced parse with it `undefined`;
16
+ * readers must treat that as "unknown", never as a mismatch.
17
+ */
18
+ export type LockOwner = {
19
+ present: false;
20
+ } | {
21
+ present: true;
22
+ pid: number;
23
+ token?: string;
24
+ acquiredAtMs?: number;
25
+ startTick?: number;
26
+ metadata: string[];
27
+ };
28
+ /**
29
+ * Reads the owner PID (line 1), acquisition token (line 2), the mechanical
30
+ * `acquired-at-ms=` / `start-tick=` lines (when present), and diagnostic
31
+ * metadata (the remaining lines 3+) from a lock directory's PID file.
32
+ * Returns `{ present: false }` when the PID file is missing, unreadable, or
33
+ * the PID does not parse as a finite integer (caller falls back to the
34
+ * age-only staleness heuristic).
35
+ *
36
+ * File format note: external readers do `parseInt(content.trim(), 10)`,
37
+ * which parses the leading digits of a multi-line file — so the token and
38
+ * mechanical lines stay compatible.
39
+ *
40
+ * @param lockPath - Lock directory whose owner record to read
41
+ * @returns The parsed owner record, or `{ present: false }`
42
+ */
43
+ export declare function readLockOwner(lockPath: string): Promise<LockOwner>;
44
+ /**
45
+ * Stamps this process's ownership (PID, per-acquisition token, acquisition
46
+ * time, own start tick, diagnostic metadata) into a lock directory it has
47
+ * just created, and reads the record back to prove it landed.
48
+ *
49
+ * The write is FATAL. It used to be best-effort, on the theory that stale
50
+ * recovery would fall back to the age heuristic — but that heuristic reaps
51
+ * any owner-less lock after five minutes, and the build lock legitimately
52
+ * holds for hours. A holder whose record failed to write was therefore
53
+ * reaped mid-build and a second process entered the critical section. On
54
+ * failure the just-created lock directory is removed (nothing else can own
55
+ * it yet) and a {@link GeneralError} names the cause.
56
+ *
57
+ * @param lockPath - Lock directory this process has just `mkdir`'d
58
+ * @param token - Per-acquisition UUID, later checked by release
59
+ * @param ownerMetadata - Extra human-diagnostic lines (see `FileLockOptions.ownerMetadata`)
60
+ * @throws {GeneralError} When the record cannot be written or does not read back as written
61
+ */
62
+ export declare function writeLockOwner(lockPath: string, token: string, ownerMetadata: readonly string[] | undefined): Promise<void>;
63
+ /**
64
+ * Boot-relative start tick of a process, where the platform exposes it
65
+ * cheaply.
66
+ *
67
+ * Linux only: field 22 of `/proc/<pid>/stat`, in USER_HZ since boot.
68
+ * Elsewhere (macOS, Windows) obtaining a start time means spawning
69
+ * `ps`/`wmic` per probe, which is not worth paying on every poll of a
70
+ * contended lock — those platforms return `undefined` and liveness stays
71
+ * PID-only.
72
+ *
73
+ * Deliberately NOT converted to wall-clock time via `/proc/stat` `btime`:
74
+ * on current kernels `btime` is derived from the realtime clock and moves
75
+ * with every clock step, so a wall-clock comparison against a recorded
76
+ * acquisition time misjudges a live holder as a reused PID after NTP steps
77
+ * the clock forward — and reaps a lock that is in use.
78
+ *
79
+ * @param pid - Process to look up
80
+ * @param procRoot - procfs mount, overridable for tests
81
+ * @returns Start tick, or `undefined` when unobtainable
82
+ */
83
+ export declare function readProcessStartTick(pid: number, procRoot?: string): number | undefined;
84
+ /**
85
+ * Whether the process named by a lock's owner record is the SAME process
86
+ * that acquired the lock, not merely a live process wearing its PID.
87
+ *
88
+ * `isProcessAlive` answers "does this PID exist"; after a holder crashes,
89
+ * the kernel may hand its PID to a long-lived daemon, and a PID-only check
90
+ * then honours the dead lock until the waiter's timeout. Where the record
91
+ * carries the writer's start tick and the candidate's own tick is cheaply
92
+ * obtainable (see {@link readProcessStartTick}), the two must be equal: a
93
+ * different tick is a different process. Records without a start tick, and
94
+ * platforms without one, keep the PID-only answer.
95
+ *
96
+ * @param owner - Owner record fields that identify the holder
97
+ * @param procRoot - procfs mount, overridable for tests
98
+ * @returns True when the original holder is still running (or cannot be proven gone)
99
+ */
100
+ export declare function isLockOwnerAlive(owner: {
101
+ pid: number;
102
+ startTick?: number | undefined;
103
+ }, procRoot?: string): boolean;