@hominis/fireforge 0.44.4 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/dist/bin/fireforge.js +6 -0
  3. package/dist/build-info.json +4 -4
  4. package/dist/src/cli-usage-error.d.ts +46 -0
  5. package/dist/src/cli-usage-error.js +81 -0
  6. package/dist/src/cli.d.ts +5 -1
  7. package/dist/src/cli.js +20 -21
  8. package/dist/src/commands/bootstrap.js +1 -2
  9. package/dist/src/commands/build.js +15 -6
  10. package/dist/src/commands/destructive-decision.d.ts +19 -0
  11. package/dist/src/commands/destructive-decision.js +31 -0
  12. package/dist/src/commands/doctor-check-core.d.ts +0 -6
  13. package/dist/src/commands/doctor-check-core.js +0 -8
  14. package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
  15. package/dist/src/commands/doctor-orphaned-harness.js +4 -12
  16. package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
  17. package/dist/src/commands/doctor-source-pin.d.ts +29 -0
  18. package/dist/src/commands/doctor-source-pin.js +81 -0
  19. package/dist/src/commands/doctor.d.ts +2 -0
  20. package/dist/src/commands/doctor.js +9 -8
  21. package/dist/src/commands/download.js +35 -24
  22. package/dist/src/commands/export-all.js +21 -4
  23. package/dist/src/commands/export-flow.js +15 -60
  24. package/dist/src/commands/export-placement-conflicts.js +3 -3
  25. package/dist/src/commands/export-placement-gate.js +2 -8
  26. package/dist/src/commands/export-placement-policy.js +3 -5
  27. package/dist/src/commands/export-shared.d.ts +42 -23
  28. package/dist/src/commands/export-shared.js +4 -19
  29. package/dist/src/commands/export.js +10 -1
  30. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  31. package/dist/src/commands/furnace/authoring-config.js +19 -0
  32. package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
  33. package/dist/src/commands/furnace/create-browser-test.js +3 -8
  34. package/dist/src/commands/furnace/create-dry-run.js +2 -5
  35. package/dist/src/commands/furnace/create-templates.d.ts +17 -1
  36. package/dist/src/commands/furnace/create-templates.js +2 -1
  37. package/dist/src/commands/furnace/create.js +25 -19
  38. package/dist/src/commands/furnace/deploy.js +11 -2
  39. package/dist/src/commands/furnace/init.js +2 -1
  40. package/dist/src/commands/furnace/override.js +28 -19
  41. package/dist/src/commands/furnace/refresh.js +9 -1
  42. package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
  43. package/dist/src/commands/furnace/remove-mutations.js +0 -1
  44. package/dist/src/commands/furnace/remove.js +2 -6
  45. package/dist/src/commands/furnace/rename-browser-test.js +1 -7
  46. package/dist/src/commands/furnace/rename.js +29 -29
  47. package/dist/src/commands/furnace/status.js +12 -9
  48. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  49. package/dist/src/commands/furnace/test-file-name.js +39 -0
  50. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  51. package/dist/src/commands/furnace/validation-output.js +3 -8
  52. package/dist/src/commands/import.js +2 -9
  53. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  54. package/dist/src/commands/lint-per-patch.js +19 -4
  55. package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
  56. package/dist/src/commands/lint.d.ts +2 -1
  57. package/dist/src/commands/lint.js +1 -1
  58. package/dist/src/commands/manifest.js +1 -1
  59. package/dist/src/commands/package.js +2 -4
  60. package/dist/src/commands/patch/compact.js +14 -15
  61. package/dist/src/commands/patch/delete.js +15 -25
  62. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  63. package/dist/src/commands/patch/entry-projection.js +24 -0
  64. package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
  65. package/dist/src/commands/patch/lint-ignore.js +19 -29
  66. package/dist/src/commands/patch/move-files-create.js +24 -10
  67. package/dist/src/commands/patch/move-files-into.js +38 -37
  68. package/dist/src/commands/patch/move-files.js +4 -13
  69. package/dist/src/commands/patch/rename.js +27 -41
  70. package/dist/src/commands/patch/reorder.js +25 -38
  71. package/dist/src/commands/patch/split-plan.d.ts +4 -5
  72. package/dist/src/commands/patch/split-plan.js +4 -11
  73. package/dist/src/commands/patch/split.js +45 -43
  74. package/dist/src/commands/patch/staged-dependency.js +16 -22
  75. package/dist/src/commands/patch/tier.js +15 -25
  76. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  77. package/dist/src/commands/patch-rename-map.js +36 -0
  78. package/dist/src/commands/pending-resolution.d.ts +15 -0
  79. package/dist/src/commands/pending-resolution.js +25 -0
  80. package/dist/src/commands/re-export-adjacent.d.ts +32 -2
  81. package/dist/src/commands/re-export-adjacent.js +51 -10
  82. package/dist/src/commands/re-export-bulk-scan.js +6 -10
  83. package/dist/src/commands/re-export-drift.d.ts +8 -0
  84. package/dist/src/commands/re-export-drift.js +65 -4
  85. package/dist/src/commands/re-export-files.d.ts +2 -3
  86. package/dist/src/commands/re-export-files.js +38 -32
  87. package/dist/src/commands/re-export-lint.d.ts +3 -4
  88. package/dist/src/commands/re-export-lint.js +9 -2
  89. package/dist/src/commands/re-export-options.js +19 -5
  90. package/dist/src/commands/re-export-register.js +5 -1
  91. package/dist/src/commands/re-export-scan.js +8 -8
  92. package/dist/src/commands/re-export-single.d.ts +22 -3
  93. package/dist/src/commands/re-export-single.js +19 -11
  94. package/dist/src/commands/re-export.js +44 -27
  95. package/dist/src/commands/rebase/abort.js +3 -8
  96. package/dist/src/commands/rebase/continue.js +12 -12
  97. package/dist/src/commands/rebase/index.js +2 -2
  98. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  99. package/dist/src/commands/rebase/patch-loop.js +2 -7
  100. package/dist/src/commands/resolve.js +18 -10
  101. package/dist/src/commands/run.d.ts +0 -13
  102. package/dist/src/commands/run.js +12 -25
  103. package/dist/src/commands/setup-support.d.ts +16 -4
  104. package/dist/src/commands/setup-support.js +16 -16
  105. package/dist/src/commands/setup.js +3 -9
  106. package/dist/src/commands/source.js +8 -1
  107. package/dist/src/commands/status-check.js +2 -2
  108. package/dist/src/commands/status-output.js +2 -2
  109. package/dist/src/commands/status.js +3 -4
  110. package/dist/src/commands/test-build-phase.d.ts +2 -2
  111. package/dist/src/commands/test-build-phase.js +33 -8
  112. package/dist/src/commands/test-diagnose.js +14 -5
  113. package/dist/src/commands/test-modes.d.ts +2 -2
  114. package/dist/src/commands/test-modes.js +0 -1
  115. package/dist/src/commands/test-register.js +6 -3
  116. package/dist/src/commands/test-run.js +29 -21
  117. package/dist/src/commands/test-stale-gate.js +8 -5
  118. package/dist/src/commands/test-verdict.d.ts +16 -2
  119. package/dist/src/commands/test-verdict.js +18 -3
  120. package/dist/src/commands/test.js +51 -7
  121. package/dist/src/commands/token-list.d.ts +26 -0
  122. package/dist/src/commands/token-list.js +153 -0
  123. package/dist/src/commands/token.js +17 -1
  124. package/dist/src/commands/tree.js +7 -2
  125. package/dist/src/commands/typecheck.js +9 -9
  126. package/dist/src/commands/wire.js +4 -3
  127. package/dist/src/core/branding.d.ts +0 -7
  128. package/dist/src/core/branding.js +3 -2
  129. package/dist/src/core/browser-wire.js +6 -3
  130. package/dist/src/core/build-audit-platform.d.ts +29 -0
  131. package/dist/src/core/build-audit-platform.js +140 -0
  132. package/dist/src/core/build-audit-registration.js +3 -2
  133. package/dist/src/core/build-audit-skip.d.ts +57 -0
  134. package/dist/src/core/build-audit-skip.js +112 -0
  135. package/dist/src/core/build-audit.d.ts +15 -2
  136. package/dist/src/core/build-audit.js +154 -12
  137. package/dist/src/core/build-baseline.d.ts +45 -28
  138. package/dist/src/core/build-baseline.js +6 -29
  139. package/dist/src/core/build-jar-escalation.d.ts +64 -0
  140. package/dist/src/core/build-jar-escalation.js +128 -0
  141. package/dist/src/core/build-overwrite-guard.js +1 -1
  142. package/dist/src/core/build-prepare.d.ts +5 -2
  143. package/dist/src/core/build-prepare.js +34 -6
  144. package/dist/src/core/config-paths.d.ts +2 -6
  145. package/dist/src/core/config-paths.js +4 -2
  146. package/dist/src/core/config-state.d.ts +0 -6
  147. package/dist/src/core/config-state.js +1 -19
  148. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  149. package/dist/src/core/config-validate-build-audit.js +80 -0
  150. package/dist/src/core/config-validate-file-size.d.ts +12 -0
  151. package/dist/src/core/config-validate-file-size.js +65 -0
  152. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  153. package/dist/src/core/config-validate-patch-policy.js +3 -3
  154. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  155. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  156. package/dist/src/core/config-validate.js +20 -3
  157. package/dist/src/core/config.d.ts +2 -2
  158. package/dist/src/core/config.js +11 -15
  159. package/dist/src/core/coverage-extend.js +4 -4
  160. package/dist/src/core/discard-baseline.js +7 -7
  161. package/dist/src/core/engine-precondition.d.ts +1 -9
  162. package/dist/src/core/engine-precondition.js +3 -8
  163. package/dist/src/core/engine-session-lock.js +13 -17
  164. package/dist/src/core/export-moved-code-shape.js +2 -2
  165. package/dist/src/core/file-lock-owner.d.ts +103 -0
  166. package/dist/src/core/file-lock-owner.js +259 -0
  167. package/dist/src/core/file-lock.d.ts +2 -7
  168. package/dist/src/core/file-lock.js +40 -91
  169. package/dist/src/core/firefox-cache.d.ts +13 -1
  170. package/dist/src/core/firefox-cache.js +59 -34
  171. package/dist/src/core/firefox-download.d.ts +15 -0
  172. package/dist/src/core/firefox-download.js +37 -21
  173. package/dist/src/core/firefox-extract.d.ts +6 -5
  174. package/dist/src/core/firefox-extract.js +36 -18
  175. package/dist/src/core/firefox.d.ts +30 -32
  176. package/dist/src/core/firefox.js +5 -36
  177. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  178. package/dist/src/core/furnace-apply-dry-run.js +1 -7
  179. package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
  180. package/dist/src/core/furnace-apply-helpers.js +8 -19
  181. package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
  182. package/dist/src/core/furnace-apply.d.ts +1 -1
  183. package/dist/src/core/furnace-apply.js +30 -10
  184. package/dist/src/core/furnace-config.d.ts +1 -1
  185. package/dist/src/core/furnace-config.js +23 -36
  186. package/dist/src/core/furnace-constants.js +3 -2
  187. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  188. package/dist/src/core/furnace-dir-entry.js +21 -0
  189. package/dist/src/core/furnace-operation.d.ts +2 -0
  190. package/dist/src/core/furnace-operation.js +3 -1
  191. package/dist/src/core/furnace-refresh.d.ts +18 -6
  192. package/dist/src/core/furnace-refresh.js +3 -6
  193. package/dist/src/core/furnace-registration-ast.d.ts +0 -1
  194. package/dist/src/core/furnace-registration-ast.js +0 -3
  195. package/dist/src/core/furnace-registration.d.ts +2 -1
  196. package/dist/src/core/furnace-registration.js +22 -29
  197. package/dist/src/core/furnace-scanner.js +3 -6
  198. package/dist/src/core/furnace-state-persist.d.ts +2 -2
  199. package/dist/src/core/furnace-validate-registration.js +5 -7
  200. package/dist/src/core/git-base.d.ts +3 -1
  201. package/dist/src/core/git-base.js +25 -3
  202. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  203. package/dist/src/core/git-diff-new-file.js +119 -0
  204. package/dist/src/core/git-diff.d.ts +9 -9
  205. package/dist/src/core/git-diff.js +88 -157
  206. package/dist/src/core/git-file-ops.d.ts +0 -7
  207. package/dist/src/core/git-file-ops.js +4 -28
  208. package/dist/src/core/git-readonly-index.d.ts +0 -2
  209. package/dist/src/core/git-readonly-index.js +0 -4
  210. package/dist/src/core/git-status.d.ts +7 -0
  211. package/dist/src/core/git-status.js +21 -9
  212. package/dist/src/core/git.d.ts +0 -6
  213. package/dist/src/core/git.js +14 -43
  214. package/dist/src/core/harness-orphans.d.ts +73 -0
  215. package/dist/src/core/harness-orphans.js +187 -0
  216. package/dist/src/core/history-log.d.ts +19 -0
  217. package/dist/src/core/history-log.js +29 -0
  218. package/dist/src/core/mach-error-hints.d.ts +0 -5
  219. package/dist/src/core/mach-error-hints.js +1 -1
  220. package/dist/src/core/mach-known-noise-filter.d.ts +25 -3
  221. package/dist/src/core/mach-known-noise-filter.js +31 -10
  222. package/dist/src/core/mach-mozconfig.js +2 -1
  223. package/dist/src/core/mach-python.d.ts +2 -0
  224. package/dist/src/core/mach-python.js +12 -7
  225. package/dist/src/core/mach-resource-shim.d.ts +13 -1
  226. package/dist/src/core/mach-resource-shim.js +112 -9
  227. package/dist/src/core/mach.d.ts +46 -53
  228. package/dist/src/core/mach.js +31 -83
  229. package/dist/src/core/marionette-port.d.ts +0 -17
  230. package/dist/src/core/marionette-port.js +12 -58
  231. package/dist/src/core/marionette-preflight.js +9 -27
  232. package/dist/src/core/mochitest-server-port.js +3 -3
  233. package/dist/src/core/moz-manifest-rules.js +37 -33
  234. package/dist/src/core/patch-apply-fuzz.js +4 -4
  235. package/dist/src/core/patch-apply.d.ts +4 -5
  236. package/dist/src/core/patch-apply.js +55 -32
  237. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  238. package/dist/src/core/patch-export-coverage.js +1 -30
  239. package/dist/src/core/patch-export-update.d.ts +18 -1
  240. package/dist/src/core/patch-export-update.js +4 -3
  241. package/dist/src/core/patch-export.d.ts +11 -21
  242. package/dist/src/core/patch-export.js +44 -66
  243. package/dist/src/core/patch-files.d.ts +0 -4
  244. package/dist/src/core/patch-files.js +0 -11
  245. package/dist/src/core/patch-lint-binary.d.ts +6 -12
  246. package/dist/src/core/patch-lint-cache.d.ts +17 -1
  247. package/dist/src/core/patch-lint-cache.js +3 -6
  248. package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
  249. package/dist/src/core/patch-lint-checkjs.js +43 -25
  250. package/dist/src/core/patch-lint-creators.d.ts +2 -6
  251. package/dist/src/core/patch-lint-cross.d.ts +16 -8
  252. package/dist/src/core/patch-lint-cross.js +49 -92
  253. package/dist/src/core/patch-lint-css.js +10 -6
  254. package/dist/src/core/patch-lint-file-size.d.ts +52 -0
  255. package/dist/src/core/patch-lint-file-size.js +53 -0
  256. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  257. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  258. package/dist/src/core/patch-lint-jsdoc.js +36 -36
  259. package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
  260. package/dist/src/core/patch-lint-prettier.d.ts +36 -0
  261. package/dist/src/core/patch-lint-prettier.js +126 -0
  262. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  263. package/dist/src/core/patch-lint-queue-types.js +12 -0
  264. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  265. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  266. package/dist/src/core/patch-lint-unmanaged-companion.d.ts +106 -0
  267. package/dist/src/core/patch-lint-unmanaged-companion.js +214 -0
  268. package/dist/src/core/patch-lint.d.ts +14 -9
  269. package/dist/src/core/patch-lint.js +27 -27
  270. package/dist/src/core/patch-lock.js +3 -3
  271. package/dist/src/core/patch-manifest-io.js +8 -7
  272. package/dist/src/core/patch-parse.d.ts +19 -8
  273. package/dist/src/core/patch-parse.js +25 -0
  274. package/dist/src/core/patch-policy.js +15 -27
  275. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  276. package/dist/src/core/patch-source-metadata.js +0 -4
  277. package/dist/src/core/patch-transform.d.ts +21 -8
  278. package/dist/src/core/patch-transform.js +38 -10
  279. package/dist/src/core/process-cpu-time.js +1 -1
  280. package/dist/src/core/rebase-session.d.ts +2 -8
  281. package/dist/src/core/rebase-session.js +5 -17
  282. package/dist/src/core/register-browser-content.js +3 -2
  283. package/dist/src/core/register-browser-test.js +2 -1
  284. package/dist/src/core/register-module.js +2 -1
  285. package/dist/src/core/register-scaffold.js +5 -4
  286. package/dist/src/core/register-shared-css.js +2 -1
  287. package/dist/src/core/run-log-redact.d.ts +38 -0
  288. package/dist/src/core/run-log-redact.js +69 -0
  289. package/dist/src/core/run-log.d.ts +13 -2
  290. package/dist/src/core/run-log.js +67 -18
  291. package/dist/src/core/signal-critical.js +2 -4
  292. package/dist/src/core/smoke-patterns.d.ts +0 -1
  293. package/dist/src/core/smoke-patterns.js +18 -11
  294. package/dist/src/core/state-file.d.ts +12 -1
  295. package/dist/src/core/state-file.js +14 -1
  296. package/dist/src/core/status-classify.js +2 -1
  297. package/dist/src/core/test-changed-prefs.js +26 -11
  298. package/dist/src/core/test-harness-crash.js +61 -48
  299. package/dist/src/core/test-harness-verdict-notes.d.ts +55 -0
  300. package/dist/src/core/test-harness-verdict-notes.js +48 -0
  301. package/dist/src/core/test-stale-check.d.ts +23 -8
  302. package/dist/src/core/test-stale-check.js +77 -4
  303. package/dist/src/core/test-stall-triage.js +37 -19
  304. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  305. package/dist/src/core/test-xpcshell-retry.js +14 -11
  306. package/dist/src/core/token-category.d.ts +33 -0
  307. package/dist/src/core/token-category.js +56 -48
  308. package/dist/src/core/token-dark-mode.js +2 -14
  309. package/dist/src/core/token-inventory.d.ts +56 -0
  310. package/dist/src/core/token-inventory.js +109 -0
  311. package/dist/src/core/token-manager.d.ts +1 -1
  312. package/dist/src/core/token-manager.js +1 -1
  313. package/dist/src/core/token-variant.js +21 -22
  314. package/dist/src/core/toolchain-preflight.js +1 -1
  315. package/dist/src/core/tree-cow.d.ts +0 -2
  316. package/dist/src/core/tree-cow.js +2 -2
  317. package/dist/src/core/tree-store.js +4 -5
  318. package/dist/src/core/typecheck.js +2 -2
  319. package/dist/src/core/wire-dom-fragment.js +6 -17
  320. package/dist/src/core/wire-utils.js +5 -17
  321. package/dist/src/core/xpcshell-appdir.js +2 -4
  322. package/dist/src/errors/base.d.ts +53 -1
  323. package/dist/src/errors/base.js +79 -11
  324. package/dist/src/errors/build.d.ts +28 -0
  325. package/dist/src/errors/build.js +51 -10
  326. package/dist/src/errors/config.d.ts +0 -14
  327. package/dist/src/errors/config.js +13 -34
  328. package/dist/src/errors/download.d.ts +13 -0
  329. package/dist/src/errors/download.js +53 -24
  330. package/dist/src/errors/furnace.js +8 -9
  331. package/dist/src/errors/git.d.ts +8 -8
  332. package/dist/src/errors/git.js +20 -30
  333. package/dist/src/errors/patch.js +8 -10
  334. package/dist/src/errors/rebase.js +7 -7
  335. package/dist/src/types/commands/options.d.ts +96 -123
  336. package/dist/src/types/commands/patches.d.ts +17 -6
  337. package/dist/src/types/config.d.ts +75 -2
  338. package/dist/src/types/index.d.ts +0 -1
  339. package/dist/src/utils/assert.d.ts +3 -15
  340. package/dist/src/utils/assert.js +1 -18
  341. package/dist/src/utils/build-info.d.ts +1 -1
  342. package/dist/src/utils/child-env.d.ts +2 -1
  343. package/dist/src/utils/elapsed.js +10 -1
  344. package/dist/src/utils/fs.d.ts +4 -2
  345. package/dist/src/utils/fs.js +11 -14
  346. package/dist/src/utils/hash.d.ts +12 -0
  347. package/dist/src/utils/hash.js +16 -0
  348. package/dist/src/utils/line-dispatch.d.ts +15 -0
  349. package/dist/src/utils/line-dispatch.js +48 -0
  350. package/dist/src/utils/options.d.ts +1 -7
  351. package/dist/src/utils/options.js +6 -7
  352. package/dist/src/utils/package-root.js +3 -12
  353. package/dist/src/utils/platform.d.ts +0 -4
  354. package/dist/src/utils/process-group.d.ts +2 -1
  355. package/dist/src/utils/process-group.js +20 -19
  356. package/dist/src/utils/process.d.ts +40 -26
  357. package/dist/src/utils/process.js +115 -166
  358. package/dist/src/utils/ps-duration.js +3 -3
  359. package/dist/src/utils/sleep.d.ts +26 -0
  360. package/dist/src/utils/sleep.js +31 -0
  361. package/dist/src/utils/validation.d.ts +2 -38
  362. package/dist/src/utils/validation.js +7 -16
  363. package/docs/configuration.md +136 -1
  364. package/docs/exit-codes.md +34 -20
  365. package/docs/lifecycle-invariants.md +18 -12
  366. package/docs/machine-output.md +31 -8
  367. package/docs/patch-workflow.md +2 -1
  368. package/docs/run-logs.md +44 -4
  369. package/docs/testing.md +70 -11
  370. package/package.json +8 -7
  371. package/dist/src/commands/rebase.d.ts +0 -4
  372. package/dist/src/commands/rebase.js +0 -5
  373. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  374. package/dist/src/core/patch-artifact-normalize.js +0 -24
  375. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  376. package/dist/src/core/patch-lint-reexports.js +0 -8
  377. package/dist/src/core/wire-targets.d.ts +0 -7
  378. package/dist/src/core/wire-targets.js +0 -8
@@ -38,33 +38,51 @@ const NO_OUTPUT_STALL_CONTROL_STEP = 'First, run a known-good control test. If t
38
38
  * its manifest from `server.js` on 8888, and a survivor of an
39
39
  * interrupted run keeps the port. Its probe is exact, and the preflight
40
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.
41
+ * - (3) is now MECHANICAL and root-caused, replacing the correlation an
42
+ * earlier revision recorded. A `chrome://` (or `resource://`) URL that
43
+ * resolves to nothing reaches `CheckForBrokenChromeURL`
44
+ * (netwerk/base/nsNetUtil.cpp), which outside automation only
45
+ * `printf_stderr`s `Missing chrome or resource URL: <uri>` — but under
46
+ * `xpc::IsInAutomation()` the same condition is
47
+ * `MOZ_CRASH_UNSAFE_PRINTF("Missing chrome or resource URLs: %s")`. A
48
+ * downstream reproduction symbolicated the faulting frame as
49
+ * `CheckForBrokenChromeURL`, which is the established diagnosis the bar
50
+ * in `mach-error-hints.ts` asks for. The discredited "stalls first
51
+ * paint" story an even earlier revision asserted must not come back: a
52
+ * failed image load in Gecko fires `error` and does not gate the
53
+ * document load event.
49
54
  *
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.
55
+ * Detection by log line is deliberately NOT specified for (3), and the
56
+ * reason is the mechanism itself: in the reproduction the crash landed in a
57
+ * CONTENT process (`plugin-container`, a child of the browser) with the
58
+ * crash reporter compiled out, so the harness log carried neither the crash
59
+ * message nor a crash line — only the no-output timeout, `Ran 0 checks`,
60
+ * and mochitest's "Can't trigger Breakpad, just killing process". Under
61
+ * automation the message is in the process that DIED, not in the log, so
62
+ * the census points at the two artefacts that do carry it: the OS crash
63
+ * report (whose faulting frame names `CheckForBrokenChromeURL`) and the
64
+ * out-of-automation smoke probe (which prints the non-crashing spelling
65
+ * and, since 0.45.0, counts it as an unallowed error so the exit code
66
+ * agrees with the capture).
54
67
  */
55
68
  const NO_OUTPUT_STALL_TRIAGE = [
56
69
  ' 1. A sleeping or locked display on an unattended HEADED run (macOS). The browser never ' +
57
70
  'paints and never reaches its first test.',
58
71
  ' 2. A headless SWGL compositor failure — re-run with --headless to separate this from (1): ' +
59
72
  '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 ' +
73
+ ' 3. A broken `chrome://` or `resource://` URL reached from the startup document. Under ' +
74
+ 'automation this is a MOZ_CRASH in whichever process opened the channel ' +
75
+ '(CheckForBrokenChromeURL, netwerk/base/nsNetUtil.cpp), so the browser dies before the ' +
76
+ 'harness sees anything. Expect the log to name NOTHING: with the crash in a content ' +
77
+ 'process and the crash reporter compiled out, the only artefacts are the OS crash report ' +
78
+ '(macOS: ~/Library/Logs/DiagnosticReports/plugin-container-*.ips — the faulting frame ' +
79
+ 'reads CheckForBrokenChromeURL) and the smoke probe below. Discriminate with ' +
64
80
  '"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.',
81
+ 'same browser OUTSIDE automation, where the same condition only PRINTS ' +
82
+ '"Missing chrome or resource URL: <uri>" — a line the probe counts as an unallowed ' +
83
+ 'error, so its exit code and its capture now agree. A clean window with no unallowed ' +
84
+ 'console errors means product-side startup is healthy and the stall is harness- or ' +
85
+ 'test-side.',
68
86
  ' 4. A stale mochitest httpd holding the harness server port (127.0.0.1:8888). A fresh ' +
69
87
  "browser connects to THAT server, which cannot serve this run's manifest, so the run " +
70
88
  'stalls before TEST_START. Probe with "lsof -nP -iTCP:8888": any listener that is not ' +
@@ -1,14 +1,33 @@
1
- import { type MachCommandResult } from './mach.js';
1
+ import { type MachCommandResult, type MachTestSuiteKind } from './mach.js';
2
2
  export interface XpcshellRetryClassification {
3
3
  xpcshell: readonly string[];
4
4
  nonXpcshell: readonly string[];
5
5
  }
6
- /** Dispatches a (possibly suite-specific) mach test run, mirroring `testWithOutput`. */
7
- export type TestDispatch = (engineDir: string, testPaths: string[], args: string[], env?: Record<string, string>, fullOutput?: boolean) => Promise<MachCommandResult>;
6
+ export interface XpcshellRetryOptions {
7
+ /** Engine checkout the run happened in. */
8
+ engineDir: string;
9
+ /** Object directory the run used, when one is configured. */
10
+ objDir: string | undefined;
11
+ /** Result of the run that may need repairing and retrying. */
12
+ result: MachCommandResult;
13
+ /** Which of the requested paths are xpcshell tests, and which are not. */
14
+ classification: XpcshellRetryClassification;
15
+ /** Test paths, normalized, as handed to mach. */
16
+ normalizedPaths: string[];
17
+ /** Extra mach arguments the run was started with. */
18
+ extraArgs: string[];
19
+ /** Environment overlay for the retried run. */
20
+ env?: Record<string, string> | undefined;
21
+ /** Mach command kind to retry with. Defaults to the generic `test`. */
22
+ kind?: MachTestSuiteKind | undefined;
23
+ /** Whether the run was started with full harness output. */
24
+ fullOutput?: boolean | undefined;
25
+ }
8
26
  /**
9
27
  * Removes a stale xpcshell install symlink and retries the focused mach test
10
- * once. The retry uses the same `dispatch` (suite-specific or generic) the
11
- * caller is already running on, so an xpcshell-suite run repairs and re-runs
12
- * via `mach xpcshell-test` rather than falling back to the generic command.
28
+ * once. The retry uses the same mach command `kind` (suite-specific or
29
+ * generic) the caller is already running on, so an xpcshell-suite run repairs
30
+ * and re-runs via `mach xpcshell-test` rather than falling back to the
31
+ * generic command.
13
32
  */
14
- export declare function retryAfterXpcshellSymlinkRepair(engineDir: string, objDir: string | undefined, result: MachCommandResult, classification: XpcshellRetryClassification, normalizedPaths: string[], extraArgs: string[], env?: Record<string, string>, dispatch?: TestDispatch, fullOutput?: boolean): Promise<MachCommandResult>;
33
+ export declare function retryAfterXpcshellSymlinkRepair(options: XpcshellRetryOptions): Promise<MachCommandResult>;
@@ -1,13 +1,15 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
- import { testWithOutput } from './mach.js';
2
+ import { runMachTestSuite } from './mach.js';
3
3
  import { tryRepairStaleXpcshellTestSymlink } from './test-stale-symlink.js';
4
4
  /**
5
5
  * Removes a stale xpcshell install symlink and retries the focused mach test
6
- * once. The retry uses the same `dispatch` (suite-specific or generic) the
7
- * caller is already running on, so an xpcshell-suite run repairs and re-runs
8
- * via `mach xpcshell-test` rather than falling back to the generic command.
6
+ * once. The retry uses the same mach command `kind` (suite-specific or
7
+ * generic) the caller is already running on, so an xpcshell-suite run repairs
8
+ * and re-runs via `mach xpcshell-test` rather than falling back to the
9
+ * generic command.
9
10
  */
10
- export async function retryAfterXpcshellSymlinkRepair(engineDir, objDir, result, classification, normalizedPaths, extraArgs, env, dispatch = testWithOutput, fullOutput) {
11
+ export async function retryAfterXpcshellSymlinkRepair(options) {
12
+ const { engineDir, objDir, result, classification, normalizedPaths, extraArgs, env, kind = 'test', fullOutput, } = options;
11
13
  if (result.exitCode !== 0 &&
12
14
  classification.xpcshell.length > 0 &&
13
15
  classification.nonXpcshell.length === 0) {
@@ -15,12 +17,13 @@ export async function retryAfterXpcshellSymlinkRepair(engineDir, objDir, result,
15
17
  if (repaired) {
16
18
  // The repaired re-run is the same logical invocation, so it keeps
17
19
  // the verbosity the run was started with.
18
- if (fullOutput === true) {
19
- return dispatch(engineDir, normalizedPaths, extraArgs, env, true);
20
- }
21
- return env
22
- ? dispatch(engineDir, normalizedPaths, extraArgs, env)
23
- : dispatch(engineDir, normalizedPaths, extraArgs);
20
+ return runMachTestSuite(kind, {
21
+ engineDir,
22
+ testPaths: normalizedPaths,
23
+ args: extraArgs,
24
+ env,
25
+ fullOutput,
26
+ });
24
27
  }
25
28
  }
26
29
  return result;
@@ -5,6 +5,32 @@
5
5
  * agree on what "exists" means.
6
6
  */
7
7
  export declare function categoryHeaderExists(lines: string[], category: string): boolean;
8
+ /**
9
+ * Scans a tokens CSS file for category header comments and returns the
10
+ * category names in document order. Used to enrich the "category not
11
+ * found" error body with concrete alternatives the operator can copy.
12
+ *
13
+ * Mirrors the shapes `findCategorySection` recognises:
14
+ * - Single-line: `/* = Foo = *\/`
15
+ * - Multi-line: `/* =====` opening, `Foo` on any of the next ~5 lines,
16
+ * closing `*\/`.
17
+ *
18
+ * This helper exists as a pure inspector; it never throws on malformed
19
+ * headers and silently skips shapes it cannot parse.
20
+ */
21
+ /**
22
+ * Category name declared by the banner STARTING at `index`, or `undefined`
23
+ * when that line opens no banner.
24
+ *
25
+ * The single reader for both banner shapes, so the inventory walk, the
26
+ * "available categories" error body and the section finder cannot drift on
27
+ * what counts as a header.
28
+ *
29
+ * @param lines - Tokens CSS split into lines
30
+ * @param index - 0-based line index to test
31
+ * @returns The declared category name, or undefined
32
+ */
33
+ export declare function categoryBannerNameAt(lines: string[], index: number): string | undefined;
8
34
  /**
9
35
  * Asserts the category banner exists (or that creation was requested).
10
36
  * Throws a GeneralError naming the available categories and the
@@ -23,6 +49,13 @@ export declare function findCategorySection(lines: string[], category: string, t
23
49
  categoryLine: number;
24
50
  sectionEnd: number;
25
51
  };
52
+ /** 0-based open/close line indices of the base `:root {` block. */
53
+ export declare function findBaseRootBounds(lines: string[]): {
54
+ open: number;
55
+ close: number;
56
+ } | undefined;
57
+ /** Masks block-comment content per line so declarations inside comments never match. */
58
+ export declare function maskCommentLines(lines: string[]): string[];
26
59
  /** Location of an existing base-`:root` token declaration. */
27
60
  export interface TokenDeclarationLocation {
28
61
  /** 1-based line number in the tokens CSS file. */
@@ -20,8 +20,8 @@ function multiLineBlockNameMatches(blockLine, category) {
20
20
  return blockLine.replace(/^\s*\*\s*/, '').trim() === category;
21
21
  }
22
22
  /**
23
- * Body of the first `/* … *\/` comment on `line`, or `null` when the line
24
- * carries no closed block comment.
23
+ * Body of the first `/* … *\/` comment on `line`, or `undefined` when the
24
+ * line carries no closed block comment.
25
25
  *
26
26
  * Deliberately index arithmetic rather than a regex: every regex spelling of
27
27
  * "banner comment" this module used to carry was super-linear on a line that
@@ -32,10 +32,10 @@ function multiLineBlockNameMatches(blockLine, category) {
32
32
  function blockCommentBody(line) {
33
33
  const open = line.indexOf('/*');
34
34
  if (open === -1)
35
- return null;
35
+ return undefined;
36
36
  const close = line.indexOf('*/', open + 2);
37
37
  if (close === -1)
38
- return null;
38
+ return undefined;
39
39
  return line.slice(open + 2, close);
40
40
  }
41
41
  /**
@@ -49,14 +49,15 @@ function isSingleLineBannerLine(line) {
49
49
  return body !== undefined && body.length >= 2 && body.startsWith('=') && body.endsWith('=');
50
50
  }
51
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`.
52
+ * Name declared by a single-line banner (`/* = Foo = *\/` → `Foo`), or
53
+ * `undefined` when the line is not a named banner. The `=` runs on both sides
54
+ * are required; a decorative all-`=` rule carries no name and yields
55
+ * `undefined`.
55
56
  */
56
57
  function singleLineBannerName(line) {
57
58
  const body = blockCommentBody(line);
58
- if (body === null)
59
- return null;
59
+ if (body === undefined)
60
+ return undefined;
60
61
  let start = 0;
61
62
  let end = body.length;
62
63
  while (start < end && /\s/.test(body[start] ?? ''))
@@ -65,16 +66,16 @@ function singleLineBannerName(line) {
65
66
  while (start < end && body[start] === '=')
66
67
  start++;
67
68
  if (start === leadingEqStart)
68
- return null;
69
+ return undefined;
69
70
  while (end > start && /\s/.test(body[end - 1] ?? ''))
70
71
  end--;
71
72
  const trailingEqEnd = end;
72
73
  while (end > start && body[end - 1] === '=')
73
74
  end--;
74
75
  if (end === trailingEqEnd)
75
- return null;
76
+ return undefined;
76
77
  const name = body.slice(start, end).trim();
77
- return name.length > 0 ? name : null;
78
+ return name.length > 0 ? name : undefined;
78
79
  }
79
80
  /**
80
81
  * True when `lines` contain a category header (single-line or multi-line
@@ -84,8 +85,7 @@ function singleLineBannerName(line) {
84
85
  */
85
86
  export function categoryHeaderExists(lines, category) {
86
87
  const singleLinePattern = singleLineBannerPattern(category);
87
- for (let i = 0; i < lines.length; i++) {
88
- const line = lines[i] ?? '';
88
+ for (const [i, line] of lines.entries()) {
89
89
  if (singleLinePattern.test(line)) {
90
90
  return true;
91
91
  }
@@ -115,29 +115,44 @@ export function categoryHeaderExists(lines, category) {
115
115
  * This helper exists as a pure inspector; it never throws on malformed
116
116
  * headers and silently skips shapes it cannot parse.
117
117
  */
118
+ /**
119
+ * Category name declared by the banner STARTING at `index`, or `undefined`
120
+ * when that line opens no banner.
121
+ *
122
+ * The single reader for both banner shapes, so the inventory walk, the
123
+ * "available categories" error body and the section finder cannot drift on
124
+ * what counts as a header.
125
+ *
126
+ * @param lines - Tokens CSS split into lines
127
+ * @param index - 0-based line index to test
128
+ * @returns The declared category name, or undefined
129
+ */
130
+ export function categoryBannerNameAt(lines, index) {
131
+ const line = lines[index] ?? '';
132
+ const extracted = singleLineBannerName(line);
133
+ if (extracted !== undefined)
134
+ return extracted;
135
+ if (/^\s*\/\*\s*=+/.test(line) && !/\*\//.test(line)) {
136
+ for (let j = index + 1; j < Math.min(index + 6, lines.length); j++) {
137
+ const blockLine = lines[j] ?? '';
138
+ if (/\*\//.test(blockLine))
139
+ break;
140
+ const trimmed = blockLine.replace(/^\s*\*\s*/, '').trim();
141
+ if (trimmed.length === 0)
142
+ continue;
143
+ if (/^=+$/.test(trimmed))
144
+ continue;
145
+ return trimmed;
146
+ }
147
+ }
148
+ return undefined;
149
+ }
118
150
  function discoverCategoryHeaders(lines) {
119
151
  const categories = new Set();
120
152
  for (let i = 0; i < lines.length; i++) {
121
- const line = lines[i] ?? '';
122
- const extracted = singleLineBannerName(line);
123
- if (extracted !== null) {
124
- categories.add(extracted);
125
- continue;
126
- }
127
- if (/^\s*\/\*\s*=+/.test(line) && !/\*\//.test(line)) {
128
- for (let j = i + 1; j < Math.min(i + 6, lines.length); j++) {
129
- const blockLine = lines[j] ?? '';
130
- if (/\*\//.test(blockLine))
131
- break;
132
- const trimmed = blockLine.replace(/^\s*\*\s*/, '').trim();
133
- if (trimmed.length === 0)
134
- continue;
135
- if (/^=+$/.test(trimmed))
136
- continue;
137
- categories.add(trimmed);
138
- break;
139
- }
140
- }
153
+ const name = categoryBannerNameAt(lines, i);
154
+ if (name !== undefined)
155
+ categories.add(name);
141
156
  }
142
157
  return [...categories];
143
158
  }
@@ -177,7 +192,7 @@ export async function assertTokenCategoryExists(engineDir, tokensCssPath, catego
177
192
  */
178
193
  export function declareCategoryBanner(lines, category, tokensCssPath) {
179
194
  const bounds = findBaseRootBounds(lines);
180
- if (bounds === null) {
195
+ if (bounds === undefined) {
181
196
  throw new GeneralError(`Cannot create category "${category}": no :root block found in ${tokensCssPath}. ` +
182
197
  'Run "fireforge furnace init --force" to re-scaffold the tokens CSS file.');
183
198
  }
@@ -190,8 +205,7 @@ export function declareCategoryBanner(lines, category, tokensCssPath) {
190
205
  export function findCategorySection(lines, category, tokensCssPath) {
191
206
  const singleLinePattern = singleLineBannerPattern(category);
192
207
  let categoryLine = -1;
193
- for (let i = 0; i < lines.length; i++) {
194
- const line = lines[i] ?? '';
208
+ for (const [i, line] of lines.entries()) {
195
209
  // Check single-line format: /* = Category = */
196
210
  if (singleLinePattern.test(line)) {
197
211
  categoryLine = i;
@@ -245,16 +259,10 @@ export function findCategorySection(lines, category, tokensCssPath) {
245
259
  return { categoryLine, sectionEnd };
246
260
  }
247
261
  /** 0-based open/close line indices of the base `:root {` block. */
248
- function findBaseRootBounds(lines) {
249
- let open = -1;
250
- for (let i = 0; i < lines.length; i++) {
251
- if (/:root\s*\{/.test(lines[i] ?? '')) {
252
- open = i;
253
- break;
254
- }
255
- }
262
+ export function findBaseRootBounds(lines) {
263
+ const open = lines.findIndex((line) => /:root\s*\{/.test(line));
256
264
  if (open === -1)
257
- return null;
265
+ return undefined;
258
266
  for (let i = open + 1; i < lines.length; i++) {
259
267
  if (/^\s*\}/.test(lines[i] ?? '')) {
260
268
  return { open, close: i };
@@ -263,7 +271,7 @@ function findBaseRootBounds(lines) {
263
271
  return { open, close: -1 };
264
272
  }
265
273
  /** Masks block-comment content per line so declarations inside comments never match. */
266
- function maskCommentLines(lines) {
274
+ export function maskCommentLines(lines) {
267
275
  const masked = lines.join('\n').replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '));
268
276
  return masked.split('\n');
269
277
  }
@@ -276,7 +284,7 @@ function maskCommentLines(lines) {
276
284
  */
277
285
  export function findTokenDeclarationInRoot(lines, tokenName) {
278
286
  const bounds = findBaseRootBounds(lines);
279
- if (bounds === null || bounds.close === -1)
287
+ if (bounds === undefined || bounds.close === -1)
280
288
  return undefined;
281
289
  const masked = maskCommentLines(lines);
282
290
  const declPattern = new RegExp(`^\\s*${escapeRegex(tokenName)}\\s*:`);
@@ -87,13 +87,7 @@ export function stripBlockCommentsInLines(lines) {
87
87
  */
88
88
  export function findDarkRootInsertionIndex(lines) {
89
89
  const stripped = stripBlockCommentsInLines(lines);
90
- let darkMediaLine = -1;
91
- for (let i = 0; i < stripped.length; i++) {
92
- if (/prefers-color-scheme:\s*dark/.test(stripped[i] ?? '')) {
93
- darkMediaLine = i;
94
- break;
95
- }
96
- }
90
+ const darkMediaLine = stripped.findIndex((line) => /prefers-color-scheme:\s*dark/.test(line));
97
91
  if (darkMediaLine === -1)
98
92
  return null;
99
93
  // Walk the comment-stripped lines after the @media header and find
@@ -172,13 +166,7 @@ export function findBlockCloseIndex(stripped, startLine) {
172
166
  */
173
167
  export function findDarkMediaCloseIndex(lines) {
174
168
  const stripped = stripBlockCommentsInLines(lines);
175
- let darkMediaLine = -1;
176
- for (let i = 0; i < stripped.length; i++) {
177
- if (/prefers-color-scheme:\s*dark/.test(stripped[i] ?? '')) {
178
- darkMediaLine = i;
179
- break;
180
- }
181
- }
169
+ const darkMediaLine = stripped.findIndex((line) => /prefers-color-scheme:\s*dark/.test(line));
182
170
  if (darkMediaLine === -1)
183
171
  return -1;
184
172
  return findBlockCloseIndex(stripped, darkMediaLine);
@@ -0,0 +1,56 @@
1
+ /** One token declaration inside the base `:root` block. */
2
+ export interface TokenInventoryEntry {
3
+ /** Custom-property name, including the leading `--`. */
4
+ name: string;
5
+ /** 1-based line number in the tokens CSS file. */
6
+ line: number;
7
+ /** Declared value, trimmed, without the trailing `;`. */
8
+ value: string;
9
+ }
10
+ /**
11
+ * Tokens grouped under the banner that precedes them, in file order.
12
+ * `category` is null for declarations that sit above the first banner —
13
+ * a real state in a hand-edited file, and one the report must show rather
14
+ * than silently drop.
15
+ */
16
+ export interface TokenCategoryInventory {
17
+ category: string | null;
18
+ tokens: TokenInventoryEntry[];
19
+ }
20
+ /** One block that declares a given token, with the value it declares. */
21
+ export interface TokenBlockValue {
22
+ /**
23
+ * Selector trail of the declaring block, outermost first, joined with
24
+ * ` > ` — for example `:root` or
25
+ * `@media (prefers-color-scheme: dark) > :root`.
26
+ */
27
+ block: string;
28
+ value: string;
29
+ /** 1-based line number of the declaration. */
30
+ line: number;
31
+ }
32
+ /**
33
+ * Collects every base-`:root` token declaration grouped by the category
34
+ * banner above it, in document order.
35
+ *
36
+ * Only the base block is walked: the dark `@media` and `:root[variant]`
37
+ * companions mirror declarations rather than owning them, so listing them
38
+ * would report the same token two or three times under no category at all.
39
+ *
40
+ * @param lines - Tokens CSS split into lines
41
+ * @returns Categories in file order, each with its tokens in file order
42
+ */
43
+ export declare function collectTokenInventory(lines: string[]): TokenCategoryInventory[];
44
+ /**
45
+ * Every declaration of `tokenName`, in every block of the file, labelled by
46
+ * the selector trail of the block that declares it.
47
+ *
48
+ * Deliberately not restricted to the base `:root`: the question `token show`
49
+ * answers is "what does this token resolve to", and a token whose dark
50
+ * override differs from its base value is precisely the case worth seeing.
51
+ *
52
+ * @param lines - Tokens CSS split into lines
53
+ * @param tokenName - Custom-property name including the leading `--`
54
+ * @returns Declarations in file order
55
+ */
56
+ export declare function collectTokenBlockValues(lines: string[], tokenName: string): TokenBlockValue[];
@@ -0,0 +1,109 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Read-only inventory of the tokens CSS file: which categories exist, which
4
+ * tokens each declares, and what a single token resolves to in every theme
5
+ * block that mentions it.
6
+ *
7
+ * `token add` requires `--category` and, until now, nothing reported the
8
+ * categories a project actually has — so the only way to name one correctly
9
+ * was to hand-parse a neighbouring `= Category =` banner out of the file.
10
+ * This module is that report. It deliberately reuses the banner and
11
+ * `:root`-bounds primitives from `token-category.ts` rather than parsing
12
+ * tokens CSS a second way: two readers that disagree on what a banner is
13
+ * would put `token list` and `token add` in different sections of the same
14
+ * file.
15
+ */
16
+ import { categoryBannerNameAt, findBaseRootBounds, maskCommentLines } from './token-category.js';
17
+ /** Matches a custom-property declaration and captures its name and value. */
18
+ const DECLARATION_PATTERN = /^\s*(--[A-Za-z0-9_-]+)\s*:\s*([^;]*);?/;
19
+ /**
20
+ * Collects every base-`:root` token declaration grouped by the category
21
+ * banner above it, in document order.
22
+ *
23
+ * Only the base block is walked: the dark `@media` and `:root[variant]`
24
+ * companions mirror declarations rather than owning them, so listing them
25
+ * would report the same token two or three times under no category at all.
26
+ *
27
+ * @param lines - Tokens CSS split into lines
28
+ * @returns Categories in file order, each with its tokens in file order
29
+ */
30
+ export function collectTokenInventory(lines) {
31
+ const bounds = findBaseRootBounds(lines);
32
+ if (bounds === undefined || bounds.close === -1)
33
+ return [];
34
+ const masked = maskCommentLines(lines);
35
+ const groups = [];
36
+ let current;
37
+ for (let i = bounds.open + 1; i < bounds.close; i++) {
38
+ // Banners are read from the RAW lines: `maskCommentLines` blanks comment
39
+ // bodies, which is exactly what a banner is.
40
+ const banner = categoryBannerNameAt(lines, i);
41
+ if (banner !== undefined) {
42
+ current = { category: banner, tokens: [] };
43
+ groups.push(current);
44
+ continue;
45
+ }
46
+ const match = DECLARATION_PATTERN.exec(masked[i] ?? '');
47
+ if (!match?.[1])
48
+ continue;
49
+ if (current === undefined) {
50
+ current = { category: null, tokens: [] };
51
+ groups.push(current);
52
+ }
53
+ current.tokens.push({ name: match[1], line: i + 1, value: (match[2] ?? '').trim() });
54
+ }
55
+ // A banner with no declarations under it is still a category `token add
56
+ // --category` accepts, so empty groups are kept.
57
+ return groups;
58
+ }
59
+ /**
60
+ * Every declaration of `tokenName`, in every block of the file, labelled by
61
+ * the selector trail of the block that declares it.
62
+ *
63
+ * Deliberately not restricted to the base `:root`: the question `token show`
64
+ * answers is "what does this token resolve to", and a token whose dark
65
+ * override differs from its base value is precisely the case worth seeing.
66
+ *
67
+ * @param lines - Tokens CSS split into lines
68
+ * @param tokenName - Custom-property name including the leading `--`
69
+ * @returns Declarations in file order
70
+ */
71
+ export function collectTokenBlockValues(lines, tokenName) {
72
+ const masked = maskCommentLines(lines);
73
+ const trail = [];
74
+ const found = [];
75
+ for (let i = 0; i < masked.length; i++) {
76
+ const line = masked[i] ?? '';
77
+ const match = DECLARATION_PATTERN.exec(line);
78
+ if (match?.[1] === tokenName) {
79
+ found.push({
80
+ block: trail.length > 0 ? trail.join(' > ') : '(file scope)',
81
+ value: (match[2] ?? '').trim(),
82
+ line: i + 1,
83
+ });
84
+ }
85
+ // Brace tracking after the declaration test, so a one-line block cannot
86
+ // swallow the declaration it opens on. Selector text is whatever
87
+ // precedes the `{`; an empty selector (a continuation line) still needs
88
+ // a stack entry or the closing `}` pops the wrong one.
89
+ for (const char of line) {
90
+ if (char === '{') {
91
+ trail.push(selectorBefore(line, trail.length));
92
+ }
93
+ else if (char === '}') {
94
+ trail.pop();
95
+ }
96
+ }
97
+ }
98
+ return found;
99
+ }
100
+ /**
101
+ * Selector text introducing the `depth`-th open brace on `line`. Falls back
102
+ * to a positional label so the trail stays well-formed on a minified or
103
+ * continuation line that opens a block with no visible selector.
104
+ */
105
+ function selectorBefore(line, depth) {
106
+ const open = line.indexOf('{');
107
+ const text = open === -1 ? '' : line.slice(0, open).trim();
108
+ return text.length > 0 ? text : `(block ${String(depth + 1)})`;
109
+ }
@@ -10,7 +10,7 @@ export declare const TOKEN_MODES: readonly ["auto", "static", "override"];
10
10
  /** Dark mode behaviour for a token. */
11
11
  export type TokenMode = (typeof TOKEN_MODES)[number];
12
12
  /** Narrows an arbitrary string to a {@link TokenMode}. */
13
- export declare const isTokenMode: (value: string) => value is "override" | "auto" | "static";
13
+ export declare const isTokenMode: (value: string) => value is "auto" | "override" | "static";
14
14
  /**
15
15
  * Options for adding a token.
16
16
  */
@@ -177,7 +177,7 @@ function evaluateBaseTokenIdempotency(lines, options, tokensCssPath) {
177
177
  ? 'remove the stray declaration first'
178
178
  : `pass --category "${existing.category}" to target the existing declaration, or remove it first`;
179
179
  throw new GeneralError(`Token "${options.tokenName}" is already declared outside category "${options.category}" ` +
180
- `(${tokensCssPath}:${String(existing.line)}${sectionSuffix}). ` +
180
+ `(${tokensCssPath}:${existing.line}${sectionSuffix}). ` +
181
181
  `A token belongs to exactly one category — ${remedy}.`);
182
182
  }
183
183
  /**