@hominis/fireforge 0.43.1 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (470) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +10 -1
  3. package/dist/bin/fireforge.d.ts +4 -9
  4. package/dist/bin/fireforge.js +42 -40
  5. package/dist/build-info.json +4 -4
  6. package/dist/src/cli.js +74 -8
  7. package/dist/src/commands/bootstrap.js +17 -20
  8. package/dist/src/commands/build.js +25 -32
  9. package/dist/src/commands/config.js +34 -41
  10. package/dist/src/commands/discard.js +8 -9
  11. package/dist/src/commands/doctor-check-core.d.ts +4 -18
  12. package/dist/src/commands/doctor-check-core.js +8 -35
  13. package/dist/src/commands/doctor-external-toolchains.js +2 -2
  14. package/dist/src/commands/doctor-furnace-config-sync.d.ts +18 -0
  15. package/dist/src/commands/{doctor-furnace-manifest-sync.js → doctor-furnace-config-sync.js} +27 -26
  16. package/dist/src/commands/doctor-furnace-jar.d.ts +7 -9
  17. package/dist/src/commands/doctor-furnace-jar.js +7 -9
  18. package/dist/src/commands/doctor-furnace.js +21 -24
  19. package/dist/src/commands/doctor-orphaned-harness.d.ts +16 -20
  20. package/dist/src/commands/doctor-orphaned-harness.js +18 -44
  21. package/dist/src/commands/doctor-post-rebase-audit.d.ts +2 -0
  22. package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
  23. package/dist/src/commands/doctor-working-tree.d.ts +10 -12
  24. package/dist/src/commands/doctor-working-tree.js +11 -18
  25. package/dist/src/commands/doctor.js +35 -53
  26. package/dist/src/commands/download.js +32 -48
  27. package/dist/src/commands/export-all.js +29 -33
  28. package/dist/src/commands/export-flow.js +6 -6
  29. package/dist/src/commands/export-placement-conflicts.js +10 -9
  30. package/dist/src/commands/export-placement-gate.js +5 -5
  31. package/dist/src/commands/export-shared.d.ts +30 -33
  32. package/dist/src/commands/export-shared.js +45 -43
  33. package/dist/src/commands/export.js +21 -27
  34. package/dist/src/commands/furnace/apply.js +14 -23
  35. package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
  36. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
  37. package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
  38. package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
  39. package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
  40. package/dist/src/commands/furnace/chrome-doc.js +31 -41
  41. package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
  42. package/dist/src/commands/furnace/create-browser-test.js +8 -8
  43. package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
  44. package/dist/src/commands/furnace/create-mochikit.js +8 -9
  45. package/dist/src/commands/furnace/create-readback.d.ts +12 -13
  46. package/dist/src/commands/furnace/create-readback.js +12 -13
  47. package/dist/src/commands/furnace/create-templates.d.ts +22 -28
  48. package/dist/src/commands/furnace/create-templates.js +35 -30
  49. package/dist/src/commands/furnace/create-xpcshell.js +2 -2
  50. package/dist/src/commands/furnace/create.js +33 -23
  51. package/dist/src/commands/furnace/deploy.js +16 -24
  52. package/dist/src/commands/furnace/diff.js +9 -9
  53. package/dist/src/commands/furnace/init.js +33 -36
  54. package/dist/src/commands/furnace/override.js +23 -25
  55. package/dist/src/commands/furnace/preview.js +47 -67
  56. package/dist/src/commands/furnace/refresh.js +12 -16
  57. package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
  58. package/dist/src/commands/furnace/remove-mutations.js +9 -16
  59. package/dist/src/commands/furnace/remove-state.js +1 -1
  60. package/dist/src/commands/furnace/remove.js +33 -38
  61. package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
  62. package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
  63. package/dist/src/commands/furnace/rename.js +59 -69
  64. package/dist/src/commands/furnace/scan.js +19 -33
  65. package/dist/src/commands/furnace/status.js +12 -9
  66. package/dist/src/commands/furnace/sync.js +9 -16
  67. package/dist/src/commands/furnace/validate.js +20 -20
  68. package/dist/src/commands/import.js +42 -35
  69. package/dist/src/commands/lint-per-patch.js +15 -10
  70. package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
  71. package/dist/src/commands/lint-per-run-checkjs.js +13 -13
  72. package/dist/src/commands/lint-report.js +6 -7
  73. package/dist/src/commands/lint.d.ts +15 -16
  74. package/dist/src/commands/lint.js +86 -102
  75. package/dist/src/commands/package.js +6 -11
  76. package/dist/src/commands/patch/compact.js +1 -1
  77. package/dist/src/commands/patch/delete.js +24 -33
  78. package/dist/src/commands/patch/index.d.ts +5 -6
  79. package/dist/src/commands/patch/index.js +10 -12
  80. package/dist/src/commands/patch/lint-ignore.js +14 -13
  81. package/dist/src/commands/patch/move-files-create.d.ts +6 -6
  82. package/dist/src/commands/patch/move-files-create.js +8 -8
  83. package/dist/src/commands/patch/move-files-into.d.ts +6 -6
  84. package/dist/src/commands/patch/move-files-into.js +8 -8
  85. package/dist/src/commands/patch/patch-context.d.ts +2 -3
  86. package/dist/src/commands/patch/patch-context.js +2 -3
  87. package/dist/src/commands/patch/rename.d.ts +10 -11
  88. package/dist/src/commands/patch/rename.js +11 -12
  89. package/dist/src/commands/patch/reorder.js +8 -9
  90. package/dist/src/commands/patch/split-plan.js +1 -1
  91. package/dist/src/commands/patch/split.d.ts +8 -10
  92. package/dist/src/commands/patch/split.js +9 -11
  93. package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
  94. package/dist/src/commands/patch/staged-dependency.js +7 -3
  95. package/dist/src/commands/patch/tier.d.ts +9 -9
  96. package/dist/src/commands/patch/tier.js +18 -15
  97. package/dist/src/commands/re-export-adjacent.js +9 -8
  98. package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
  99. package/dist/src/commands/re-export-bulk-scan.js +22 -21
  100. package/dist/src/commands/re-export-drift.js +10 -10
  101. package/dist/src/commands/re-export-files.d.ts +6 -7
  102. package/dist/src/commands/re-export-files.js +34 -44
  103. package/dist/src/commands/re-export-lint.d.ts +10 -13
  104. package/dist/src/commands/re-export-lint.js +16 -14
  105. package/dist/src/commands/re-export-options.d.ts +6 -6
  106. package/dist/src/commands/re-export-options.js +6 -6
  107. package/dist/src/commands/re-export-register.d.ts +4 -10
  108. package/dist/src/commands/re-export-register.js +5 -11
  109. package/dist/src/commands/re-export-scan.js +1 -1
  110. package/dist/src/commands/re-export-single.d.ts +3 -3
  111. package/dist/src/commands/re-export-single.js +33 -25
  112. package/dist/src/commands/re-export.js +13 -21
  113. package/dist/src/commands/rebase/abort.js +3 -3
  114. package/dist/src/commands/rebase/confirm.js +2 -1
  115. package/dist/src/commands/rebase/continue.d.ts +1 -1
  116. package/dist/src/commands/rebase/continue.js +14 -9
  117. package/dist/src/commands/rebase/index.js +11 -11
  118. package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
  119. package/dist/src/commands/rebase/patch-loop.js +60 -39
  120. package/dist/src/commands/rebase/summary.js +1 -1
  121. package/dist/src/commands/register.js +5 -7
  122. package/dist/src/commands/reset.js +2 -1
  123. package/dist/src/commands/resolve.d.ts +16 -13
  124. package/dist/src/commands/resolve.js +30 -33
  125. package/dist/src/commands/run.js +33 -35
  126. package/dist/src/commands/setup-support.js +15 -19
  127. package/dist/src/commands/setup.js +13 -9
  128. package/dist/src/commands/status-check.d.ts +6 -6
  129. package/dist/src/commands/status-json.d.ts +6 -6
  130. package/dist/src/commands/status-json.js +5 -0
  131. package/dist/src/commands/status-output.d.ts +5 -5
  132. package/dist/src/commands/status-output.js +6 -6
  133. package/dist/src/commands/status-ownership.d.ts +16 -14
  134. package/dist/src/commands/status-ownership.js +16 -14
  135. package/dist/src/commands/status.js +42 -34
  136. package/dist/src/commands/test-build-phase.js +26 -13
  137. package/dist/src/commands/test-diagnose.js +44 -44
  138. package/dist/src/commands/test-register.js +1 -0
  139. package/dist/src/commands/test-run.d.ts +14 -16
  140. package/dist/src/commands/test-run.js +21 -13
  141. package/dist/src/commands/test-stale-gate.d.ts +15 -16
  142. package/dist/src/commands/test-stale-gate.js +15 -16
  143. package/dist/src/commands/test-verdict.d.ts +20 -3
  144. package/dist/src/commands/test-verdict.js +30 -0
  145. package/dist/src/commands/test.js +83 -76
  146. package/dist/src/commands/token-coverage.js +3 -4
  147. package/dist/src/commands/token.js +16 -18
  148. package/dist/src/commands/tree.js +47 -31
  149. package/dist/src/commands/typecheck.d.ts +10 -12
  150. package/dist/src/commands/typecheck.js +24 -27
  151. package/dist/src/commands/verify.js +13 -15
  152. package/dist/src/commands/watch.js +21 -23
  153. package/dist/src/commands/wire.js +52 -62
  154. package/dist/src/core/ast-utils.d.ts +8 -7
  155. package/dist/src/core/ast-utils.js +8 -7
  156. package/dist/src/core/branding.d.ts +9 -14
  157. package/dist/src/core/branding.js +7 -11
  158. package/dist/src/core/browser-wire.d.ts +1 -1
  159. package/dist/src/core/browser-wire.js +14 -19
  160. package/dist/src/core/build-audit-platform.js +25 -33
  161. package/dist/src/core/build-audit-registration.js +17 -25
  162. package/dist/src/core/build-audit-resolve.d.ts +15 -17
  163. package/dist/src/core/build-audit-resolve.js +27 -33
  164. package/dist/src/core/build-audit-transforms.js +18 -19
  165. package/dist/src/core/build-audit.js +28 -42
  166. package/dist/src/core/build-baseline-types.d.ts +29 -35
  167. package/dist/src/core/build-baseline.d.ts +5 -4
  168. package/dist/src/core/build-baseline.js +14 -14
  169. package/dist/src/core/build-overwrite-guard.d.ts +7 -14
  170. package/dist/src/core/build-overwrite-guard.js +17 -27
  171. package/dist/src/core/build-prepare.d.ts +2 -2
  172. package/dist/src/core/build-prepare.js +8 -9
  173. package/dist/src/core/config-state.js +12 -4
  174. package/dist/src/core/config-validate.js +8 -5
  175. package/dist/src/core/config.d.ts +5 -8
  176. package/dist/src/core/config.js +7 -16
  177. package/dist/src/core/coverage-extend.d.ts +15 -13
  178. package/dist/src/core/coverage-extend.js +15 -13
  179. package/dist/src/core/destructive.d.ts +31 -9
  180. package/dist/src/core/destructive.js +29 -26
  181. package/dist/src/core/diff-hunks.js +10 -12
  182. package/dist/src/core/discard-baseline.d.ts +8 -8
  183. package/dist/src/core/discard-baseline.js +16 -15
  184. package/dist/src/core/display-state.d.ts +12 -14
  185. package/dist/src/core/display-state.js +12 -14
  186. package/dist/src/core/engine-changes.d.ts +2 -4
  187. package/dist/src/core/engine-changes.js +2 -4
  188. package/dist/src/core/engine-precondition.d.ts +28 -13
  189. package/dist/src/core/engine-precondition.js +25 -10
  190. package/dist/src/core/engine-session-lock.d.ts +47 -21
  191. package/dist/src/core/engine-session-lock.js +168 -33
  192. package/dist/src/core/export-moved-code-shape.d.ts +10 -10
  193. package/dist/src/core/export-moved-code-shape.js +10 -10
  194. package/dist/src/core/file-lock.d.ts +62 -5
  195. package/dist/src/core/file-lock.js +196 -59
  196. package/dist/src/core/firefox-cache.js +1 -3
  197. package/dist/src/core/firefox-extract.js +10 -14
  198. package/dist/src/core/firefox.d.ts +7 -5
  199. package/dist/src/core/firefox.js +4 -5
  200. package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
  201. package/dist/src/core/furnace-apply-dry-run.js +3 -2
  202. package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
  203. package/dist/src/core/furnace-apply-ftl.js +12 -12
  204. package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
  205. package/dist/src/core/furnace-apply-helpers.js +19 -16
  206. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
  207. package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
  208. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
  209. package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
  210. package/dist/src/core/furnace-apply.d.ts +5 -8
  211. package/dist/src/core/furnace-apply.js +19 -24
  212. package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
  213. package/dist/src/core/furnace-checksum-utils.js +16 -0
  214. package/dist/src/core/furnace-config.d.ts +21 -30
  215. package/dist/src/core/furnace-config.js +32 -54
  216. package/dist/src/core/furnace-constants.d.ts +8 -0
  217. package/dist/src/core/furnace-constants.js +10 -2
  218. package/dist/src/core/furnace-css-fragments.d.ts +5 -5
  219. package/dist/src/core/furnace-css-fragments.js +5 -5
  220. package/dist/src/core/furnace-jsconfig.d.ts +11 -12
  221. package/dist/src/core/furnace-jsconfig.js +11 -12
  222. package/dist/src/core/furnace-marker.d.ts +7 -8
  223. package/dist/src/core/furnace-marker.js +7 -8
  224. package/dist/src/core/furnace-operation.d.ts +31 -0
  225. package/dist/src/core/furnace-operation.js +54 -17
  226. package/dist/src/core/furnace-precondition.d.ts +61 -0
  227. package/dist/src/core/furnace-precondition.js +67 -0
  228. package/dist/src/core/furnace-registration-remove.d.ts +5 -6
  229. package/dist/src/core/furnace-registration-remove.js +6 -7
  230. package/dist/src/core/furnace-registration-validate.d.ts +13 -0
  231. package/dist/src/core/furnace-registration-validate.js +20 -1
  232. package/dist/src/core/furnace-registration.d.ts +5 -6
  233. package/dist/src/core/furnace-registration.js +10 -12
  234. package/dist/src/core/furnace-rollback.js +7 -7
  235. package/dist/src/core/furnace-scanner.d.ts +1 -0
  236. package/dist/src/core/furnace-scanner.js +1 -2
  237. package/dist/src/core/furnace-stale-export.d.ts +10 -10
  238. package/dist/src/core/furnace-stale-export.js +10 -10
  239. package/dist/src/core/furnace-state-persist.d.ts +6 -8
  240. package/dist/src/core/furnace-state-persist.js +6 -8
  241. package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
  242. package/dist/src/core/furnace-validate-accessibility.js +310 -1
  243. package/dist/src/core/furnace-validate-compatibility.js +141 -1
  244. package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
  245. package/dist/src/core/furnace-validate-helpers.js +0 -491
  246. package/dist/src/core/furnace-validate-registration.d.ts +7 -0
  247. package/dist/src/core/furnace-validate-registration.js +115 -41
  248. package/dist/src/core/furnace-validate.d.ts +16 -0
  249. package/dist/src/core/furnace-validate.js +188 -145
  250. package/dist/src/core/furnace-version-drift.d.ts +10 -11
  251. package/dist/src/core/furnace-version-drift.js +10 -11
  252. package/dist/src/core/git-base.d.ts +7 -9
  253. package/dist/src/core/git-base.js +7 -9
  254. package/dist/src/core/git-diff.d.ts +20 -9
  255. package/dist/src/core/git-diff.js +148 -63
  256. package/dist/src/core/git-file-ops.d.ts +19 -19
  257. package/dist/src/core/git-file-ops.js +19 -19
  258. package/dist/src/core/git-readonly-index.d.ts +53 -19
  259. package/dist/src/core/git-readonly-index.js +75 -31
  260. package/dist/src/core/git-status.d.ts +5 -6
  261. package/dist/src/core/git-status.js +5 -6
  262. package/dist/src/core/git.d.ts +9 -10
  263. package/dist/src/core/git.js +54 -70
  264. package/dist/src/core/license-headers.d.ts +17 -23
  265. package/dist/src/core/license-headers.js +17 -23
  266. package/dist/src/core/mach-build-artifacts.d.ts +9 -9
  267. package/dist/src/core/mach-build-artifacts.js +57 -19
  268. package/dist/src/core/mach-error-hints.js +42 -29
  269. package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
  270. package/dist/src/core/mach-known-noise-filter.js +18 -20
  271. package/dist/src/core/mach-mozconfig.d.ts +14 -0
  272. package/dist/src/core/mach-mozconfig.js +30 -0
  273. package/dist/src/core/mach-resource-shim.d.ts +43 -58
  274. package/dist/src/core/mach-resource-shim.js +50 -67
  275. package/dist/src/core/mach.d.ts +37 -41
  276. package/dist/src/core/mach.js +50 -51
  277. package/dist/src/core/marionette-port.d.ts +36 -1
  278. package/dist/src/core/marionette-port.js +96 -31
  279. package/dist/src/core/marionette-preflight.d.ts +8 -12
  280. package/dist/src/core/marionette-preflight.js +86 -52
  281. package/dist/src/core/mochitest-server-port.d.ts +42 -0
  282. package/dist/src/core/mochitest-server-port.js +119 -0
  283. package/dist/src/core/module-resolution-preflight.d.ts +9 -11
  284. package/dist/src/core/module-resolution-preflight.js +9 -11
  285. package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
  286. package/dist/src/core/moz-manifest-helpers.js +163 -0
  287. package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
  288. package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
  289. package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
  290. package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
  291. package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
  292. package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
  293. package/dist/src/core/ownership-table.d.ts +14 -16
  294. package/dist/src/core/ownership-table.js +14 -16
  295. package/dist/src/core/parser-fallback.d.ts +15 -1
  296. package/dist/src/core/parser-fallback.js +22 -3
  297. package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
  298. package/dist/src/core/patch-apply-fuzz.js +15 -18
  299. package/dist/src/core/patch-apply.d.ts +13 -34
  300. package/dist/src/core/patch-apply.js +16 -76
  301. package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
  302. package/dist/src/core/patch-artifact-normalize.js +13 -15
  303. package/dist/src/core/patch-export-metadata.d.ts +12 -2
  304. package/dist/src/core/patch-export-metadata.js +13 -4
  305. package/dist/src/core/patch-export-update.d.ts +13 -1
  306. package/dist/src/core/patch-export-update.js +25 -8
  307. package/dist/src/core/patch-export.d.ts +15 -8
  308. package/dist/src/core/patch-export.js +10 -11
  309. package/dist/src/core/patch-files.d.ts +5 -6
  310. package/dist/src/core/patch-files.js +5 -6
  311. package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
  312. package/dist/src/core/patch-identifier-suggest.js +14 -19
  313. package/dist/src/core/patch-lint-binary.d.ts +51 -0
  314. package/dist/src/core/patch-lint-binary.js +39 -0
  315. package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
  316. package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
  317. package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
  318. package/dist/src/core/patch-lint-checkjs.js +16 -16
  319. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
  320. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
  321. package/dist/src/core/patch-lint-creators.d.ts +8 -8
  322. package/dist/src/core/patch-lint-creators.js +8 -8
  323. package/dist/src/core/patch-lint-cross.d.ts +17 -20
  324. package/dist/src/core/patch-lint-cross.js +16 -16
  325. package/dist/src/core/patch-lint-css.d.ts +3 -5
  326. package/dist/src/core/patch-lint-css.js +17 -22
  327. package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
  328. package/dist/src/core/patch-lint-diff-tag.js +14 -16
  329. package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
  330. package/dist/src/core/patch-lint-jsdoc.js +26 -19
  331. package/dist/src/core/patch-lint-mozbuild.js +20 -20
  332. package/dist/src/core/patch-lint-observer.d.ts +7 -7
  333. package/dist/src/core/patch-lint-observer.js +7 -7
  334. package/dist/src/core/patch-lint.d.ts +38 -29
  335. package/dist/src/core/patch-lint.js +89 -121
  336. package/dist/src/core/patch-lock.d.ts +1 -1
  337. package/dist/src/core/patch-lock.js +45 -9
  338. package/dist/src/core/patch-manifest-consistency.d.ts +2 -1
  339. package/dist/src/core/patch-manifest-consistency.js +25 -27
  340. package/dist/src/core/patch-manifest-io.js +14 -18
  341. package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
  342. package/dist/src/core/patch-manifest-resolve.js +9 -12
  343. package/dist/src/core/patch-manifest-validate.d.ts +6 -1
  344. package/dist/src/core/patch-manifest-validate.js +25 -13
  345. package/dist/src/core/patch-parse.d.ts +24 -13
  346. package/dist/src/core/patch-parse.js +19 -14
  347. package/dist/src/core/patch-policy.d.ts +2 -2
  348. package/dist/src/core/patch-policy.js +2 -2
  349. package/dist/src/core/patch-registration-refs.d.ts +7 -8
  350. package/dist/src/core/patch-registration-refs.js +9 -9
  351. package/dist/src/core/process-cpu-time.d.ts +12 -0
  352. package/dist/src/core/process-cpu-time.js +52 -0
  353. package/dist/src/core/rebase-session.d.ts +38 -26
  354. package/dist/src/core/rebase-session.js +55 -28
  355. package/dist/src/core/register-browser-content.js +9 -74
  356. package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
  357. package/dist/src/core/register-module.js +10 -43
  358. package/dist/src/core/register-scaffold.d.ts +8 -8
  359. package/dist/src/core/register-scaffold.js +10 -10
  360. package/dist/src/core/register-shared-css.d.ts +9 -12
  361. package/dist/src/core/register-shared-css.js +17 -87
  362. package/dist/src/core/register-xpcshell-test.d.ts +6 -7
  363. package/dist/src/core/register-xpcshell-test.js +7 -8
  364. package/dist/src/core/shared-ftl.js +12 -4
  365. package/dist/src/core/state-file.d.ts +45 -0
  366. package/dist/src/core/state-file.js +34 -0
  367. package/dist/src/core/status-binary.d.ts +18 -11
  368. package/dist/src/core/status-binary.js +19 -12
  369. package/dist/src/core/status-classify.d.ts +30 -36
  370. package/dist/src/core/status-classify.js +21 -26
  371. package/dist/src/core/test-changed-prefs.d.ts +30 -0
  372. package/dist/src/core/test-changed-prefs.js +78 -0
  373. package/dist/src/core/test-harness-crash.d.ts +25 -63
  374. package/dist/src/core/test-harness-crash.js +121 -142
  375. package/dist/src/core/test-harness-signature.d.ts +18 -0
  376. package/dist/src/core/test-harness-signature.js +13 -0
  377. package/dist/src/core/test-path-scope.d.ts +12 -15
  378. package/dist/src/core/test-path-scope.js +15 -18
  379. package/dist/src/core/test-stale-check.d.ts +17 -17
  380. package/dist/src/core/test-stale-check.js +50 -43
  381. package/dist/src/core/test-stall-triage.d.ts +51 -0
  382. package/dist/src/core/test-stall-triage.js +126 -0
  383. package/dist/src/core/token-category.js +5 -7
  384. package/dist/src/core/token-coverage.js +9 -9
  385. package/dist/src/core/token-dark-mode.d.ts +9 -11
  386. package/dist/src/core/token-dark-mode.js +9 -11
  387. package/dist/src/core/token-docs.d.ts +2 -0
  388. package/dist/src/core/token-docs.js +2 -0
  389. package/dist/src/core/token-manager.d.ts +10 -2
  390. package/dist/src/core/token-manager.js +17 -8
  391. package/dist/src/core/token-scaffold.d.ts +7 -7
  392. package/dist/src/core/token-scaffold.js +7 -7
  393. package/dist/src/core/token-variant.d.ts +4 -4
  394. package/dist/src/core/token-variant.js +4 -4
  395. package/dist/src/core/toolchain-preflight.d.ts +20 -21
  396. package/dist/src/core/toolchain-preflight.js +28 -31
  397. package/dist/src/core/tree-guard.js +12 -2
  398. package/dist/src/core/tree-store.d.ts +41 -27
  399. package/dist/src/core/tree-store.js +63 -42
  400. package/dist/src/core/typecheck-shim.d.ts +23 -24
  401. package/dist/src/core/typecheck-shim.js +23 -24
  402. package/dist/src/core/typecheck.js +8 -8
  403. package/dist/src/core/wire-destroy.d.ts +3 -3
  404. package/dist/src/core/wire-destroy.js +7 -15
  405. package/dist/src/core/wire-dom-fragment.d.ts +9 -17
  406. package/dist/src/core/wire-dom-fragment.js +11 -44
  407. package/dist/src/core/wire-init.d.ts +2 -3
  408. package/dist/src/core/wire-init.js +8 -19
  409. package/dist/src/core/wire-subscript.d.ts +2 -3
  410. package/dist/src/core/wire-subscript.js +4 -13
  411. package/dist/src/core/wire-utils.d.ts +7 -8
  412. package/dist/src/core/wire-utils.js +7 -8
  413. package/dist/src/core/xpcshell-appdir.d.ts +38 -45
  414. package/dist/src/core/xpcshell-appdir.js +38 -45
  415. package/dist/src/errors/base.d.ts +37 -16
  416. package/dist/src/errors/base.js +37 -16
  417. package/dist/src/errors/build.d.ts +13 -1
  418. package/dist/src/errors/build.js +14 -2
  419. package/dist/src/errors/codes.d.ts +16 -0
  420. package/dist/src/errors/codes.js +16 -0
  421. package/dist/src/errors/furnace.d.ts +4 -4
  422. package/dist/src/errors/git.d.ts +6 -10
  423. package/dist/src/errors/git.js +6 -10
  424. package/dist/src/errors/patch.d.ts +7 -7
  425. package/dist/src/errors/patch.js +7 -7
  426. package/dist/src/errors/rebase.d.ts +4 -4
  427. package/dist/src/errors/rebase.js +4 -4
  428. package/dist/src/index.d.ts +7 -1
  429. package/dist/src/index.js +5 -0
  430. package/dist/src/types/commands/index.d.ts +2 -2
  431. package/dist/src/types/commands/options.d.ts +88 -70
  432. package/dist/src/types/commands/patches.d.ts +24 -34
  433. package/dist/src/types/commands/project.d.ts +5 -34
  434. package/dist/src/types/config.d.ts +25 -23
  435. package/dist/src/types/config.js +6 -1
  436. package/dist/src/types/furnace.d.ts +37 -13
  437. package/dist/src/types/furnace.js +19 -1
  438. package/dist/src/types/index.d.ts +3 -3
  439. package/dist/src/utils/build-info.d.ts +7 -8
  440. package/dist/src/utils/build-info.js +12 -11
  441. package/dist/src/utils/errors.d.ts +6 -8
  442. package/dist/src/utils/errors.js +6 -8
  443. package/dist/src/utils/logger.d.ts +27 -10
  444. package/dist/src/utils/logger.js +28 -16
  445. package/dist/src/utils/machine-output.d.ts +12 -0
  446. package/dist/src/utils/machine-output.js +33 -0
  447. package/dist/src/utils/options.d.ts +31 -14
  448. package/dist/src/utils/options.js +86 -30
  449. package/dist/src/utils/paths.d.ts +10 -8
  450. package/dist/src/utils/paths.js +10 -8
  451. package/dist/src/utils/platform.d.ts +0 -25
  452. package/dist/src/utils/platform.js +1 -42
  453. package/dist/src/utils/process-group.d.ts +9 -4
  454. package/dist/src/utils/process-group.js +19 -5
  455. package/dist/src/utils/process.d.ts +42 -7
  456. package/dist/src/utils/process.js +44 -17
  457. package/dist/src/utils/ps-duration.d.ts +31 -0
  458. package/dist/src/utils/ps-duration.js +70 -0
  459. package/dist/src/utils/regex.d.ts +9 -10
  460. package/dist/src/utils/regex.js +9 -10
  461. package/dist/src/utils/validation.d.ts +58 -28
  462. package/dist/src/utils/validation.js +60 -33
  463. package/package.json +2 -2
  464. package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
  465. package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
  466. package/dist/src/core/manifest-helpers.d.ts +0 -38
  467. package/dist/src/core/manifest-helpers.js +0 -120
  468. /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
  469. /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
  470. /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
@@ -3,79 +3,64 @@
3
3
  *
4
4
  * mozbuild's build resource monitor calls `psutil.virtual_memory()` at
5
5
  * startup (`start_resource_recording`) and again later from
6
- * `mozbuild.base._run_make`. On some hosts (psutil vs Darwin 27) those
7
- * raise `RuntimeError: host_statistics64(HOST_VM_INFO64) syscall failed`,
8
- * and a `SystemResourceMonitor.__init__` that fails partway leaves an
9
- * instance without `poll_interval`, so later polling dies with
10
- * `AttributeError: ... poll_interval`. Any of these aborts `mach build` /
11
- * `mach build faster` before the compiler ever runs.
6
+ * `mozbuild.base._run_make`. On some hosts those raise `RuntimeError:
7
+ * host_statistics64(HOST_VM_INFO64) syscall failed`, and a
8
+ * `SystemResourceMonitor.__init__` that fails partway leaves an instance
9
+ * without `poll_interval`, so later polling dies with `AttributeError: ...
10
+ * poll_interval`. Any of these aborts `mach build` / `mach build faster`
11
+ * before the compiler ever runs.
12
12
  *
13
- * 0.33.0 injected a `sitecustomize.py` via the mach subprocess PYTHONPATH.
14
- * Field report (0.34.0 cycle): mach re-execs itself into its private
15
- * virtualenv and drops PYTHONPATH, so the env-var route never loads and
16
- * the crash family escaped on every protected entry point. FireForge now
17
- * owns the guard in-process on its mach dispatches by installing a
18
- * `fireforge_mach_guard.pth` + module pair directly into every discovered
19
- * mach virtualenv site-packages directory: `.pth` files execute at
20
- * interpreter startup from the venv's own site-packages, which survives
21
- * the re-exec. The PYTHONPATH `sitecustomize.py` is retained only as a
22
- * belt-and-suspenders for the pre-venv bootstrap phase (first build, no
23
- * `_virtualenvs` on disk yet).
13
+ * The guard is installed as a `fireforge_mach_guard.pth` + module pair in
14
+ * every discovered mach virtualenv site-packages directory. A PYTHONPATH
15
+ * `sitecustomize.py` is NOT sufficient on its own: mach re-execs itself into
16
+ * its private virtualenv and drops PYTHONPATH, so that route never loads.
17
+ * `.pth` files execute at interpreter startup from the venv's own
18
+ * site-packages, which survives the re-exec. The PYTHONPATH copy is retained
19
+ * only for the pre-venv bootstrap phase (first build, no `_virtualenvs` on
20
+ * disk yet).
24
21
  *
25
- * The guard itself covers the whole crash family:
22
+ * The guard covers the whole crash family:
26
23
  * - wraps `psutil.virtual_memory` / `swap_memory` / `cpu_percent` /
27
24
  * `cpu_times` / `disk_io_counters` module-wide, so every call site —
28
25
  * including the direct `psutil.virtual_memory()` in
29
26
  * `mozbuild.base._run_make` — degrades to a `UserWarning` and a zeroed
30
- * reading instead of aborting. Downstream report (0.34.0 cycle):
31
- * mozsystemmonitor subscripts (`_build_meta` does
32
- * `psutil.virtual_memory()[0]`) and iterates/unpacks the reading, so the
33
- * zeroed fallback is a full namedtuple duck type; `cpu_percent` degrades
34
- * to `0.0` because callers do arithmetic on it. Field report (0.34.1,
35
- * flapping host): the fallback must ALSO be per-function arity-correct
36
- * and reconstructible — mozsystemmonitor's parent captures reading types
37
- * at `__init__` (`self._swap_type = type(psutil.swap_memory())`) and
38
- * rebuilds each collector sample via `self._swap_type(*swap_mem)`, and an
39
- * svmem-shaped (8-field) fallback in the swap (6-field `sswap`) position
40
- * made the parent reject every sample ("failed to read the received
41
- * data") and BREAK its drain loop, so the pipe filled, the collector
42
- * child blocked in `send()` and never read the terminate sentinel, and
43
- * mach hung forever in its atexit `waitpid`. Each wrapped function now
44
- * degrades to a zeroed instance of psutil's own result namedtuple where
45
- * resolvable, else a module-level guard-owned namedtuple with the exact
46
- * macOS field order/arity for THAT function (module-level so readings
47
- * pickle by reference across the collector pipe); `_DegradedReading`
48
- * remains only as a documented last resort and now tolerates
49
- * `type(reading)(*values)` reconstruction;
27
+ * reading instead of aborting.
28
+ *
29
+ * The fallback must be per-function ARITY-CORRECT and reconstructible:
30
+ * mozsystemmonitor captures reading types at `__init__`
31
+ * (`self._swap_type = type(psutil.swap_memory())`) and rebuilds each
32
+ * collector sample via `self._swap_type(*swap_mem)`. An svmem-shaped
33
+ * (8-field) fallback in the swap (6-field `sswap`) position makes the
34
+ * parent reject every sample and break its drain loop, so the pipe fills,
35
+ * the collector child blocks in `send()` and never reads the terminate
36
+ * sentinel, and mach hangs forever in its atexit `waitpid`. Each wrapped
37
+ * function therefore degrades to a zeroed instance of psutil's own result
38
+ * namedtuple where resolvable, else a module-level guard-owned namedtuple
39
+ * with the exact field order and arity for THAT function (module-level so
40
+ * readings pickle by reference across the collector pipe).
41
+ * `_DegradedReading` remains only as a documented last resort and
42
+ * tolerates `type(reading)(*values)` reconstruction. Guard-owned fallback
43
+ * classes never depend on psutil internals resolving, which also makes
44
+ * them safe when two guard copies load (PYTHONPATH `sitecustomize` in the
45
+ * bootstrap phase plus the in-venv `.pth`), each with its own classes;
50
46
  * - guards `SystemResourceMonitor` CONSTRUCTION via an import hook:
51
47
  * `poll_interval` is pre-populated before the real `__init__` runs, a
52
- * failing `__init__` marks the instance degraded instead of raising,
53
- * and every monitor method no-ops on a degraded instance — so a
48
+ * failing `__init__` marks the instance degraded instead of raising, and
49
+ * every monitor method no-ops on a degraded instance — so a
54
50
  * partially-constructed monitor can never surface the
55
51
  * `AttributeError: poll_interval` variant. Degraded aggregate methods
56
52
  * return zeroed shapes (not `None`) where callers subscript the result
57
- * (`aggregate_io` → zeroed io reading, so mozbuild's
58
- * `log_resource_usage` no longer dies on `usage["io"].read_bytes` after
59
- * a fully successful compile);
53
+ * (`aggregate_io` → zeroed io reading, so mozbuild's `log_resource_usage`
54
+ * does not die on `usage["io"].read_bytes` after a successful compile);
60
55
  * - suppresses the mozsystemmonitor collector child on degradation: once
61
56
  * any psutil degradation is observed in the process, monitors are kept
62
57
  * inert (`start` never spawns the collector), and a degraded transition
63
58
  * or raising `stop` best-effort terminates a live collector child and
64
- * drains the pipe — so a malformed sample stream can never wedge mach's
59
+ * drains the pipe, so a malformed sample stream cannot wedge mach's
65
60
  * shutdown even if a future shape mismatch slips through;
66
- * - additionally wraps `mozbuild.controller.building.BuildMonitor
67
- * .log_resource_usage` to warn-and-continue on any exception, so
68
- * end-of-build resource reporting can never fail a build whose
69
- * artifacts are complete.
70
- *
71
- * Why the 0.34.1 report saw `_DegradedReading` in the collector child even
72
- * though the psutil result classes resolve when probed directly: two guard
73
- * copies can load (PYTHONPATH `sitecustomize` in the bootstrap phase plus
74
- * the in-venv `.pth`), each with its own factory/classes, and flapping at
75
- * guard-import time can perturb `psutil._psplatform` attribute resolution
76
- * in the spawn child specifically. The fix is robust to either mechanism:
77
- * the guard-owned fallback classes never depend on psutil internals
78
- * resolving and are arity-correct in every position.
61
+ * - wraps `mozbuild.controller.building.BuildMonitor.log_resource_usage` to
62
+ * warn-and-continue on any exception, so end-of-build resource reporting
63
+ * can never fail a build whose artifacts are complete.
79
64
  */
80
65
  /**
81
66
  * Python guard body, importable both as the in-venv `fireforge_mach_guard`
@@ -86,7 +71,7 @@
86
71
  * regression test (mach-resource-shim.python.test.ts), which asserts the
87
72
  * degraded readings' runtime behavior rather than string-matching the source.
88
73
  */
89
- export declare const GUARD_PYTHON_SOURCE = "# Generated by FireForge - do not edit.\n# Degrades the broken host resource monitor family (psutil vs Darwin) from\n# fatal startup errors into non-fatal warnings, so mach builds and tests\n# proceed. Installed into the mach virtualenv site-packages via a .pth file\n# (survives mach's venv re-exec, which drops PYTHONPATH).\nimport builtins\nimport collections\nimport sys\nimport warnings\n\n# Any psutil degradation observed in this process. Once set, monitors are\n# kept inert (start never spawns the collector child) \u2014 on a flapping host a\n# healthy-looking moment must not be trusted to start a sample stream that a\n# later degraded reading could malform.\n_fireforge_host_degraded = [False]\n\n\ndef _fireforge_degraded_notice(exc):\n _fireforge_host_degraded[0] = True\n warnings.warn(\n \"FireForge: host resource monitor degraded (%s); continuing without resource monitoring.\" % exc\n )\n\n\nclass _DegradedReading(object):\n # Last-resort duck type of psutil's namedtuple results (svmem field\n # order): mozsystemmonitor subscripts, iterates, and unpacks readings,\n # so the degraded fallback must survive r[0], list(r), len(r), r._fields\n # and r._asdict(), not just attribute access. Field report (0.34.1): the\n # parent also reconstructs readings via type(reading)(*values), so the\n # constructor must tolerate the full positional field list. Normal\n # degraded paths use the per-function namedtuple fallbacks below; this\n # class only backstops a fallback that itself raised.\n _fields = (\"total\", \"available\", \"percent\", \"used\", \"free\", \"active\", \"inactive\", \"wired\")\n total = available = used = free = active = inactive = wired = 0\n percent = 0.0\n\n def __init__(self, *_args, **_kwargs):\n pass\n\n def __getattr__(self, _name):\n return 0\n\n def _values(self):\n return tuple(getattr(self, _field) for _field in self._fields)\n\n def __getitem__(self, index):\n return self._values()[index]\n\n def __iter__(self):\n return iter(self._values())\n\n def __len__(self):\n return len(self._fields)\n\n def _asdict(self):\n return dict(zip(self._fields, self._values()))\n\n\n# Per-function fallback result classes with the exact macOS field\n# orders/arities. Module-level on purpose: readings cross the\n# mozsystemmonitor collector pipe via pickle (by reference), and the parent\n# rebuilds each sample with type(reading)(*values) \u2014 so a swap fallback must\n# be 6-field sswap-shaped in every position (field report 0.34.1: an\n# svmem-shaped fallback in the swap position made the parent reject every\n# sample, fill the pipe, block the collector child in send(), and wedge\n# mach's atexit join forever).\n_FallbackVmem = collections.namedtuple(\n \"_FallbackVmem\",\n (\"total\", \"available\", \"percent\", \"used\", \"free\", \"active\", \"inactive\", \"wired\"),\n)\n_FallbackSwap = collections.namedtuple(\n \"_FallbackSwap\", (\"total\", \"used\", \"free\", \"percent\", \"sin\", \"sout\")\n)\n_FallbackCpuTimes = collections.namedtuple(\"_FallbackCpuTimes\", (\"user\", \"nice\", \"system\", \"idle\"))\n_FallbackDiskIO = collections.namedtuple(\n \"_FallbackDiskIO\",\n (\"read_count\", \"write_count\", \"read_bytes\", \"write_bytes\", \"read_time\", \"write_time\"),\n)\n\n_FALLBACK_CLASSES = {\n \"virtual_memory\": _FallbackVmem,\n \"swap_memory\": _FallbackSwap,\n \"cpu_times\": _FallbackCpuTimes,\n \"disk_io_counters\": _FallbackDiskIO,\n}\n\n# psutil result namedtuple per wrapped function, resolvable via getattr from\n# the platform module / _common without invoking the failing syscall.\n_PSUTIL_RESULT_CLASSES = {\n \"virtual_memory\": \"svmem\",\n \"swap_memory\": \"sswap\",\n \"cpu_times\": \"scputimes\",\n \"disk_io_counters\": \"sdiskio\",\n}\n\n\ndef _zeroed(cls):\n return cls(*([0] * len(cls._fields)))\n\n\ndef _percpu_requested(args, kwargs, positional_index):\n if \"percpu\" in kwargs:\n return bool(kwargs[\"percpu\"])\n return len(args) > positional_index and bool(args[positional_index])\n\n\ndef _degraded_result_factory(psutil, name):\n # cpu_percent returns a plain float (or a per-CPU list) callers do\n # arithmetic on.\n if name == \"cpu_percent\":\n def _cpu_percent_fallback(*args, **kwargs):\n # cpu_percent(interval=None, percpu=False)\n if _percpu_requested(args, kwargs, 1):\n return []\n return 0.0\n\n return _cpu_percent_fallback\n # The guard-owned class is always shape-correct for this function;\n # psutil's own result namedtuple is still preferred when resolvable so\n # type() captures stay identical to real readings on a flapping host.\n _cls = _FALLBACK_CLASSES.get(name, _DegradedReading)\n _cls_name = _PSUTIL_RESULT_CLASSES.get(name)\n if _cls_name is not None:\n for _mod in (getattr(psutil, \"_psplatform\", None), getattr(psutil, \"_common\", None)):\n _real = getattr(_mod, _cls_name, None) if _mod is not None else None\n if _real is not None and getattr(_real, \"_fields\", None):\n _cls = _real\n break\n if name == \"cpu_times\":\n def _cpu_times_fallback(*args, **kwargs):\n # cpu_times(percpu=False); the collector child samples per-CPU.\n if _percpu_requested(args, kwargs, 0):\n return []\n return _zeroed(_cls)\n\n return _cpu_times_fallback\n\n def _reading_fallback(*_args, **_kwargs):\n return _zeroed(_cls)\n\n return _reading_fallback\n\n\ndef _guard_psutil():\n try:\n import psutil\n except Exception:\n return\n\n def _wrap(orig, fallback):\n def wrapper(*args, **kwargs):\n try:\n return orig(*args, **kwargs)\n except Exception as exc: # noqa: BLE001\n _fireforge_degraded_notice(exc)\n try:\n return fallback(*args, **kwargs)\n except Exception: # noqa: BLE001\n return _DegradedReading()\n\n return wrapper\n\n for _name in (\"virtual_memory\", \"swap_memory\", \"cpu_percent\", \"cpu_times\", \"disk_io_counters\"):\n _orig = getattr(psutil, _name, None)\n if _orig is not None and not getattr(_orig, \"_fireforge_guarded\", False):\n try:\n _fallback = _degraded_result_factory(psutil, _name)\n except Exception: # noqa: BLE001\n _fallback = _DegradedReading\n _wrapped = _wrap(_orig, _fallback)\n _wrapped._fireforge_guarded = True\n setattr(psutil, _name, _wrapped)\n\n\n_MONITOR_METHOD_NAMES = (\n \"start\",\n \"stop\",\n \"record_event\",\n \"record_marker\",\n \"begin_phase\",\n \"finish_phase\",\n \"aggregate_cpu_percent\",\n \"aggregate_cpu_times\",\n \"aggregate_io\",\n \"min_memory_available\",\n \"as_dict\",\n)\n\n\ndef _fireforge_stop_collector(monitor):\n # Best-effort: terminate a live collector child and drain the parent end\n # of the pipe, so a child blocked in send() on a full pipe can never\n # keep mach's atexit join (os.waitpid) waiting forever. Attribute names\n # follow upstream resourcemonitor.py (self._process / self._pipe); a\n # miss on a refactored upstream is harmless \u2014 the shape-correct\n # fallbacks above still keep the sample stream well-formed.\n try:\n proc = getattr(monitor, \"_process\", None)\n if proc is not None and getattr(proc, \"is_alive\", lambda: False)():\n proc.terminate()\n proc.join(1)\n except Exception: # noqa: BLE001\n pass\n try:\n pipe = getattr(monitor, \"_pipe\", None)\n if pipe is not None:\n while pipe.poll(0):\n pipe.recv()\n except Exception: # noqa: BLE001\n pass\n\n\ndef _monitor_per_cpu_requested(args, kwargs):\n # aggregate_cpu_percent/aggregate_cpu_times(start=None, end=None,\n # phase=None, per_cpu=True) \u2014 self already stripped from args.\n if \"per_cpu\" in kwargs:\n return bool(kwargs[\"per_cpu\"])\n if len(args) > 3:\n return bool(args[3])\n return True\n\n\ndef _monitor_degraded_result(name, args, kwargs):\n # Degraded aggregate methods return zeroed shapes, not None, where\n # callers subscript the result: mozbuild's log_resource_usage does\n # usage[\"io\"].read_bytes after a successful compile (field report\n # 0.34.1: 'NoneType' object has no attribute 'read_bytes' failed the\n # build with complete artifacts).\n if name == \"aggregate_io\":\n return _zeroed(_FallbackDiskIO)\n if name == \"aggregate_cpu_percent\":\n return [] if _monitor_per_cpu_requested(args, kwargs) else 0.0\n if name == \"aggregate_cpu_times\":\n return [] if _monitor_per_cpu_requested(args, kwargs) else _zeroed(_FallbackCpuTimes)\n return None\n\n\ndef _patch_monitor_class(cls):\n if cls is None or getattr(cls, \"_fireforge_guarded\", False):\n return\n orig_init = cls.__init__\n\n def guarded_init(self, *args, **kwargs):\n # Pre-populate the attributes a partially-constructed instance is\n # later polled for, so a failing __init__ can never surface\n # AttributeError: poll_interval.\n self.poll_interval = kwargs.get(\"poll_interval\") or 1.0\n self._fireforge_degraded = False\n try:\n orig_init(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n self._fireforge_degraded = True\n _fireforge_degraded_notice(exc)\n if _fireforge_host_degraded[0] and not self._fireforge_degraded:\n # Host already degraded once: keep the monitor inert so the\n # collector child never spawns on a flapping host.\n self._fireforge_degraded = True\n if self._fireforge_degraded:\n _fireforge_stop_collector(self)\n\n cls.__init__ = guarded_init\n\n def _wrap_method(name, orig):\n def wrapper(self, *args, **kwargs):\n if (\n name == \"start\"\n and _fireforge_host_degraded[0]\n and not getattr(self, \"_fireforge_degraded\", False)\n ):\n self._fireforge_degraded = True\n _fireforge_stop_collector(self)\n if getattr(self, \"_fireforge_degraded\", False):\n if name == \"stop\":\n _fireforge_stop_collector(self)\n return _monitor_degraded_result(name, args, kwargs)\n try:\n return orig(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n self._fireforge_degraded = True\n _fireforge_degraded_notice(exc)\n # A raising stop (or any degraded transition) must still\n # take the collector child down, or the parent's atexit\n # join hangs on a child blocked writing to a full pipe.\n _fireforge_stop_collector(self)\n return _monitor_degraded_result(name, args, kwargs)\n\n return wrapper\n\n for _name in _MONITOR_METHOD_NAMES:\n _orig = getattr(cls, _name, None)\n if _orig is not None:\n setattr(cls, _name, _wrap_method(_name, _orig))\n cls._fireforge_guarded = True\n\n\ndef _patch_build_monitor_class(cls):\n # mozbuild's BuildMonitor is not a SystemResourceMonitor; only\n # log_resource_usage needs guarding \u2014 end-of-build resource reporting\n # must never fail a build whose artifacts are complete. No __init__\n # replacement, no degraded-instance gating.\n if cls is None or getattr(cls, \"_fireforge_guarded\", False):\n return\n _orig = getattr(cls, \"log_resource_usage\", None)\n if _orig is not None:\n def _guarded_log_resource_usage(self, *args, **kwargs):\n try:\n return _orig(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n _fireforge_degraded_notice(exc)\n return None\n\n cls.log_resource_usage = _guarded_log_resource_usage\n cls._fireforge_guarded = True\n\n\n_MONITOR_CLASS_PATCHES = (\n (\"mozsystemmonitor.resourcemonitor\", \"SystemResourceMonitor\", _patch_monitor_class),\n (\"mozbuild.resources\", \"SystemResourceMonitor\", _patch_monitor_class),\n (\"mozbuild.controller.building\", \"BuildMonitor\", _patch_build_monitor_class),\n)\n\n\ndef _patch_loaded_monitor_modules():\n for _mod_name, _cls_name, _patcher in _MONITOR_CLASS_PATCHES:\n _mod = sys.modules.get(_mod_name)\n if _mod is not None:\n try:\n _patcher(getattr(_mod, _cls_name, None))\n except Exception: # noqa: BLE001\n pass\n\n\ndef _install_import_hook():\n _orig_import = builtins.__import__\n if getattr(_orig_import, \"_fireforge_guarded\", False):\n return\n\n def _guarding_import(name, *args, **kwargs):\n _module = _orig_import(name, *args, **kwargs)\n try:\n _patch_loaded_monitor_modules()\n except Exception: # noqa: BLE001\n pass\n return _module\n\n _guarding_import._fireforge_guarded = True\n builtins.__import__ = _guarding_import\n\n\ntry:\n _guard_psutil()\n _patch_loaded_monitor_modules()\n _install_import_hook()\nexcept Exception: # noqa: BLE001\n pass\n";
74
+ export declare const GUARD_PYTHON_SOURCE = "# Generated by FireForge - do not edit.\n# Degrades the broken host resource monitor family (psutil vs Darwin) from\n# fatal startup errors into non-fatal warnings, so mach builds and tests\n# proceed. Installed into the mach virtualenv site-packages via a .pth file\n# (survives mach's venv re-exec, which drops PYTHONPATH).\nimport builtins\nimport collections\nimport sys\nimport warnings\n\n# Any psutil degradation observed in this process. Once set, monitors are\n# kept inert (start never spawns the collector child) \u2014 on a flapping host a\n# healthy-looking moment must not be trusted to start a sample stream that a\n# later degraded reading could malform.\n_fireforge_host_degraded = [False]\n\n\ndef _fireforge_degraded_notice(exc):\n _fireforge_host_degraded[0] = True\n warnings.warn(\n \"FireForge: host resource monitor degraded (%s); continuing without resource monitoring.\" % exc\n )\n\n\nclass _DegradedReading(object):\n # Last-resort duck type of psutil's namedtuple results (svmem field\n # order): mozsystemmonitor subscripts, iterates, and unpacks readings,\n # so the degraded fallback must survive r[0], list(r), len(r), r._fields\n # and r._asdict(), not just attribute access. The parent also reconstructs\n # readings via type(reading)(*values), so the constructor must tolerate\n # the full positional field list. Normal\n # degraded paths use the per-function namedtuple fallbacks below; this\n # class only backstops a fallback that itself raised.\n _fields = (\"total\", \"available\", \"percent\", \"used\", \"free\", \"active\", \"inactive\", \"wired\")\n total = available = used = free = active = inactive = wired = 0\n percent = 0.0\n\n def __init__(self, *_args, **_kwargs):\n pass\n\n def __getattr__(self, _name):\n return 0\n\n def _values(self):\n return tuple(getattr(self, _field) for _field in self._fields)\n\n def __getitem__(self, index):\n return self._values()[index]\n\n def __iter__(self):\n return iter(self._values())\n\n def __len__(self):\n return len(self._fields)\n\n def _asdict(self):\n return dict(zip(self._fields, self._values()))\n\n\n# Per-function fallback result classes with the exact macOS field\n# orders/arities. Module-level on purpose: readings cross the\n# mozsystemmonitor collector pipe via pickle (by reference), and the parent\n# rebuilds each sample with type(reading)(*values) \u2014 so a swap fallback must\n# be 6-field sswap-shaped in every position. An svmem-shaped fallback there\n# makes the parent reject every sample, fill the pipe, block the collector\n# child in send(), and wedge mach's atexit join forever.\n_FallbackVmem = collections.namedtuple(\n \"_FallbackVmem\",\n (\"total\", \"available\", \"percent\", \"used\", \"free\", \"active\", \"inactive\", \"wired\"),\n)\n_FallbackSwap = collections.namedtuple(\n \"_FallbackSwap\", (\"total\", \"used\", \"free\", \"percent\", \"sin\", \"sout\")\n)\n_FallbackCpuTimes = collections.namedtuple(\"_FallbackCpuTimes\", (\"user\", \"nice\", \"system\", \"idle\"))\n_FallbackDiskIO = collections.namedtuple(\n \"_FallbackDiskIO\",\n (\"read_count\", \"write_count\", \"read_bytes\", \"write_bytes\", \"read_time\", \"write_time\"),\n)\n\n_FALLBACK_CLASSES = {\n \"virtual_memory\": _FallbackVmem,\n \"swap_memory\": _FallbackSwap,\n \"cpu_times\": _FallbackCpuTimes,\n \"disk_io_counters\": _FallbackDiskIO,\n}\n\n# psutil result namedtuple per wrapped function, resolvable via getattr from\n# the platform module / _common without invoking the failing syscall.\n_PSUTIL_RESULT_CLASSES = {\n \"virtual_memory\": \"svmem\",\n \"swap_memory\": \"sswap\",\n \"cpu_times\": \"scputimes\",\n \"disk_io_counters\": \"sdiskio\",\n}\n\n\ndef _zeroed(cls):\n return cls(*([0] * len(cls._fields)))\n\n\ndef _percpu_requested(args, kwargs, positional_index):\n if \"percpu\" in kwargs:\n return bool(kwargs[\"percpu\"])\n return len(args) > positional_index and bool(args[positional_index])\n\n\ndef _degraded_result_factory(psutil, name):\n # cpu_percent returns a plain float (or a per-CPU list) callers do\n # arithmetic on.\n if name == \"cpu_percent\":\n def _cpu_percent_fallback(*args, **kwargs):\n # cpu_percent(interval=None, percpu=False)\n if _percpu_requested(args, kwargs, 1):\n return []\n return 0.0\n\n return _cpu_percent_fallback\n # The guard-owned class is always shape-correct for this function;\n # psutil's own result namedtuple is still preferred when resolvable so\n # type() captures stay identical to real readings on a flapping host.\n _cls = _FALLBACK_CLASSES.get(name, _DegradedReading)\n _cls_name = _PSUTIL_RESULT_CLASSES.get(name)\n if _cls_name is not None:\n for _mod in (getattr(psutil, \"_psplatform\", None), getattr(psutil, \"_common\", None)):\n _real = getattr(_mod, _cls_name, None) if _mod is not None else None\n if _real is not None and getattr(_real, \"_fields\", None):\n _cls = _real\n break\n if name == \"cpu_times\":\n def _cpu_times_fallback(*args, **kwargs):\n # cpu_times(percpu=False); the collector child samples per-CPU.\n if _percpu_requested(args, kwargs, 0):\n return []\n return _zeroed(_cls)\n\n return _cpu_times_fallback\n\n def _reading_fallback(*_args, **_kwargs):\n return _zeroed(_cls)\n\n return _reading_fallback\n\n\ndef _guard_psutil():\n try:\n import psutil\n except Exception:\n return\n\n def _wrap(orig, fallback):\n def wrapper(*args, **kwargs):\n try:\n return orig(*args, **kwargs)\n except Exception as exc: # noqa: BLE001\n _fireforge_degraded_notice(exc)\n try:\n return fallback(*args, **kwargs)\n except Exception: # noqa: BLE001\n return _DegradedReading()\n\n return wrapper\n\n for _name in (\"virtual_memory\", \"swap_memory\", \"cpu_percent\", \"cpu_times\", \"disk_io_counters\"):\n _orig = getattr(psutil, _name, None)\n if _orig is not None and not getattr(_orig, \"_fireforge_guarded\", False):\n try:\n _fallback = _degraded_result_factory(psutil, _name)\n except Exception: # noqa: BLE001\n _fallback = _DegradedReading\n _wrapped = _wrap(_orig, _fallback)\n _wrapped._fireforge_guarded = True\n setattr(psutil, _name, _wrapped)\n\n\n_MONITOR_METHOD_NAMES = (\n \"start\",\n \"stop\",\n \"record_event\",\n \"record_marker\",\n \"begin_phase\",\n \"finish_phase\",\n \"aggregate_cpu_percent\",\n \"aggregate_cpu_times\",\n \"aggregate_io\",\n \"min_memory_available\",\n \"as_dict\",\n)\n\n\ndef _fireforge_stop_collector(monitor):\n # Best-effort: terminate a live collector child and drain the parent end\n # of the pipe, so a child blocked in send() on a full pipe can never\n # keep mach's atexit join (os.waitpid) waiting forever. Attribute names\n # follow upstream resourcemonitor.py (self._process / self._pipe); a\n # miss on a refactored upstream is harmless \u2014 the shape-correct\n # fallbacks above still keep the sample stream well-formed.\n try:\n proc = getattr(monitor, \"_process\", None)\n if proc is not None and getattr(proc, \"is_alive\", lambda: False)():\n proc.terminate()\n proc.join(1)\n except Exception: # noqa: BLE001\n pass\n try:\n pipe = getattr(monitor, \"_pipe\", None)\n if pipe is not None:\n while pipe.poll(0):\n pipe.recv()\n except Exception: # noqa: BLE001\n pass\n\n\ndef _monitor_per_cpu_requested(args, kwargs):\n # aggregate_cpu_percent/aggregate_cpu_times(start=None, end=None,\n # phase=None, per_cpu=True) \u2014 self already stripped from args.\n if \"per_cpu\" in kwargs:\n return bool(kwargs[\"per_cpu\"])\n if len(args) > 3:\n return bool(args[3])\n return True\n\n\ndef _monitor_degraded_result(name, args, kwargs):\n # Degraded aggregate methods return zeroed shapes, not None, where\n # callers subscript the result: mozbuild's log_resource_usage does\n # usage[\"io\"].read_bytes after a successful compile, so None there fails\n # the build with complete artifacts already on disk.\n if name == \"aggregate_io\":\n return _zeroed(_FallbackDiskIO)\n if name == \"aggregate_cpu_percent\":\n return [] if _monitor_per_cpu_requested(args, kwargs) else 0.0\n if name == \"aggregate_cpu_times\":\n return [] if _monitor_per_cpu_requested(args, kwargs) else _zeroed(_FallbackCpuTimes)\n return None\n\n\ndef _patch_monitor_class(cls):\n if cls is None or getattr(cls, \"_fireforge_guarded\", False):\n return\n orig_init = cls.__init__\n\n def guarded_init(self, *args, **kwargs):\n # Pre-populate the attributes a partially-constructed instance is\n # later polled for, so a failing __init__ can never surface\n # AttributeError: poll_interval.\n self.poll_interval = kwargs.get(\"poll_interval\") or 1.0\n self._fireforge_degraded = False\n try:\n orig_init(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n self._fireforge_degraded = True\n _fireforge_degraded_notice(exc)\n if _fireforge_host_degraded[0] and not self._fireforge_degraded:\n # Host already degraded once: keep the monitor inert so the\n # collector child never spawns on a flapping host.\n self._fireforge_degraded = True\n if self._fireforge_degraded:\n _fireforge_stop_collector(self)\n\n cls.__init__ = guarded_init\n\n def _wrap_method(name, orig):\n def wrapper(self, *args, **kwargs):\n if (\n name == \"start\"\n and _fireforge_host_degraded[0]\n and not getattr(self, \"_fireforge_degraded\", False)\n ):\n self._fireforge_degraded = True\n _fireforge_stop_collector(self)\n if getattr(self, \"_fireforge_degraded\", False):\n if name == \"stop\":\n _fireforge_stop_collector(self)\n return _monitor_degraded_result(name, args, kwargs)\n try:\n return orig(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n self._fireforge_degraded = True\n _fireforge_degraded_notice(exc)\n # A raising stop (or any degraded transition) must still\n # take the collector child down, or the parent's atexit\n # join hangs on a child blocked writing to a full pipe.\n _fireforge_stop_collector(self)\n return _monitor_degraded_result(name, args, kwargs)\n\n return wrapper\n\n for _name in _MONITOR_METHOD_NAMES:\n _orig = getattr(cls, _name, None)\n if _orig is not None:\n setattr(cls, _name, _wrap_method(_name, _orig))\n cls._fireforge_guarded = True\n\n\ndef _patch_build_monitor_class(cls):\n # mozbuild's BuildMonitor is not a SystemResourceMonitor; only\n # log_resource_usage needs guarding \u2014 end-of-build resource reporting\n # must never fail a build whose artifacts are complete. No __init__\n # replacement, no degraded-instance gating.\n if cls is None or getattr(cls, \"_fireforge_guarded\", False):\n return\n _orig = getattr(cls, \"log_resource_usage\", None)\n if _orig is not None:\n def _guarded_log_resource_usage(self, *args, **kwargs):\n try:\n return _orig(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n _fireforge_degraded_notice(exc)\n return None\n\n cls.log_resource_usage = _guarded_log_resource_usage\n cls._fireforge_guarded = True\n\n\n_MONITOR_CLASS_PATCHES = (\n (\"mozsystemmonitor.resourcemonitor\", \"SystemResourceMonitor\", _patch_monitor_class),\n (\"mozbuild.resources\", \"SystemResourceMonitor\", _patch_monitor_class),\n (\"mozbuild.controller.building\", \"BuildMonitor\", _patch_build_monitor_class),\n)\n\n\ndef _patch_loaded_monitor_modules():\n for _mod_name, _cls_name, _patcher in _MONITOR_CLASS_PATCHES:\n _mod = sys.modules.get(_mod_name)\n if _mod is not None:\n try:\n _patcher(getattr(_mod, _cls_name, None))\n except Exception: # noqa: BLE001\n pass\n\n\ndef _install_import_hook():\n _orig_import = builtins.__import__\n if getattr(_orig_import, \"_fireforge_guarded\", False):\n return\n\n def _guarding_import(name, *args, **kwargs):\n _module = _orig_import(name, *args, **kwargs)\n try:\n _patch_loaded_monitor_modules()\n except Exception: # noqa: BLE001\n pass\n return _module\n\n _guarding_import._fireforge_guarded = True\n builtins.__import__ = _guarding_import\n\n\ntry:\n _guard_psutil()\n _patch_loaded_monitor_modules()\n _install_import_hook()\nexcept Exception: # noqa: BLE001\n pass\n";
90
75
  /** Result of installing the mach resource guard before a dispatch. */
91
76
  export interface MachResourceGuardInstallation {
92
77
  /**
@@ -4,79 +4,64 @@
4
4
  *
5
5
  * mozbuild's build resource monitor calls `psutil.virtual_memory()` at
6
6
  * startup (`start_resource_recording`) and again later from
7
- * `mozbuild.base._run_make`. On some hosts (psutil vs Darwin 27) those
8
- * raise `RuntimeError: host_statistics64(HOST_VM_INFO64) syscall failed`,
9
- * and a `SystemResourceMonitor.__init__` that fails partway leaves an
10
- * instance without `poll_interval`, so later polling dies with
11
- * `AttributeError: ... poll_interval`. Any of these aborts `mach build` /
12
- * `mach build faster` before the compiler ever runs.
7
+ * `mozbuild.base._run_make`. On some hosts those raise `RuntimeError:
8
+ * host_statistics64(HOST_VM_INFO64) syscall failed`, and a
9
+ * `SystemResourceMonitor.__init__` that fails partway leaves an instance
10
+ * without `poll_interval`, so later polling dies with `AttributeError: ...
11
+ * poll_interval`. Any of these aborts `mach build` / `mach build faster`
12
+ * before the compiler ever runs.
13
13
  *
14
- * 0.33.0 injected a `sitecustomize.py` via the mach subprocess PYTHONPATH.
15
- * Field report (0.34.0 cycle): mach re-execs itself into its private
16
- * virtualenv and drops PYTHONPATH, so the env-var route never loads and
17
- * the crash family escaped on every protected entry point. FireForge now
18
- * owns the guard in-process on its mach dispatches by installing a
19
- * `fireforge_mach_guard.pth` + module pair directly into every discovered
20
- * mach virtualenv site-packages directory: `.pth` files execute at
21
- * interpreter startup from the venv's own site-packages, which survives
22
- * the re-exec. The PYTHONPATH `sitecustomize.py` is retained only as a
23
- * belt-and-suspenders for the pre-venv bootstrap phase (first build, no
24
- * `_virtualenvs` on disk yet).
14
+ * The guard is installed as a `fireforge_mach_guard.pth` + module pair in
15
+ * every discovered mach virtualenv site-packages directory. A PYTHONPATH
16
+ * `sitecustomize.py` is NOT sufficient on its own: mach re-execs itself into
17
+ * its private virtualenv and drops PYTHONPATH, so that route never loads.
18
+ * `.pth` files execute at interpreter startup from the venv's own
19
+ * site-packages, which survives the re-exec. The PYTHONPATH copy is retained
20
+ * only for the pre-venv bootstrap phase (first build, no `_virtualenvs` on
21
+ * disk yet).
25
22
  *
26
- * The guard itself covers the whole crash family:
23
+ * The guard covers the whole crash family:
27
24
  * - wraps `psutil.virtual_memory` / `swap_memory` / `cpu_percent` /
28
25
  * `cpu_times` / `disk_io_counters` module-wide, so every call site —
29
26
  * including the direct `psutil.virtual_memory()` in
30
27
  * `mozbuild.base._run_make` — degrades to a `UserWarning` and a zeroed
31
- * reading instead of aborting. Downstream report (0.34.0 cycle):
32
- * mozsystemmonitor subscripts (`_build_meta` does
33
- * `psutil.virtual_memory()[0]`) and iterates/unpacks the reading, so the
34
- * zeroed fallback is a full namedtuple duck type; `cpu_percent` degrades
35
- * to `0.0` because callers do arithmetic on it. Field report (0.34.1,
36
- * flapping host): the fallback must ALSO be per-function arity-correct
37
- * and reconstructible — mozsystemmonitor's parent captures reading types
38
- * at `__init__` (`self._swap_type = type(psutil.swap_memory())`) and
39
- * rebuilds each collector sample via `self._swap_type(*swap_mem)`, and an
40
- * svmem-shaped (8-field) fallback in the swap (6-field `sswap`) position
41
- * made the parent reject every sample ("failed to read the received
42
- * data") and BREAK its drain loop, so the pipe filled, the collector
43
- * child blocked in `send()` and never read the terminate sentinel, and
44
- * mach hung forever in its atexit `waitpid`. Each wrapped function now
45
- * degrades to a zeroed instance of psutil's own result namedtuple where
46
- * resolvable, else a module-level guard-owned namedtuple with the exact
47
- * macOS field order/arity for THAT function (module-level so readings
48
- * pickle by reference across the collector pipe); `_DegradedReading`
49
- * remains only as a documented last resort and now tolerates
50
- * `type(reading)(*values)` reconstruction;
28
+ * reading instead of aborting.
29
+ *
30
+ * The fallback must be per-function ARITY-CORRECT and reconstructible:
31
+ * mozsystemmonitor captures reading types at `__init__`
32
+ * (`self._swap_type = type(psutil.swap_memory())`) and rebuilds each
33
+ * collector sample via `self._swap_type(*swap_mem)`. An svmem-shaped
34
+ * (8-field) fallback in the swap (6-field `sswap`) position makes the
35
+ * parent reject every sample and break its drain loop, so the pipe fills,
36
+ * the collector child blocks in `send()` and never reads the terminate
37
+ * sentinel, and mach hangs forever in its atexit `waitpid`. Each wrapped
38
+ * function therefore degrades to a zeroed instance of psutil's own result
39
+ * namedtuple where resolvable, else a module-level guard-owned namedtuple
40
+ * with the exact field order and arity for THAT function (module-level so
41
+ * readings pickle by reference across the collector pipe).
42
+ * `_DegradedReading` remains only as a documented last resort and
43
+ * tolerates `type(reading)(*values)` reconstruction. Guard-owned fallback
44
+ * classes never depend on psutil internals resolving, which also makes
45
+ * them safe when two guard copies load (PYTHONPATH `sitecustomize` in the
46
+ * bootstrap phase plus the in-venv `.pth`), each with its own classes;
51
47
  * - guards `SystemResourceMonitor` CONSTRUCTION via an import hook:
52
48
  * `poll_interval` is pre-populated before the real `__init__` runs, a
53
- * failing `__init__` marks the instance degraded instead of raising,
54
- * and every monitor method no-ops on a degraded instance — so a
49
+ * failing `__init__` marks the instance degraded instead of raising, and
50
+ * every monitor method no-ops on a degraded instance — so a
55
51
  * partially-constructed monitor can never surface the
56
52
  * `AttributeError: poll_interval` variant. Degraded aggregate methods
57
53
  * return zeroed shapes (not `None`) where callers subscript the result
58
- * (`aggregate_io` → zeroed io reading, so mozbuild's
59
- * `log_resource_usage` no longer dies on `usage["io"].read_bytes` after
60
- * a fully successful compile);
54
+ * (`aggregate_io` → zeroed io reading, so mozbuild's `log_resource_usage`
55
+ * does not die on `usage["io"].read_bytes` after a successful compile);
61
56
  * - suppresses the mozsystemmonitor collector child on degradation: once
62
57
  * any psutil degradation is observed in the process, monitors are kept
63
58
  * inert (`start` never spawns the collector), and a degraded transition
64
59
  * or raising `stop` best-effort terminates a live collector child and
65
- * drains the pipe — so a malformed sample stream can never wedge mach's
60
+ * drains the pipe, so a malformed sample stream cannot wedge mach's
66
61
  * shutdown even if a future shape mismatch slips through;
67
- * - additionally wraps `mozbuild.controller.building.BuildMonitor
68
- * .log_resource_usage` to warn-and-continue on any exception, so
69
- * end-of-build resource reporting can never fail a build whose
70
- * artifacts are complete.
71
- *
72
- * Why the 0.34.1 report saw `_DegradedReading` in the collector child even
73
- * though the psutil result classes resolve when probed directly: two guard
74
- * copies can load (PYTHONPATH `sitecustomize` in the bootstrap phase plus
75
- * the in-venv `.pth`), each with its own factory/classes, and flapping at
76
- * guard-import time can perturb `psutil._psplatform` attribute resolution
77
- * in the spawn child specifically. The fix is robust to either mechanism:
78
- * the guard-owned fallback classes never depend on psutil internals
79
- * resolving and are arity-correct in every position.
62
+ * - wraps `mozbuild.controller.building.BuildMonitor.log_resource_usage` to
63
+ * warn-and-continue on any exception, so end-of-build resource reporting
64
+ * can never fail a build whose artifacts are complete.
80
65
  */
81
66
  import { readdir } from 'node:fs/promises';
82
67
  import { homedir, tmpdir } from 'node:os';
@@ -125,9 +110,9 @@ class _DegradedReading(object):
125
110
  # Last-resort duck type of psutil's namedtuple results (svmem field
126
111
  # order): mozsystemmonitor subscripts, iterates, and unpacks readings,
127
112
  # so the degraded fallback must survive r[0], list(r), len(r), r._fields
128
- # and r._asdict(), not just attribute access. Field report (0.34.1): the
129
- # parent also reconstructs readings via type(reading)(*values), so the
130
- # constructor must tolerate the full positional field list. Normal
113
+ # and r._asdict(), not just attribute access. The parent also reconstructs
114
+ # readings via type(reading)(*values), so the constructor must tolerate
115
+ # the full positional field list. Normal
131
116
  # degraded paths use the per-function namedtuple fallbacks below; this
132
117
  # class only backstops a fallback that itself raised.
133
118
  _fields = ("total", "available", "percent", "used", "free", "active", "inactive", "wired")
@@ -160,10 +145,9 @@ class _DegradedReading(object):
160
145
  # orders/arities. Module-level on purpose: readings cross the
161
146
  # mozsystemmonitor collector pipe via pickle (by reference), and the parent
162
147
  # rebuilds each sample with type(reading)(*values) — so a swap fallback must
163
- # be 6-field sswap-shaped in every position (field report 0.34.1: an
164
- # svmem-shaped fallback in the swap position made the parent reject every
165
- # sample, fill the pipe, block the collector child in send(), and wedge
166
- # mach's atexit join forever).
148
+ # be 6-field sswap-shaped in every position. An svmem-shaped fallback there
149
+ # makes the parent reject every sample, fill the pipe, block the collector
150
+ # child in send(), and wedge mach's atexit join forever.
167
151
  _FallbackVmem = collections.namedtuple(
168
152
  "_FallbackVmem",
169
153
  ("total", "available", "percent", "used", "free", "active", "inactive", "wired"),
@@ -323,9 +307,8 @@ def _monitor_per_cpu_requested(args, kwargs):
323
307
  def _monitor_degraded_result(name, args, kwargs):
324
308
  # Degraded aggregate methods return zeroed shapes, not None, where
325
309
  # callers subscript the result: mozbuild's log_resource_usage does
326
- # usage["io"].read_bytes after a successful compile (field report
327
- # 0.34.1: 'NoneType' object has no attribute 'read_bytes' failed the
328
- # build with complete artifacts).
310
+ # usage["io"].read_bytes after a successful compile, so None there fails
311
+ # the build with complete artifacts already on disk.
329
312
  if name == "aggregate_io":
330
313
  return _zeroed(_FallbackDiskIO)
331
314
  if name == "aggregate_cpu_percent":
@@ -86,21 +86,20 @@ export interface ProtectedMachBuildResult extends MachCommandResult {
86
86
  }
87
87
  /**
88
88
  * The single protected path every FireForge mach build dispatch routes
89
- * through (field report: `build --ui` and the pre-test `--build` step died
90
- * on the resource-monitor crash family while plain `build` survived,
91
- * because only some entries were protected and retry budgets differed):
89
+ * through — `build`, `build --ui`, and the pre-test `--build` step all use
90
+ * it, so no entry point is left unprotected with a different retry budget:
92
91
  *
93
92
  * 1. installs the resource-monitor degrade guard IN the mach virtualenvs
94
- * (plus the PYTHONPATH fallback) — re-installed before every attempt,
95
- * so a venv materialized by a crashed first attempt is guarded on the
96
- * next one instead of every retry dying on the same wedged state;
93
+ * (plus the PYTHONPATH fallback) — re-installed before every attempt, so
94
+ * a venv materialized by a crashed first attempt is guarded on the next
95
+ * one instead of every retry dying on the same wedged state;
97
96
  * 2. spawns a fresh mach process per attempt;
98
97
  * 3. retries ONLY the recognized harness-crash family (resource monitor /
99
98
  * psutil startup tracebacks) up to the uniform budget — an ordinary
100
99
  * compile error is never retried.
101
100
  *
102
- * The protected path never runs `mach configure`, never clobbers, and
103
- * never widens the requested build kind — a `faster` dispatch retries as
101
+ * The protected path never runs `mach configure`, never clobbers, and never
102
+ * widens the requested build kind — a `faster` dispatch retries as
104
103
  * `mach build faster`, so it cannot invalidate more of the objdir than the
105
104
  * command the operator asked for.
106
105
  */
@@ -129,32 +128,27 @@ export declare function build(engineDir: string, jobs?: number): Promise<Protect
129
128
  */
130
129
  export declare function buildUI(engineDir: string): Promise<ProtectedMachBuildResult>;
131
130
  /**
132
- * Runs an operation while holding a sidecar build lock keyed on the
133
- * project root. Concurrent `fireforge build` / `fireforge build --ui`
134
- * invocations against the same tree serialise instead of racing through
135
- * the mach obj-dir.
131
+ * Runs an operation while holding a sidecar build lock keyed on the project
132
+ * root. Concurrent `fireforge build` / `fireforge build --ui` invocations
133
+ * against the same tree serialise instead of racing through the mach
134
+ * obj-dir.
136
135
  *
137
- * Motivating case (2026-04-21 eval): a `fireforge build --ui` run
138
- * kicked off while a full `fireforge build` was still in flight against
139
- * the same engine tree accepted the command and handed off to `mach
140
- * build faster`, which failed almost immediately with `No rule to make
141
- * target 'XUL'`. The real problem is that the first build had not yet
142
- * materialised the full backend; the operator was left staring at a
143
- * low-level make error with no link to the actual cause (a concurrent
144
- * build in flight). The lock intercepts the second invocation before
145
- * it touches mach, and the refusal message names the PID currently
146
- * holding the lock so the operator can decide whether to wait or
147
- * investigate a hung process.
136
+ * Without it, a `build --ui` started while a full `build` is still in flight
137
+ * hands off to `mach build faster` and fails almost immediately with `No
138
+ * rule to make target 'XUL'` — the real problem being that the first build
139
+ * has not yet materialised the full backend, with nothing in the low-level
140
+ * make error pointing at the concurrent run. The lock intercepts the second
141
+ * invocation before it touches mach, and the refusal names the PID holding
142
+ * the lock so the operator can decide whether to wait or investigate.
148
143
  *
149
144
  * Stale-lock recovery: the lock stores the owner PID; a crashed build
150
- * (SIGINT, SIGTERM, or a kernel kill) leaves the lock dir behind but
151
- * not the owning process, and `withFileLock` removes the lock on the
152
- * next attempt when `process.kill(pid, 0)` shows the owner is gone.
145
+ * (SIGINT, SIGTERM, or a kernel kill) leaves the lock dir behind but not the
146
+ * owning process, and `withFileLock` removes the lock on the next attempt
147
+ * when `process.kill(pid, 0)` shows the owner is gone.
153
148
  *
154
- * The project-root variant is the right granularity: a single machine
155
- * may have several FireForge projects side by side, and nothing says
156
- * they cannot build in parallel. The lock serialises *within* one
157
- * project, not across unrelated ones.
149
+ * The project-root variant is the right granularity: a single machine may
150
+ * have several FireForge projects side by side and nothing says they cannot
151
+ * build in parallel. The lock serialises *within* one project.
158
152
  *
159
153
  * Returns whatever the inner operation returns.
160
154
  */
@@ -216,18 +210,17 @@ export declare function machPackageCapture(engineDir: string): Promise<MachComma
216
210
  export declare function watch(engineDir: string): Promise<number>;
217
211
  /**
218
212
  * Runs mach watch while preserving stdin and capturing emitted output.
213
+ *
214
+ * `env` is threaded through so the caller can prepend the resolved watchman
215
+ * directory to PATH in a way mach inherits: `fireforge watch` can locate
216
+ * `watchman` via `which`, but the mach subprocess spawns with the parent's
217
+ * PATH only — on macOS that typically omits `/opt/homebrew/bin`, so
218
+ * `mach watch` fails at the `watch-project` subscription step.
219
+ *
219
220
  * @param engineDir - Path to the engine directory
220
- * @param options - Optional environment overrides merged into the mach subprocess env
221
+ * @param options - Optional environment overrides merged into the mach
222
+ * subprocess env
221
223
  * @returns Captured output and exit code
222
- *
223
- * 2026-04-24 eval Finding 12: the pre-0.18.1 shape accepted no options
224
- * and so never forwarded the detected watchman path into the mach
225
- * subprocess env. `fireforge watch` could locate `watchman` via PATH
226
- * (the probe's `which` succeeded) but the mach subprocess spawned with
227
- * the parent's PATH only — on macOS that typically omits
228
- * `/opt/homebrew/bin`, so `mach watch` failed at the `watch-project`
229
- * subscription step. Accepting `env` here lets the caller prepend the
230
- * resolved watchman directory to PATH in a way mach inherits.
231
224
  */
232
225
  export declare function watchWithOutput(engineDir: string, options?: {
233
226
  env?: Record<string, string>;
@@ -243,6 +236,9 @@ export declare function test(engineDir: string, testPaths?: string[], args?: str
243
236
  /**
244
237
  * Runs mach test while capturing streamed output for better diagnostics.
245
238
  *
239
+ * @param engineDir - Absolute path to the engine checkout
240
+ * @param testPaths - Test paths to pass to `mach test`; empty runs the default set
241
+ * @param args - Extra `mach test` arguments appended after the paths
246
242
  * @param env - Optional extra environment variables for the mach process
247
243
  * (merged over `process.env` by the exec layer). Used by
248
244
  * `fireforge test --perf-samples` to publish the artifact-path contract.
@@ -253,7 +249,7 @@ export declare function testWithOutput(engineDir: string, testPaths?: string[],
253
249
  * capturing output. Unlike the generic `mach test`, the suite-specific
254
250
  * commands degrade a broken mozlog resource monitor to a warning instead of
255
251
  * crashing at startup, so `fireforge test` dispatches single-suite runs here
256
- * to stay resilient to the host psutil failure (field report E1).
252
+ * to stay resilient to the host psutil failure.
257
253
  *
258
254
  * Signature mirrors {@link testWithOutput} so the two are interchangeable in
259
255
  * the dispatch path.