@hominis/fireforge 0.43.1 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/README.md +3 -3
  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 +13 -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-patch-manifest.d.ts +8 -0
  22. package/dist/src/commands/doctor-patch-manifest.js +155 -0
  23. package/dist/src/commands/doctor-post-rebase-audit.d.ts +2 -0
  24. package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
  25. package/dist/src/commands/doctor-working-tree.d.ts +10 -12
  26. package/dist/src/commands/doctor-working-tree.js +11 -18
  27. package/dist/src/commands/doctor.d.ts +1 -1
  28. package/dist/src/commands/doctor.js +64 -96
  29. package/dist/src/commands/download.js +32 -48
  30. package/dist/src/commands/export-all.js +29 -33
  31. package/dist/src/commands/export-flow.js +6 -6
  32. package/dist/src/commands/export-placement-conflicts.js +10 -9
  33. package/dist/src/commands/export-placement-gate.js +5 -5
  34. package/dist/src/commands/export-shared.d.ts +30 -33
  35. package/dist/src/commands/export-shared.js +45 -43
  36. package/dist/src/commands/export.js +21 -27
  37. package/dist/src/commands/furnace/apply.js +14 -23
  38. package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
  39. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
  40. package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
  41. package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
  42. package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
  43. package/dist/src/commands/furnace/chrome-doc.js +31 -41
  44. package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
  45. package/dist/src/commands/furnace/create-browser-test.js +8 -8
  46. package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
  47. package/dist/src/commands/furnace/create-mochikit.js +8 -9
  48. package/dist/src/commands/furnace/create-readback.d.ts +12 -13
  49. package/dist/src/commands/furnace/create-readback.js +12 -13
  50. package/dist/src/commands/furnace/create-templates.d.ts +22 -28
  51. package/dist/src/commands/furnace/create-templates.js +35 -30
  52. package/dist/src/commands/furnace/create-xpcshell.js +2 -2
  53. package/dist/src/commands/furnace/create.js +33 -23
  54. package/dist/src/commands/furnace/deploy.js +16 -24
  55. package/dist/src/commands/furnace/diff.js +15 -10
  56. package/dist/src/commands/furnace/init.js +33 -36
  57. package/dist/src/commands/furnace/override.js +23 -25
  58. package/dist/src/commands/furnace/preview.js +47 -67
  59. package/dist/src/commands/furnace/refresh.js +12 -16
  60. package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
  61. package/dist/src/commands/furnace/remove-mutations.js +14 -17
  62. package/dist/src/commands/furnace/remove-state.js +1 -1
  63. package/dist/src/commands/furnace/remove.js +33 -38
  64. package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
  65. package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
  66. package/dist/src/commands/furnace/rename.js +59 -69
  67. package/dist/src/commands/furnace/scan.js +19 -33
  68. package/dist/src/commands/furnace/status.js +12 -9
  69. package/dist/src/commands/furnace/sync.js +9 -16
  70. package/dist/src/commands/furnace/validate.js +20 -20
  71. package/dist/src/commands/import.js +48 -37
  72. package/dist/src/commands/lint-per-patch.js +15 -10
  73. package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
  74. package/dist/src/commands/lint-per-run-checkjs.js +13 -13
  75. package/dist/src/commands/lint-report.js +6 -7
  76. package/dist/src/commands/lint.d.ts +15 -16
  77. package/dist/src/commands/lint.js +86 -102
  78. package/dist/src/commands/package.js +6 -11
  79. package/dist/src/commands/patch/compact.js +1 -1
  80. package/dist/src/commands/patch/delete.js +24 -33
  81. package/dist/src/commands/patch/index.d.ts +5 -6
  82. package/dist/src/commands/patch/index.js +10 -12
  83. package/dist/src/commands/patch/lint-ignore.js +14 -13
  84. package/dist/src/commands/patch/move-files-create.d.ts +6 -6
  85. package/dist/src/commands/patch/move-files-create.js +8 -8
  86. package/dist/src/commands/patch/move-files-into.d.ts +6 -6
  87. package/dist/src/commands/patch/move-files-into.js +8 -8
  88. package/dist/src/commands/patch/patch-context.d.ts +2 -3
  89. package/dist/src/commands/patch/patch-context.js +2 -3
  90. package/dist/src/commands/patch/rename.d.ts +10 -11
  91. package/dist/src/commands/patch/rename.js +11 -12
  92. package/dist/src/commands/patch/reorder.js +8 -9
  93. package/dist/src/commands/patch/split-plan.js +1 -1
  94. package/dist/src/commands/patch/split.d.ts +8 -10
  95. package/dist/src/commands/patch/split.js +9 -11
  96. package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
  97. package/dist/src/commands/patch/staged-dependency.js +7 -3
  98. package/dist/src/commands/patch/tier.d.ts +9 -9
  99. package/dist/src/commands/patch/tier.js +18 -15
  100. package/dist/src/commands/re-export-adjacent.js +9 -8
  101. package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
  102. package/dist/src/commands/re-export-bulk-scan.js +22 -21
  103. package/dist/src/commands/re-export-drift.js +10 -10
  104. package/dist/src/commands/re-export-files.d.ts +6 -7
  105. package/dist/src/commands/re-export-files.js +34 -44
  106. package/dist/src/commands/re-export-lint.d.ts +10 -13
  107. package/dist/src/commands/re-export-lint.js +16 -14
  108. package/dist/src/commands/re-export-options.d.ts +6 -6
  109. package/dist/src/commands/re-export-options.js +6 -6
  110. package/dist/src/commands/re-export-register.d.ts +4 -10
  111. package/dist/src/commands/re-export-register.js +5 -11
  112. package/dist/src/commands/re-export-scan.js +1 -1
  113. package/dist/src/commands/re-export-single.d.ts +3 -3
  114. package/dist/src/commands/re-export-single.js +33 -25
  115. package/dist/src/commands/re-export.js +13 -21
  116. package/dist/src/commands/rebase/abort.js +3 -3
  117. package/dist/src/commands/rebase/confirm.js +2 -1
  118. package/dist/src/commands/rebase/continue.d.ts +1 -1
  119. package/dist/src/commands/rebase/continue.js +14 -9
  120. package/dist/src/commands/rebase/index.js +11 -11
  121. package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
  122. package/dist/src/commands/rebase/patch-loop.js +60 -39
  123. package/dist/src/commands/rebase/summary.js +1 -1
  124. package/dist/src/commands/register.js +5 -7
  125. package/dist/src/commands/reset.js +2 -1
  126. package/dist/src/commands/resolve.d.ts +16 -13
  127. package/dist/src/commands/resolve.js +30 -33
  128. package/dist/src/commands/run.js +33 -35
  129. package/dist/src/commands/setup-support.js +15 -19
  130. package/dist/src/commands/setup.js +13 -9
  131. package/dist/src/commands/status-check.d.ts +6 -6
  132. package/dist/src/commands/status-json.d.ts +6 -6
  133. package/dist/src/commands/status-json.js +5 -0
  134. package/dist/src/commands/status-output.d.ts +5 -5
  135. package/dist/src/commands/status-output.js +6 -6
  136. package/dist/src/commands/status-ownership.d.ts +16 -14
  137. package/dist/src/commands/status-ownership.js +16 -14
  138. package/dist/src/commands/status.js +42 -34
  139. package/dist/src/commands/test-build-phase.js +26 -13
  140. package/dist/src/commands/test-diagnose.js +44 -44
  141. package/dist/src/commands/test-register.js +1 -0
  142. package/dist/src/commands/test-run.d.ts +14 -16
  143. package/dist/src/commands/test-run.js +21 -13
  144. package/dist/src/commands/test-stale-gate.d.ts +15 -16
  145. package/dist/src/commands/test-stale-gate.js +15 -16
  146. package/dist/src/commands/test-verdict.d.ts +20 -3
  147. package/dist/src/commands/test-verdict.js +30 -0
  148. package/dist/src/commands/test.js +83 -76
  149. package/dist/src/commands/token-coverage.js +3 -4
  150. package/dist/src/commands/token.js +16 -18
  151. package/dist/src/commands/tree.js +47 -31
  152. package/dist/src/commands/typecheck.d.ts +10 -12
  153. package/dist/src/commands/typecheck.js +24 -27
  154. package/dist/src/commands/verify.js +13 -15
  155. package/dist/src/commands/watch.js +21 -23
  156. package/dist/src/commands/wire.js +52 -62
  157. package/dist/src/core/ast-utils.d.ts +8 -7
  158. package/dist/src/core/ast-utils.js +8 -7
  159. package/dist/src/core/branding.d.ts +9 -14
  160. package/dist/src/core/branding.js +7 -11
  161. package/dist/src/core/browser-wire.d.ts +1 -1
  162. package/dist/src/core/browser-wire.js +14 -19
  163. package/dist/src/core/build-audit-platform.js +25 -33
  164. package/dist/src/core/build-audit-registration.js +17 -25
  165. package/dist/src/core/build-audit-resolve.d.ts +15 -17
  166. package/dist/src/core/build-audit-resolve.js +39 -36
  167. package/dist/src/core/build-audit-transforms.js +18 -19
  168. package/dist/src/core/build-audit.js +28 -42
  169. package/dist/src/core/build-baseline-types.d.ts +29 -35
  170. package/dist/src/core/build-baseline.d.ts +5 -4
  171. package/dist/src/core/build-baseline.js +14 -14
  172. package/dist/src/core/build-overwrite-guard.d.ts +7 -14
  173. package/dist/src/core/build-overwrite-guard.js +17 -27
  174. package/dist/src/core/build-prepare.d.ts +2 -2
  175. package/dist/src/core/build-prepare.js +8 -9
  176. package/dist/src/core/config-mutate.js +20 -19
  177. package/dist/src/core/config-state.js +12 -4
  178. package/dist/src/core/config-validate.js +8 -5
  179. package/dist/src/core/config.d.ts +5 -8
  180. package/dist/src/core/config.js +7 -16
  181. package/dist/src/core/coverage-extend.d.ts +15 -13
  182. package/dist/src/core/coverage-extend.js +15 -13
  183. package/dist/src/core/destructive.d.ts +31 -9
  184. package/dist/src/core/destructive.js +29 -26
  185. package/dist/src/core/diff-hunks.js +10 -12
  186. package/dist/src/core/discard-baseline.d.ts +8 -8
  187. package/dist/src/core/discard-baseline.js +16 -15
  188. package/dist/src/core/display-state.d.ts +12 -14
  189. package/dist/src/core/display-state.js +12 -14
  190. package/dist/src/core/engine-changes.d.ts +2 -4
  191. package/dist/src/core/engine-changes.js +2 -4
  192. package/dist/src/core/engine-precondition.d.ts +28 -13
  193. package/dist/src/core/engine-precondition.js +25 -10
  194. package/dist/src/core/engine-session-lock.d.ts +47 -21
  195. package/dist/src/core/engine-session-lock.js +168 -33
  196. package/dist/src/core/export-moved-code-shape.d.ts +10 -10
  197. package/dist/src/core/export-moved-code-shape.js +10 -10
  198. package/dist/src/core/file-lock.d.ts +62 -5
  199. package/dist/src/core/file-lock.js +196 -59
  200. package/dist/src/core/firefox-cache.js +1 -3
  201. package/dist/src/core/firefox-extract.js +10 -14
  202. package/dist/src/core/firefox.d.ts +7 -5
  203. package/dist/src/core/firefox.js +4 -5
  204. package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
  205. package/dist/src/core/furnace-apply-dry-run.js +3 -2
  206. package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
  207. package/dist/src/core/furnace-apply-ftl.js +16 -13
  208. package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
  209. package/dist/src/core/furnace-apply-helpers.js +30 -21
  210. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
  211. package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
  212. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
  213. package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
  214. package/dist/src/core/furnace-apply.d.ts +5 -8
  215. package/dist/src/core/furnace-apply.js +19 -24
  216. package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
  217. package/dist/src/core/furnace-checksum-utils.js +16 -0
  218. package/dist/src/core/furnace-config.d.ts +21 -30
  219. package/dist/src/core/furnace-config.js +32 -54
  220. package/dist/src/core/furnace-constants.d.ts +8 -0
  221. package/dist/src/core/furnace-constants.js +10 -2
  222. package/dist/src/core/furnace-css-fragments.d.ts +5 -5
  223. package/dist/src/core/furnace-css-fragments.js +5 -5
  224. package/dist/src/core/furnace-jsconfig.d.ts +11 -12
  225. package/dist/src/core/furnace-jsconfig.js +11 -12
  226. package/dist/src/core/furnace-marker.d.ts +7 -8
  227. package/dist/src/core/furnace-marker.js +7 -8
  228. package/dist/src/core/furnace-operation.d.ts +31 -0
  229. package/dist/src/core/furnace-operation.js +54 -17
  230. package/dist/src/core/furnace-precondition.d.ts +61 -0
  231. package/dist/src/core/furnace-precondition.js +67 -0
  232. package/dist/src/core/furnace-registration-remove.d.ts +5 -6
  233. package/dist/src/core/furnace-registration-remove.js +6 -7
  234. package/dist/src/core/furnace-registration-validate.d.ts +13 -0
  235. package/dist/src/core/furnace-registration-validate.js +20 -1
  236. package/dist/src/core/furnace-registration.d.ts +5 -6
  237. package/dist/src/core/furnace-registration.js +10 -12
  238. package/dist/src/core/furnace-rollback.js +7 -7
  239. package/dist/src/core/furnace-scanner.d.ts +1 -0
  240. package/dist/src/core/furnace-scanner.js +1 -2
  241. package/dist/src/core/furnace-stale-export.d.ts +10 -10
  242. package/dist/src/core/furnace-stale-export.js +10 -10
  243. package/dist/src/core/furnace-state-persist.d.ts +6 -8
  244. package/dist/src/core/furnace-state-persist.js +6 -8
  245. package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
  246. package/dist/src/core/furnace-validate-accessibility.js +310 -1
  247. package/dist/src/core/furnace-validate-compatibility.js +141 -1
  248. package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
  249. package/dist/src/core/furnace-validate-helpers.js +0 -491
  250. package/dist/src/core/furnace-validate-registration.d.ts +7 -0
  251. package/dist/src/core/furnace-validate-registration.js +115 -41
  252. package/dist/src/core/furnace-validate.d.ts +16 -0
  253. package/dist/src/core/furnace-validate.js +188 -145
  254. package/dist/src/core/furnace-version-drift.d.ts +10 -11
  255. package/dist/src/core/furnace-version-drift.js +10 -11
  256. package/dist/src/core/git-base.d.ts +21 -9
  257. package/dist/src/core/git-base.js +24 -9
  258. package/dist/src/core/git-diff.d.ts +20 -9
  259. package/dist/src/core/git-diff.js +148 -63
  260. package/dist/src/core/git-file-ops.d.ts +19 -19
  261. package/dist/src/core/git-file-ops.js +19 -19
  262. package/dist/src/core/git-readonly-index.d.ts +53 -19
  263. package/dist/src/core/git-readonly-index.js +75 -31
  264. package/dist/src/core/git-status.d.ts +5 -6
  265. package/dist/src/core/git-status.js +5 -6
  266. package/dist/src/core/git.d.ts +9 -10
  267. package/dist/src/core/git.js +57 -71
  268. package/dist/src/core/license-headers.d.ts +17 -23
  269. package/dist/src/core/license-headers.js +17 -23
  270. package/dist/src/core/mach-build-artifacts.d.ts +9 -9
  271. package/dist/src/core/mach-build-artifacts.js +57 -19
  272. package/dist/src/core/mach-error-hints.js +42 -29
  273. package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
  274. package/dist/src/core/mach-known-noise-filter.js +18 -20
  275. package/dist/src/core/mach-mozconfig.d.ts +14 -0
  276. package/dist/src/core/mach-mozconfig.js +30 -0
  277. package/dist/src/core/mach-resource-shim.d.ts +43 -58
  278. package/dist/src/core/mach-resource-shim.js +50 -67
  279. package/dist/src/core/mach.d.ts +37 -41
  280. package/dist/src/core/mach.js +50 -51
  281. package/dist/src/core/marionette-port.d.ts +36 -1
  282. package/dist/src/core/marionette-port.js +96 -31
  283. package/dist/src/core/marionette-preflight.d.ts +8 -12
  284. package/dist/src/core/marionette-preflight.js +86 -52
  285. package/dist/src/core/mochitest-server-port.d.ts +42 -0
  286. package/dist/src/core/mochitest-server-port.js +119 -0
  287. package/dist/src/core/module-resolution-preflight.d.ts +9 -11
  288. package/dist/src/core/module-resolution-preflight.js +9 -11
  289. package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
  290. package/dist/src/core/moz-manifest-helpers.js +163 -0
  291. package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
  292. package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
  293. package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
  294. package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
  295. package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
  296. package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
  297. package/dist/src/core/ownership-table.d.ts +14 -16
  298. package/dist/src/core/ownership-table.js +14 -16
  299. package/dist/src/core/parser-fallback.d.ts +15 -1
  300. package/dist/src/core/parser-fallback.js +22 -3
  301. package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
  302. package/dist/src/core/patch-apply-fuzz.js +15 -18
  303. package/dist/src/core/patch-apply.d.ts +13 -34
  304. package/dist/src/core/patch-apply.js +16 -76
  305. package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
  306. package/dist/src/core/patch-artifact-normalize.js +13 -15
  307. package/dist/src/core/patch-export-metadata.d.ts +12 -2
  308. package/dist/src/core/patch-export-metadata.js +13 -4
  309. package/dist/src/core/patch-export-update.d.ts +13 -1
  310. package/dist/src/core/patch-export-update.js +25 -8
  311. package/dist/src/core/patch-export.d.ts +15 -8
  312. package/dist/src/core/patch-export.js +10 -11
  313. package/dist/src/core/patch-files.d.ts +5 -6
  314. package/dist/src/core/patch-files.js +5 -6
  315. package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
  316. package/dist/src/core/patch-identifier-suggest.js +14 -19
  317. package/dist/src/core/patch-lint-binary.d.ts +51 -0
  318. package/dist/src/core/patch-lint-binary.js +39 -0
  319. package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
  320. package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
  321. package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
  322. package/dist/src/core/patch-lint-checkjs.js +41 -20
  323. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
  324. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
  325. package/dist/src/core/patch-lint-creators.d.ts +8 -8
  326. package/dist/src/core/patch-lint-creators.js +8 -8
  327. package/dist/src/core/patch-lint-cross.d.ts +17 -20
  328. package/dist/src/core/patch-lint-cross.js +16 -16
  329. package/dist/src/core/patch-lint-css.d.ts +3 -5
  330. package/dist/src/core/patch-lint-css.js +17 -22
  331. package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
  332. package/dist/src/core/patch-lint-diff-tag.js +14 -16
  333. package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
  334. package/dist/src/core/patch-lint-jsdoc.js +26 -19
  335. package/dist/src/core/patch-lint-mozbuild.js +20 -20
  336. package/dist/src/core/patch-lint-observer.d.ts +7 -7
  337. package/dist/src/core/patch-lint-observer.js +7 -7
  338. package/dist/src/core/patch-lint.d.ts +38 -29
  339. package/dist/src/core/patch-lint.js +89 -121
  340. package/dist/src/core/patch-lock.d.ts +1 -1
  341. package/dist/src/core/patch-lock.js +45 -9
  342. package/dist/src/core/patch-manifest-consistency.d.ts +57 -6
  343. package/dist/src/core/patch-manifest-consistency.js +111 -40
  344. package/dist/src/core/patch-manifest-files-affected.d.ts +43 -0
  345. package/dist/src/core/patch-manifest-files-affected.js +77 -0
  346. package/dist/src/core/patch-manifest-io.js +14 -18
  347. package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
  348. package/dist/src/core/patch-manifest-resolve.js +9 -12
  349. package/dist/src/core/patch-manifest-validate.d.ts +6 -1
  350. package/dist/src/core/patch-manifest-validate.js +25 -13
  351. package/dist/src/core/patch-manifest.d.ts +2 -1
  352. package/dist/src/core/patch-manifest.js +2 -1
  353. package/dist/src/core/patch-parse.d.ts +24 -13
  354. package/dist/src/core/patch-parse.js +19 -14
  355. package/dist/src/core/patch-policy.d.ts +2 -2
  356. package/dist/src/core/patch-policy.js +2 -2
  357. package/dist/src/core/patch-registration-refs.d.ts +7 -8
  358. package/dist/src/core/patch-registration-refs.js +9 -9
  359. package/dist/src/core/process-cpu-time.d.ts +12 -0
  360. package/dist/src/core/process-cpu-time.js +52 -0
  361. package/dist/src/core/rebase-session.d.ts +38 -26
  362. package/dist/src/core/rebase-session.js +55 -28
  363. package/dist/src/core/register-browser-content.js +9 -74
  364. package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
  365. package/dist/src/core/register-module.js +10 -43
  366. package/dist/src/core/register-scaffold.d.ts +8 -8
  367. package/dist/src/core/register-scaffold.js +10 -10
  368. package/dist/src/core/register-shared-css.d.ts +9 -12
  369. package/dist/src/core/register-shared-css.js +17 -87
  370. package/dist/src/core/register-xpcshell-test.d.ts +6 -7
  371. package/dist/src/core/register-xpcshell-test.js +7 -8
  372. package/dist/src/core/shared-ftl.js +12 -4
  373. package/dist/src/core/state-file.d.ts +45 -0
  374. package/dist/src/core/state-file.js +34 -0
  375. package/dist/src/core/status-binary.d.ts +18 -11
  376. package/dist/src/core/status-binary.js +19 -12
  377. package/dist/src/core/status-classify.d.ts +30 -36
  378. package/dist/src/core/status-classify.js +21 -26
  379. package/dist/src/core/test-changed-prefs.d.ts +30 -0
  380. package/dist/src/core/test-changed-prefs.js +78 -0
  381. package/dist/src/core/test-harness-crash.d.ts +25 -63
  382. package/dist/src/core/test-harness-crash.js +121 -142
  383. package/dist/src/core/test-harness-signature.d.ts +18 -0
  384. package/dist/src/core/test-harness-signature.js +13 -0
  385. package/dist/src/core/test-path-scope.d.ts +12 -15
  386. package/dist/src/core/test-path-scope.js +15 -18
  387. package/dist/src/core/test-stale-check.d.ts +17 -17
  388. package/dist/src/core/test-stale-check.js +50 -43
  389. package/dist/src/core/test-stall-triage.d.ts +51 -0
  390. package/dist/src/core/test-stall-triage.js +126 -0
  391. package/dist/src/core/token-category.js +66 -14
  392. package/dist/src/core/token-coverage.js +9 -9
  393. package/dist/src/core/token-dark-mode.d.ts +9 -11
  394. package/dist/src/core/token-dark-mode.js +29 -13
  395. package/dist/src/core/token-docs.d.ts +2 -0
  396. package/dist/src/core/token-docs.js +3 -1
  397. package/dist/src/core/token-manager.d.ts +10 -2
  398. package/dist/src/core/token-manager.js +17 -8
  399. package/dist/src/core/token-scaffold.d.ts +7 -7
  400. package/dist/src/core/token-scaffold.js +7 -7
  401. package/dist/src/core/token-variant.d.ts +4 -4
  402. package/dist/src/core/token-variant.js +28 -6
  403. package/dist/src/core/toolchain-preflight.d.ts +20 -21
  404. package/dist/src/core/toolchain-preflight.js +28 -31
  405. package/dist/src/core/tree-guard.js +12 -2
  406. package/dist/src/core/tree-store.d.ts +41 -27
  407. package/dist/src/core/tree-store.js +68 -43
  408. package/dist/src/core/typecheck-shim.d.ts +23 -24
  409. package/dist/src/core/typecheck-shim.js +23 -24
  410. package/dist/src/core/typecheck.js +15 -9
  411. package/dist/src/core/wire-destroy.d.ts +3 -3
  412. package/dist/src/core/wire-destroy.js +7 -15
  413. package/dist/src/core/wire-dom-fragment.d.ts +9 -17
  414. package/dist/src/core/wire-dom-fragment.js +11 -44
  415. package/dist/src/core/wire-init.d.ts +2 -3
  416. package/dist/src/core/wire-init.js +8 -19
  417. package/dist/src/core/wire-subscript.d.ts +2 -3
  418. package/dist/src/core/wire-subscript.js +4 -13
  419. package/dist/src/core/wire-utils.d.ts +7 -8
  420. package/dist/src/core/wire-utils.js +7 -8
  421. package/dist/src/core/xpcshell-appdir.d.ts +38 -45
  422. package/dist/src/core/xpcshell-appdir.js +38 -45
  423. package/dist/src/errors/base.d.ts +37 -16
  424. package/dist/src/errors/base.js +37 -16
  425. package/dist/src/errors/build.d.ts +13 -1
  426. package/dist/src/errors/build.js +14 -2
  427. package/dist/src/errors/codes.d.ts +16 -0
  428. package/dist/src/errors/codes.js +16 -0
  429. package/dist/src/errors/furnace.d.ts +4 -4
  430. package/dist/src/errors/git.d.ts +6 -10
  431. package/dist/src/errors/git.js +6 -10
  432. package/dist/src/errors/patch.d.ts +7 -7
  433. package/dist/src/errors/patch.js +7 -7
  434. package/dist/src/errors/rebase.d.ts +4 -4
  435. package/dist/src/errors/rebase.js +4 -4
  436. package/dist/src/index.d.ts +7 -1
  437. package/dist/src/index.js +5 -0
  438. package/dist/src/types/commands/index.d.ts +2 -2
  439. package/dist/src/types/commands/options.d.ts +111 -70
  440. package/dist/src/types/commands/patches.d.ts +24 -34
  441. package/dist/src/types/commands/project.d.ts +5 -34
  442. package/dist/src/types/config.d.ts +25 -23
  443. package/dist/src/types/config.js +6 -1
  444. package/dist/src/types/furnace.d.ts +37 -13
  445. package/dist/src/types/furnace.js +19 -1
  446. package/dist/src/types/index.d.ts +3 -3
  447. package/dist/src/utils/build-info.d.ts +7 -8
  448. package/dist/src/utils/build-info.js +12 -11
  449. package/dist/src/utils/errors.d.ts +6 -8
  450. package/dist/src/utils/errors.js +6 -8
  451. package/dist/src/utils/logger.d.ts +27 -10
  452. package/dist/src/utils/logger.js +28 -16
  453. package/dist/src/utils/machine-output.d.ts +12 -0
  454. package/dist/src/utils/machine-output.js +33 -0
  455. package/dist/src/utils/options.d.ts +31 -14
  456. package/dist/src/utils/options.js +86 -30
  457. package/dist/src/utils/paths.d.ts +10 -8
  458. package/dist/src/utils/paths.js +10 -8
  459. package/dist/src/utils/platform.d.ts +0 -25
  460. package/dist/src/utils/platform.js +1 -42
  461. package/dist/src/utils/process-group.d.ts +9 -4
  462. package/dist/src/utils/process-group.js +19 -5
  463. package/dist/src/utils/process.d.ts +42 -7
  464. package/dist/src/utils/process.js +44 -17
  465. package/dist/src/utils/ps-duration.d.ts +31 -0
  466. package/dist/src/utils/ps-duration.js +70 -0
  467. package/dist/src/utils/regex.d.ts +9 -10
  468. package/dist/src/utils/regex.js +9 -10
  469. package/dist/src/utils/validation.d.ts +58 -28
  470. package/dist/src/utils/validation.js +60 -33
  471. package/package.json +3 -3
  472. package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
  473. package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
  474. package/dist/src/core/manifest-helpers.d.ts +0 -38
  475. package/dist/src/core/manifest-helpers.js +0 -120
  476. /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
  477. /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
  478. /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
@@ -4,24 +4,23 @@
4
4
  *
5
5
  * Without this preflight, an operator who edits engine chrome / packaged
6
6
  * resources (`jar.mn` entries, `.xhtml`/`.mjs`/`.css` under chrome trees,
7
- * pref files) and then runs `fireforge test <path>` only discovers the
8
- * build is stale AFTER xpcshell / mach test starts and errors out with
9
- * `NS_ERROR_FILE_NOT_FOUND` against a `chrome://browser/content/…` URI
10
- * — which reads as a test bug, not a rebuild prompt. The motivating case
11
- * was scaffolding a new top-level chrome document + BrowserGlue-style
12
- * xpcshell test: the test file existed, the manifests were registered,
13
- * but `dist/` still held the pre-edit bundle and chrome URIs resolved
14
- * to nothing.
7
+ * pref files) and then runs `fireforge test <path>` only discovers the build
8
+ * is stale AFTER xpcshell / mach test starts and errors out with
9
+ * `NS_ERROR_FILE_NOT_FOUND` against a `chrome://browser/content/…` URI —
10
+ * which reads as a test bug, not a rebuild prompt. Scaffolding a new
11
+ * top-level chrome document plus an xpcshell test is the canonical case: the
12
+ * test file exists, the manifests are registered, but `dist/` still holds
13
+ * the pre-edit bundle and chrome URIs resolve to nothing.
15
14
  *
16
15
  * This preflight diffs engine HEAD (or workdir) against the last-build
17
16
  * baseline (`.fireforge/last-build.json`), filters to paths that imply
18
17
  * packaging, and returns a compact summary. `fireforge test` prints a
19
- * warning up-front so the operator sees "you edited X, Y, Z since the
20
- * last build — rerun with `--build` to refresh" BEFORE mach test
21
- * launches. Detection stays advisory (warn-only) because a fork that
22
- * rebuilds out-of-band (a separate `./mach build` invocation, an IDE
23
- * plugin, etc.) can legitimately have a fresh `dist/` with no
24
- * FireForge-recorded baseline update.
18
+ * warning up-front so the operator sees "you edited X, Y, Z since the last
19
+ * build — rerun with `--build` to refresh" BEFORE mach test launches.
20
+ * Detection stays advisory (warn-only) because a fork that rebuilds
21
+ * out-of-band (a separate `./mach build` invocation, an IDE plugin) can
22
+ * legitimately have a fresh `dist/` with no FireForge-recorded baseline
23
+ * update.
25
24
  */
26
25
  import { toError } from '../utils/errors.js';
27
26
  import { verbose, warn } from '../utils/logger.js';
@@ -51,17 +50,16 @@ export async function checkStaleBuildForTest(projectRoot, engineDir) {
51
50
  let packageable = changed.filter((path) => isPackageablePath(path)).sort();
52
51
  // Content-hash comparison: when the baseline carries a fingerprint set,
53
52
  // fold each candidate path through a live re-hash and drop paths whose
54
- // current content matches the baseline. Pre-0.16.0 baselines have no
55
- // `packageableFingerprints` field; those fall through and the
56
- // path-only comparison behaves as before (every workdir-dirty
57
- // packageable path is reported as stale). The concrete motivating
58
- // case: a project with imported patches + Furnace-applied components
59
- // always has a persistent workdir diff against HEAD. Before the
60
- // fingerprint layer, `git diff --name-only HEAD` returned that diff
61
- // on every build, so the stale check fired immediately after a
62
- // successful build even though nothing had actually changed. The
63
- // fingerprints capture "these files had this content when the build
64
- // ran"; a path stays stale only when its live hash diverges.
53
+ // current content matches the baseline. Baselines without
54
+ // `packageableFingerprints` fall through to the path-only comparison,
55
+ // where every workdir-dirty packageable path reports as stale.
56
+ //
57
+ // This matters because a project with imported patches plus
58
+ // Furnace-applied components always has a persistent workdir diff against
59
+ // HEAD, so `git diff --name-only HEAD` returns that diff on every build
60
+ // and the stale check fires immediately after a successful one. The
61
+ // fingerprints capture "these files had this content when the build ran";
62
+ // a path stays stale only when its live hash diverges.
65
63
  const fingerprints = baseline.packageableFingerprints;
66
64
  if (fingerprints) {
67
65
  const staleAfterHashCheck = [];
@@ -105,16 +103,16 @@ export const FULL_SUITE_REQUEST = '(entire suite)';
105
103
  * packaging does NOT cover — the runs that would dispatch against missing
106
104
  * `_tests/` support fixtures and hang rather than fail.
107
105
  *
108
- * Coverage semantics: `undefined` (pre-0.37.0 baseline) and `'full'` cover
109
- * everything. A scoped list covers a request path when the request equals a
110
- * covered entry, sits beneath a covered directory entry, or shares a
111
- * manifest granule with a covered entry ({@link toManifestGranule} — a
112
- * scoped `test --build` packages the whole manifest directory, so a
113
- * same-manifest sibling of a covered file is packaged too). Both sides are
114
- * normalized to forward slashes so Windows-style CLI input cannot defeat
115
- * the prefix rule (baseline paths are POSIX by convention). A request with
116
- * no paths is a full-suite run and is never covered by a scoped list — the
117
- * {@link FULL_SUITE_REQUEST} sentinel is returned so the refusal can name it.
106
+ * Coverage semantics: an absent claim and `'full'` cover everything. A
107
+ * scoped list covers a request path when the request equals a covered entry,
108
+ * sits beneath a covered directory entry, or shares a manifest granule with
109
+ * a covered entry ({@link toManifestGranule} — a scoped `test --build`
110
+ * packages the whole manifest directory, so a same-manifest sibling of a
111
+ * covered file is packaged too). Both sides are normalized to forward
112
+ * slashes so Windows-style CLI input cannot defeat the prefix rule (baseline
113
+ * paths are POSIX by convention). A request with no paths is a full-suite
114
+ * run and is never covered by a scoped list — the {@link FULL_SUITE_REQUEST}
115
+ * sentinel is returned so the refusal can name it.
118
116
  */
119
117
  export function findUncoveredRequestPaths(coverage, requestedPaths) {
120
118
  if (coverage === undefined || coverage === 'full') {
@@ -167,8 +165,17 @@ export function formatTestCoverageRefusal(uncovered, coverage) {
167
165
  return head.join(', ') + (truncated > 0 ? `, … (+${truncated} more)` : '');
168
166
  };
169
167
  const rebuildTargets = uncovered.filter((p) => p !== FULL_SUITE_REQUEST);
168
+ // Name --extend-coverage here or it is undiscoverable: a scoped build
169
+ // REPLACES the claim, so under several concurrent sessions a peer's build
170
+ // for an unrelated path erases coverage you legitimately still hold, and
171
+ // the union that fixes it is a flag the operator has no reason to know
172
+ // exists at the moment they need it. The anchor conditions are stated
173
+ // because they are what decides whether it will work.
174
+ const extendHint = rebuildTargets.length > 0
175
+ ? ` Add --extend-coverage to that rebuild to UNION the new paths onto the recorded claim instead of replacing it — it holds while engine HEAD and engine/mozconfig are unchanged since the recorded build.`
176
+ : '';
170
177
  const rebuildHint = rebuildTargets.length > 0
171
- ? `Rerun "fireforge test --build ${rebuildTargets.slice(0, STALE_PATHS_LIMIT).join(' ')}" to package them, or run "fireforge build" for full coverage.`
178
+ ? `Rerun "fireforge test --build ${rebuildTargets.slice(0, STALE_PATHS_LIMIT).join(' ')}" to package them, or run "fireforge build" for full coverage.${extendHint}`
172
179
  : 'Run "fireforge build" (or a path-less "fireforge test --build") for full coverage first.';
173
180
  return (`The packaged test runtime was produced by a scoped "fireforge test --build" covering only: ${cap(coverage)}.\n` +
174
181
  `The requested run needs ${cap(uncovered)}, which that packaging does not cover — support ` +
@@ -180,17 +187,17 @@ export function formatTestCoverageRefusal(uncovered, coverage) {
180
187
  * Probes whether any `components.conf` changed since the last FULL
181
188
  * `fireforge build` — i.e. since the compiled StaticComponents table was
182
189
  * last regenerated. `components.conf` entries bake into compiled code; a
183
- * scoped `test --build` packages the file but the child process resolves
184
- * the OLD table and fails with `NS_ERROR_MALFORMED_URI` that reads as a
185
- * test bug.
190
+ * scoped `test --build` packages the file but the child process resolves the
191
+ * OLD table and fails with `NS_ERROR_MALFORMED_URI` that reads as a test
192
+ * bug.
186
193
  *
187
194
  * The diff anchors to the baseline's `staticComponentsBaseline` (the last
188
- * full build's engine HEAD SHA), NOT the baseline's own `engineHeadSha`
195
+ * full build's engine HEAD SHA), NOT the baseline's own `engineHeadSha`,
189
196
  * which a scoped `test --build` advances. Dirty candidates are hash-checked
190
197
  * against the anchor's fingerprints so only genuine content divergence
191
- * counts. No baseline / no anchor (pre-0.38.0 marker) → fresh. Never
192
- * throws — the probes it composes degrade to verbose lines and empty
193
- * results on git failure, matching {@link checkStaleBuildForTest}.
198
+ * counts. No baseline or no anchor means fresh. Never throws — the probes it
199
+ * composes degrade to verbose lines and empty results on git failure,
200
+ * matching {@link checkStaleBuildForTest}.
194
201
  */
195
202
  export async function checkStaticComponentsStale(engineDir, baseline) {
196
203
  const anchor = baseline?.staticComponentsBaseline;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Operator-facing triage for the `timed out … with no output` / `Ran 0
3
+ * checks` stall — the shape where the harness dies before ANY test output.
4
+ *
5
+ * Split out of `test-harness-crash.ts`, which CLASSIFIES runs; this module
6
+ * only renders advice about one already-classified shape. Keeping the two
7
+ * apart matters because the census below carries claims of unequal
8
+ * evidential strength, and the rules for editing it are not the rules for
9
+ * editing a classifier: a pattern in the classifier is either matched by
10
+ * the output or not, while a cause in the census is an assertion about the
11
+ * world that an operator will act on.
12
+ *
13
+ * Everything here is pure — the platform and the probed display state are
14
+ * injected, so unit tests need no mocking.
15
+ */
16
+ import type { DisplaySleepState } from './display-state.js';
17
+ import type { HarnessCrashSignature } from './test-harness-signature.js';
18
+ /**
19
+ * Optional hint appended to the harness-crash message when a HEADED run on
20
+ * macOS died at the no-output timeout.
21
+ *
22
+ * When `displayState` names a MEASURED display state, the hint states it as
23
+ * fact rather than as one of three possibilities — that is the point of
24
+ * probing: an operator staring at a bare test failure should not have to
25
+ * rediscover that their machine dimmed. `caffeinate` is described accurately
26
+ * (it prevents sleep; it cannot wake an already-sleeping display), so
27
+ * operators are not sent to a command that could not have helped them.
28
+ *
29
+ * Pure — the platform and the probed state are injected so unit tests need
30
+ * no mocking.
31
+ *
32
+ * @param signature - The recognized crash shape
33
+ * @param context - Run mode, platform, and the probed display state
34
+ * @returns The hint text, or undefined when the shape does not apply
35
+ */
36
+ export declare function headedNoOutputTimeoutHint(signature: HarnessCrashSignature, context: {
37
+ headless: boolean;
38
+ platform: string;
39
+ displayState?: DisplaySleepState;
40
+ }): string | undefined;
41
+ /**
42
+ * Verdict-line note for a headed no-output stall whose display was
43
+ * measured asleep. Returned as the parenthetical the verdict line carries,
44
+ * so the one greppable line an automated consumer reads already names the
45
+ * environmental cause instead of a bare `FAIL reason=crash`.
46
+ */
47
+ export declare function headedDisplayAsleepVerdictNote(signature: HarnessCrashSignature, context: {
48
+ headless: boolean;
49
+ platform: string;
50
+ displayState: DisplaySleepState;
51
+ }): string | undefined;
@@ -0,0 +1,126 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Operator-facing triage for the `timed out … with no output` / `Ran 0
4
+ * checks` stall — the shape where the harness dies before ANY test output.
5
+ *
6
+ * Split out of `test-harness-crash.ts`, which CLASSIFIES runs; this module
7
+ * only renders advice about one already-classified shape. Keeping the two
8
+ * apart matters because the census below carries claims of unequal
9
+ * evidential strength, and the rules for editing it are not the rules for
10
+ * editing a classifier: a pattern in the classifier is either matched by
11
+ * the output or not, while a cause in the census is an assertion about the
12
+ * world that an operator will act on.
13
+ *
14
+ * Everything here is pure — the platform and the probed display state are
15
+ * injected, so unit tests need no mocking.
16
+ */
17
+ /**
18
+ * First step for the `timed out … with no output` / `Ran 0 checks`
19
+ * signature, printed ABOVE the cause list. Running a known-good control
20
+ * separates "this test" from "this build" and is the correct opening move
21
+ * for EVERY cause below — it is not specific to any one of them, so
22
+ * attaching it to a single entry made reaching that entry informationally
23
+ * empty.
24
+ */
25
+ const NO_OUTPUT_STALL_CONTROL_STEP = 'First, run a known-good control test. If the control stalls too, the cause is not the test ' +
26
+ 'under investigation, and the list below applies to the build or the host.';
27
+ /**
28
+ * Recorded causes of the `timed out … with no output` / `Ran 0 checks`
29
+ * signature, each paired with the probe that DISCRIMINATES it from the
30
+ * others. Printed verbatim under the hint.
31
+ *
32
+ * Evidence per entry is deliberately unequal, and the text says so:
33
+ *
34
+ * - (1) is MEASURED — `probeDisplaySleepState` reads the display's power
35
+ * state, so the hint states it as fact rather than as a possibility.
36
+ * - (2) is discriminated by `--headless`.
37
+ * - (4) is MECHANICAL and was root-caused downstream: the harness serves
38
+ * its manifest from `server.js` on 8888, and a survivor of an
39
+ * interrupted run keeps the port. Its probe is exact, and the preflight
40
+ * in `mochitest-server-port.ts` now refuses it before a run starts.
41
+ * - (3) is a single downstream report (one consumer, 3/3 reproduce and 3/3
42
+ * restore on one variable) whose MECHANISM was never root-caused. It is
43
+ * stated as the correlation it is. An earlier revision asserted "stalls
44
+ * first paint" as fact; that was a causal story written on top of the
45
+ * correlation, and a doubtful one — a failed image load in Gecko fires
46
+ * `error` and does not gate the document load event, and the harness's
47
+ * browser-ready handshake does not wait on image decode. Do not restore
48
+ * a mechanism claim here without a root cause to cite.
49
+ *
50
+ * Detection (a named signature, as with the module-resolution SIGSEGV) is
51
+ * not specified for (3) because no log line is known to key on. The bar for
52
+ * adding one is the bar `mach-error-hints.ts` states: an established
53
+ * diagnosis, never a plausible guess.
54
+ */
55
+ const NO_OUTPUT_STALL_TRIAGE = [
56
+ ' 1. A sleeping or locked display on an unattended HEADED run (macOS). The browser never ' +
57
+ 'paints and never reaches its first test.',
58
+ ' 2. A headless SWGL compositor failure — re-run with --headless to separate this from (1): ' +
59
+ 'if --headless passes, the stall was display/compositor-side, not product-side.',
60
+ ' 3. Product-side startup: the browser never reaches the state the harness waits for. One ' +
61
+ 'downstream report correlates this stall with a broken `chrome://` image URI on the ' +
62
+ 'startup page (reproduced and un-reproduced on that one variable); the mechanism was ' +
63
+ 'never root-caused, so treat it as a lead, not an explanation. Discriminate with ' +
64
+ '"fireforge run --smoke-exit 60 --headless --capture-console <file>", which launches the ' +
65
+ 'same browser OUTSIDE the test harness: a clean window with no unallowed console errors ' +
66
+ 'means product-side startup is healthy and the stall is harness- or test-side; a hang, a ' +
67
+ 'non-zero exit, or console errors in <file> put it in the startup document.',
68
+ ' 4. A stale mochitest httpd holding the harness server port (127.0.0.1:8888). A fresh ' +
69
+ "browser connects to THAT server, which cannot serve this run's manifest, so the run " +
70
+ 'stalls before TEST_START. Probe with "lsof -nP -iTCP:8888": any listener that is not ' +
71
+ "this run's own is the cause. FireForge now refuses this at preflight, so it should " +
72
+ 'reach a stall only on a host where the port could not be probed.',
73
+ ];
74
+ /**
75
+ * Optional hint appended to the harness-crash message when a HEADED run on
76
+ * macOS died at the no-output timeout.
77
+ *
78
+ * When `displayState` names a MEASURED display state, the hint states it as
79
+ * fact rather than as one of three possibilities — that is the point of
80
+ * probing: an operator staring at a bare test failure should not have to
81
+ * rediscover that their machine dimmed. `caffeinate` is described accurately
82
+ * (it prevents sleep; it cannot wake an already-sleeping display), so
83
+ * operators are not sent to a command that could not have helped them.
84
+ *
85
+ * Pure — the platform and the probed state are injected so unit tests need
86
+ * no mocking.
87
+ *
88
+ * @param signature - The recognized crash shape
89
+ * @param context - Run mode, platform, and the probed display state
90
+ * @returns The hint text, or undefined when the shape does not apply
91
+ */
92
+ export function headedNoOutputTimeoutHint(signature, context) {
93
+ if (context.platform !== 'darwin' || context.headless)
94
+ return undefined;
95
+ if (!signature.reason.includes('no-output timeout'))
96
+ return undefined;
97
+ const lead = context.displayState === 'asleep'
98
+ ? 'Hint: this was a HEADED run on macOS and the display was MEASURED ASLEEP ' +
99
+ '(IODisplayWrangler below its awake power state). A headed browser on a sleeping ' +
100
+ 'display never paints and never starts a test, so this stall is environmental — not a ' +
101
+ 'product or test failure.'
102
+ : context.displayState === 'awake'
103
+ ? 'Hint: this was a HEADED run on macOS that died at the no-output timeout. The display ' +
104
+ 'was measured AWAKE, so the sleeping-display cause below is ruled out for this run.'
105
+ : 'Hint: this was a HEADED run on macOS that died at the no-output timeout. The display ' +
106
+ 'state could not be measured, so all three causes below remain open.';
107
+ const remedy = 'Note that `caffeinate -disu` PREVENTS sleep; it cannot WAKE a display that is already ' +
108
+ 'asleep. Wake the display (or run on an attended machine), or pass --headless.';
109
+ return (`${lead}\n\n${NO_OUTPUT_STALL_CONTROL_STEP}\n\n` +
110
+ `Known causes of this exact signature:\n${NO_OUTPUT_STALL_TRIAGE.join('\n')}\n\n${remedy}`);
111
+ }
112
+ /**
113
+ * Verdict-line note for a headed no-output stall whose display was
114
+ * measured asleep. Returned as the parenthetical the verdict line carries,
115
+ * so the one greppable line an automated consumer reads already names the
116
+ * environmental cause instead of a bare `FAIL reason=crash`.
117
+ */
118
+ export function headedDisplayAsleepVerdictNote(signature, context) {
119
+ if (context.platform !== 'darwin' || context.headless)
120
+ return undefined;
121
+ if (context.displayState !== 'asleep')
122
+ return undefined;
123
+ if (!signature.reason.includes('no-output timeout'))
124
+ return undefined;
125
+ return 'headed run stalled with the display asleep';
126
+ }
@@ -1,15 +1,13 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
- * Category banner and token-declaration helpers for the tokens CSS file
4
- *. Split out of `token-manager.ts` to stay inside the per-file
5
- * line budget.
3
+ * Category banner and token-declaration helpers for the tokens CSS file.
4
+ * Split out of `token-manager.ts` to stay inside the per-file line budget.
6
5
  *
7
6
  * Banner matching is EXACT: the text between the `=` runs (single-line
8
7
  * shape) or on the block line (multi-line shape) must equal the category
9
- * after trimming. The pre-0.40.0 substring match let a TOC comment or a
10
- * longer banner (`Colors — Canvas` for `Colors`) satisfy the lookup, so
11
- * `token add` wrote into the wrong section — or no-oped for 19
12
- * consecutive calls — with exit 0.
8
+ * after trimming. A substring match lets a TOC comment or a longer banner
9
+ * (`Colors — Canvas` for `Colors`) satisfy the lookup, so `token add` writes
10
+ * into the wrong section — or no-ops — with exit 0.
13
11
  */
14
12
  import { join } from 'node:path';
15
13
  import { GeneralError } from '../errors/base.js';
@@ -21,6 +19,63 @@ function singleLineBannerPattern(category) {
21
19
  function multiLineBlockNameMatches(blockLine, category) {
22
20
  return blockLine.replace(/^\s*\*\s*/, '').trim() === category;
23
21
  }
22
+ /**
23
+ * Body of the first `/* … *\/` comment on `line`, or `null` when the line
24
+ * carries no closed block comment.
25
+ *
26
+ * Deliberately index arithmetic rather than a regex: every regex spelling of
27
+ * "banner comment" this module used to carry was super-linear on a line that
28
+ * repeats the opening shape (CodeQL `js/polynomial-redos`), and `tokens.css`
29
+ * is a file FireForge reads out of a consumer's engine tree, so a
30
+ * pathological line needs no attacker to arrive.
31
+ */
32
+ function blockCommentBody(line) {
33
+ const open = line.indexOf('/*');
34
+ if (open === -1)
35
+ return null;
36
+ const close = line.indexOf('*/', open + 2);
37
+ if (close === -1)
38
+ return null;
39
+ return line.slice(open + 2, close);
40
+ }
41
+ /**
42
+ * True when `line` carries a single-line banner comment — a closed block
43
+ * comment whose body both opens and closes with `=`. Recognises the decorative
44
+ * all-`=` rule as well as a named `= Foo =` banner, which is what the section
45
+ * scan wants: either shape ends the preceding section.
46
+ */
47
+ function isSingleLineBannerLine(line) {
48
+ const body = blockCommentBody(line)?.trim();
49
+ return body !== undefined && body.length >= 2 && body.startsWith('=') && body.endsWith('=');
50
+ }
51
+ /**
52
+ * Name declared by a single-line banner (`/* = Foo = *\/` → `Foo`), or `null`
53
+ * when the line is not a named banner. The `=` runs on both sides are
54
+ * required; a decorative all-`=` rule carries no name and yields `null`.
55
+ */
56
+ function singleLineBannerName(line) {
57
+ const body = blockCommentBody(line);
58
+ if (body === null)
59
+ return null;
60
+ let start = 0;
61
+ let end = body.length;
62
+ while (start < end && /\s/.test(body[start] ?? ''))
63
+ start++;
64
+ const leadingEqStart = start;
65
+ while (start < end && body[start] === '=')
66
+ start++;
67
+ if (start === leadingEqStart)
68
+ return null;
69
+ while (end > start && /\s/.test(body[end - 1] ?? ''))
70
+ end--;
71
+ const trailingEqEnd = end;
72
+ while (end > start && body[end - 1] === '=')
73
+ end--;
74
+ if (end === trailingEqEnd)
75
+ return null;
76
+ const name = body.slice(start, end).trim();
77
+ return name.length > 0 ? name : null;
78
+ }
24
79
  /**
25
80
  * True when `lines` contain a category header (single-line or multi-line
26
81
  * banner shape) whose name EQUALS `category`. Shared by the pre-add
@@ -62,14 +117,11 @@ export function categoryHeaderExists(lines, category) {
62
117
  */
63
118
  function discoverCategoryHeaders(lines) {
64
119
  const categories = new Set();
65
- const singleLinePattern = /\/\*\s*=+\s*(.+?)\s*=+\s*\*\//;
66
120
  for (let i = 0; i < lines.length; i++) {
67
121
  const line = lines[i] ?? '';
68
- const singleMatch = singleLinePattern.exec(line);
69
- if (singleMatch?.[1]) {
70
- const extracted = singleMatch[1].trim();
71
- if (extracted.length > 0)
72
- categories.add(extracted);
122
+ const extracted = singleLineBannerName(line);
123
+ if (extracted !== null) {
124
+ categories.add(extracted);
73
125
  continue;
74
126
  }
75
127
  if (/^\s*\/\*\s*=+/.test(line) && !/\*\//.test(line)) {
@@ -183,7 +235,7 @@ export function findCategorySection(lines, category, tokensCssPath) {
183
235
  let sectionEnd = lines.length;
184
236
  for (let i = scanStart; i < lines.length; i++) {
185
237
  const line = lines[i] ?? '';
186
- if (/\/\*\s*=.*=\s*\*\//.test(line) ||
238
+ if (isSingleLineBannerLine(line) ||
187
239
  (/^\s*\/\*\s*=+/.test(line) && !/\*\//.test(line)) ||
188
240
  /^\s*\}/.test(line)) {
189
241
  sectionEnd = i;
@@ -7,17 +7,17 @@ import { countRawCssColors } from '../utils/regex.js';
7
7
  import { loadFurnaceConfig } from './furnace-config.js';
8
8
  /**
9
9
  * Default platform prefixes treated as allowlisted upstream vars. Any
10
- * `var(--moz-*)` usage in a fork's CSS is a Firefox platform variable
11
- * that the fork does not own and should not be counted as an unknown.
12
- * 2026-04-21 eval (Finding #5): a `furnace override moz-button -t
13
- * css-only` + one fork token produced 1% coverage because the 84
14
- * upstream `--moz-*` vars in the copied baseline counted as unknown.
10
+ * `var(--moz-*)` usage in a fork's CSS is a Firefox platform variable the
11
+ * fork does not own and must not count as an unknown — otherwise a
12
+ * `furnace override moz-button -t css-only` plus one fork token reports 1%
13
+ * coverage, because the ~84 upstream `--moz-*` vars in the copied baseline
14
+ * all count against it.
15
15
  *
16
16
  * Forks that want to opt out can override this via
17
- * `furnace.json.platformPrefixes = []`; forks that want more can
18
- * extend it (e.g. `['--moz-', '--in-content-']`). The config is
19
- * additive — nothing is removed from the defaults unless the operator
20
- * explicitly writes a shorter list.
17
+ * `furnace.json.platformPrefixes = []`; forks that want more can extend it
18
+ * (e.g. `['--moz-', '--in-content-']`). The config is additive — nothing is
19
+ * removed from the defaults unless the operator explicitly writes a shorter
20
+ * list.
21
21
  */
22
22
  const DEFAULT_PLATFORM_PREFIXES = ['--moz-'];
23
23
  /**
@@ -1,17 +1,15 @@
1
1
  /**
2
2
  * Dark-mode insertion helpers for the tokens CSS scaffold.
3
3
  *
4
- * The 2026-04-21 eval reproduced a bug where `fireforge token add
5
- * --mode override --dark-value ...` landed the dark declaration
6
- * AFTER the nested `:root { }` inside the
7
- * `@media (prefers-color-scheme: dark)` block had already closed,
8
- * producing a declaration outside any rule block. The helpers here
9
- * scan the comment-stripped source lines to find the *inner* `:root`
10
- * block's closing `}` and return a line index the caller can splice
11
- * into. When the inner `:root` is missing (a scaffold that drifted
12
- * from the default), the fallback helper returns the outer `@media`
13
- * block's close so the caller can materialise a fresh `:root` wrapper
14
- * rather than dropping the dark value.
4
+ * `fireforge token add --mode override --dark-value ...` must land the dark
5
+ * declaration INSIDE the nested `:root { }` of the
6
+ * `@media (prefers-color-scheme: dark)` block; landing after that block has
7
+ * closed produces a declaration outside any rule. The helpers here scan the
8
+ * comment-stripped source lines to find the *inner* `:root` block's closing
9
+ * `}` and return a line index the caller can splice into. When the inner
10
+ * `:root` is missing (a scaffold that drifted from the default), the
11
+ * fallback helper returns the outer `@media` block's close so the caller can
12
+ * materialise a fresh `:root` wrapper rather than dropping the dark value.
15
13
  */
16
14
  /**
17
15
  * Strips the content of `/* ... *\/` block comments from an array of
@@ -2,18 +2,34 @@
2
2
  /**
3
3
  * Dark-mode insertion helpers for the tokens CSS scaffold.
4
4
  *
5
- * The 2026-04-21 eval reproduced a bug where `fireforge token add
6
- * --mode override --dark-value ...` landed the dark declaration
7
- * AFTER the nested `:root { }` inside the
8
- * `@media (prefers-color-scheme: dark)` block had already closed,
9
- * producing a declaration outside any rule block. The helpers here
10
- * scan the comment-stripped source lines to find the *inner* `:root`
11
- * block's closing `}` and return a line index the caller can splice
12
- * into. When the inner `:root` is missing (a scaffold that drifted
13
- * from the default), the fallback helper returns the outer `@media`
14
- * block's close so the caller can materialise a fresh `:root` wrapper
15
- * rather than dropping the dark value.
5
+ * `fireforge token add --mode override --dark-value ...` must land the dark
6
+ * declaration INSIDE the nested `:root { }` of the
7
+ * `@media (prefers-color-scheme: dark)` block; landing after that block has
8
+ * closed produces a declaration outside any rule. The helpers here scan the
9
+ * comment-stripped source lines to find the *inner* `:root` block's closing
10
+ * `}` and return a line index the caller can splice into. When the inner
11
+ * `:root` is missing (a scaffold that drifted from the default), the
12
+ * fallback helper returns the outer `@media` block's close so the caller can
13
+ * materialise a fresh `:root` wrapper rather than dropping the dark value.
16
14
  */
15
+ /**
16
+ * True when `line` carries a `:root` selector whose opening brace is on the
17
+ * same line, with no intervening brace of either kind.
18
+ *
19
+ * Index arithmetic rather than `/:root[^{}]*\{/`: that pattern restarts at
20
+ * every `:root` in the line, which is quadratic on a line repeating the
21
+ * selector (CodeQL `js/polynomial-redos`). `tokens.css` comes from a
22
+ * consumer's engine tree, so such a line needs no attacker to arrive.
23
+ */
24
+ function rootOpensBraceOnLine(line) {
25
+ const brace = line.indexOf('{');
26
+ if (brace === -1)
27
+ return false;
28
+ const root = line.lastIndexOf(':root', brace - ':root'.length);
29
+ if (root === -1 || root + ':root'.length > brace)
30
+ return false;
31
+ return !line.slice(root + ':root'.length, brace).includes('}');
32
+ }
17
33
  /**
18
34
  * Strips the content of `/* ... *\/` block comments from an array of
19
35
  * CSS source lines while preserving each line's length. Indexed scans
@@ -87,9 +103,9 @@ export function findDarkRootInsertionIndex(lines) {
87
103
  let rootOpenLine = -1;
88
104
  for (let i = darkMediaLine; i < stripped.length; i++) {
89
105
  const line = stripped[i] ?? '';
90
- if (/(^|[\s,{])\s*:root\b/.test(line)) {
106
+ if (/(?:^|[\s,{]):root\b/.test(line)) {
91
107
  // Brace on the same line?
92
- if (/:root[^{}]*\{/.test(line)) {
108
+ if (rootOpensBraceOnLine(line)) {
93
109
  rootOpenLine = i;
94
110
  break;
95
111
  }
@@ -21,6 +21,8 @@ export interface TokenDocInput {
21
21
  * against a freshly parsed view of the document so that splice indices
22
22
  * stay valid as rewrites are layered.
23
23
  *
24
+ * @param engineDir - Absolute path to the engine checkout
25
+ * @param options - Token name, category and description to document
24
26
  * @param annotation - The mode annotation string the caller already computed
25
27
  * (kept here as a parameter so this module needs no dependency on
26
28
  * token-manager's `getModeAnnotation`).
@@ -26,6 +26,8 @@ function stripInlineCode(cell) {
26
26
  * against a freshly parsed view of the document so that splice indices
27
27
  * stay valid as rewrites are layered.
28
28
  *
29
+ * @param engineDir - Absolute path to the engine checkout
30
+ * @param options - Token name, category and description to document
29
31
  * @param annotation - The mode annotation string the caller already computed
30
32
  * (kept here as a parameter so this module needs no dependency on
31
33
  * token-manager's `getModeAnnotation`).
@@ -42,7 +44,7 @@ export async function addTokenToDocs(engineDir, options, annotation) {
42
44
  let unmappedAdded = false;
43
45
  let countUpdated = false;
44
46
  const isLiteral = !options.value.startsWith('var(');
45
- const mapsTo = isLiteral ? '—' : options.value.replace(/var\(([^)]+)\)/, '$1');
47
+ const mapsTo = isLiteral ? '—' : options.value.replace(/^var\(([^)]+)\)/, '$1');
46
48
  const tokenCell = `\`${options.tokenName}\``;
47
49
  const valueCell = `\`${options.value}\``;
48
50
  // --- Main token table: Category | Token | Value | Maps to | Mode ---
@@ -1,8 +1,16 @@
1
1
  import { type TokenDeclarationLocation } from './token-category.js';
2
2
  /**
3
- * Dark mode behavior for a token.
3
+ * Dark mode behaviour for a token.
4
+ *
5
+ * Derived from {@link TOKEN_MODES} so the runtime allowlist and the type
6
+ * cannot drift: adding a member here without updating the list (or the
7
+ * reverse) is no longer possible, because there is only one list.
4
8
  */
5
- export type TokenMode = 'auto' | 'static' | 'override';
9
+ export declare const TOKEN_MODES: readonly ["auto", "static", "override"];
10
+ /** Dark mode behaviour for a token. */
11
+ export type TokenMode = (typeof TOKEN_MODES)[number];
12
+ /** Narrows an arbitrary string to a {@link TokenMode}. */
13
+ export declare const isTokenMode: (value: string) => value is "override" | "auto" | "static";
6
14
  /**
7
15
  * Options for adding a token.
8
16
  */