@hominis/fireforge 0.49.2 → 0.50.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 (334) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/README.md +1 -1
  3. package/dist/bin/fireforge.js +6 -0
  4. package/dist/build-info.json +4 -4
  5. package/dist/src/commands/build-prelock.d.ts +20 -0
  6. package/dist/src/commands/build-prelock.js +50 -0
  7. package/dist/src/commands/build.d.ts +3 -1
  8. package/dist/src/commands/build.js +13 -6
  9. package/dist/src/commands/check-report.d.ts +28 -0
  10. package/dist/src/commands/check-report.js +78 -0
  11. package/dist/src/commands/check-run.d.ts +28 -0
  12. package/dist/src/commands/check-run.js +104 -0
  13. package/dist/src/commands/check-steps.d.ts +67 -0
  14. package/dist/src/commands/check-steps.js +175 -0
  15. package/dist/src/commands/check.d.ts +32 -0
  16. package/dist/src/commands/check.js +160 -0
  17. package/dist/src/commands/config.js +5 -6
  18. package/dist/src/commands/doctor-engine-eslint.js +6 -1
  19. package/dist/src/commands/doctor-source-pin.js +4 -1
  20. package/dist/src/commands/export-adopt-check.d.ts +38 -0
  21. package/dist/src/commands/export-adopt-check.js +98 -0
  22. package/dist/src/commands/export-flow.js +29 -14
  23. package/dist/src/commands/export-placement-gate.d.ts +6 -2
  24. package/dist/src/commands/export-shared.d.ts +2 -1
  25. package/dist/src/commands/export-staged-registration.d.ts +46 -0
  26. package/dist/src/commands/export-staged-registration.js +91 -0
  27. package/dist/src/commands/forward-import-remedy.d.ts +32 -0
  28. package/dist/src/commands/forward-import-remedy.js +63 -0
  29. package/dist/src/commands/furnace/apply.js +3 -3
  30. package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
  31. package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
  32. package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
  33. package/dist/src/commands/furnace/chrome-doc.js +2 -2
  34. package/dist/src/commands/furnace/create.js +2 -2
  35. package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
  36. package/dist/src/commands/furnace/deploy-named.js +107 -0
  37. package/dist/src/commands/furnace/deploy.d.ts +3 -2
  38. package/dist/src/commands/furnace/deploy.js +50 -86
  39. package/dist/src/commands/furnace/diff.js +26 -3
  40. package/dist/src/commands/furnace/index.js +66 -41
  41. package/dist/src/commands/furnace/init.js +13 -6
  42. package/dist/src/commands/furnace/override.js +4 -2
  43. package/dist/src/commands/furnace/preview.js +4 -3
  44. package/dist/src/commands/furnace/refresh.js +2 -2
  45. package/dist/src/commands/furnace/remove.js +4 -3
  46. package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
  47. package/dist/src/commands/furnace/rename-dry-run.js +127 -0
  48. package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
  49. package/dist/src/commands/furnace/rename-helpers.js +15 -1
  50. package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
  51. package/dist/src/commands/furnace/rename-registrations.js +93 -0
  52. package/dist/src/commands/furnace/rename.d.ts +2 -1
  53. package/dist/src/commands/furnace/rename.js +28 -66
  54. package/dist/src/commands/furnace/scan.d.ts +9 -4
  55. package/dist/src/commands/furnace/scan.js +11 -9
  56. package/dist/src/commands/furnace/sync.js +3 -2
  57. package/dist/src/commands/furnace/validate-report.d.ts +45 -0
  58. package/dist/src/commands/furnace/validate-report.js +91 -0
  59. package/dist/src/commands/furnace/validate.d.ts +1 -1
  60. package/dist/src/commands/furnace/validate.js +127 -114
  61. package/dist/src/commands/furnace/validation-output.js +14 -3
  62. package/dist/src/commands/lint-per-patch.js +31 -60
  63. package/dist/src/commands/lint-report.d.ts +8 -4
  64. package/dist/src/commands/lint-report.js +15 -5
  65. package/dist/src/commands/lint.js +15 -34
  66. package/dist/src/commands/manifest.js +2 -0
  67. package/dist/src/commands/patch/delete-gap-notice.js +3 -1
  68. package/dist/src/commands/patch/delete.d.ts +1 -1
  69. package/dist/src/commands/patch/delete.js +13 -98
  70. package/dist/src/commands/patch/index.d.ts +1 -1
  71. package/dist/src/commands/patch/index.js +4 -2
  72. package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
  73. package/dist/src/commands/patch/lint-ignore.js +65 -28
  74. package/dist/src/commands/patch/move-files-create.js +26 -17
  75. package/dist/src/commands/patch/move-files-into.js +5 -3
  76. package/dist/src/commands/patch/move-files.js +3 -1
  77. package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
  78. package/dist/src/commands/patch/removal-dependents.js +133 -0
  79. package/dist/src/commands/patch/rename.js +7 -0
  80. package/dist/src/commands/patch/retire.d.ts +34 -0
  81. package/dist/src/commands/patch/retire.js +128 -0
  82. package/dist/src/commands/patch/split-plan.d.ts +7 -7
  83. package/dist/src/commands/patch/split-plan.js +45 -28
  84. package/dist/src/commands/patch/split-staged.d.ts +108 -0
  85. package/dist/src/commands/patch/split-staged.js +202 -0
  86. package/dist/src/commands/patch/split.d.ts +5 -0
  87. package/dist/src/commands/patch/split.js +51 -15
  88. package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
  89. package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
  90. package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
  91. package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
  92. package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
  93. package/dist/src/commands/patch/staged-dependency-from.js +108 -0
  94. package/dist/src/commands/patch/staged-dependency.js +14 -41
  95. package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
  96. package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
  97. package/dist/src/commands/re-export-adjacent.d.ts +45 -2
  98. package/dist/src/commands/re-export-adjacent.js +156 -37
  99. package/dist/src/commands/re-export-drift.d.ts +36 -8
  100. package/dist/src/commands/re-export-drift.js +73 -49
  101. package/dist/src/commands/re-export-files.js +11 -8
  102. package/dist/src/commands/re-export-lint.d.ts +2 -1
  103. package/dist/src/commands/re-export-lint.js +23 -10
  104. package/dist/src/commands/re-export-options.js +45 -4
  105. package/dist/src/commands/re-export-preflight.d.ts +23 -0
  106. package/dist/src/commands/re-export-preflight.js +160 -0
  107. package/dist/src/commands/re-export-register.js +42 -17
  108. package/dist/src/commands/re-export-removed.d.ts +12 -1
  109. package/dist/src/commands/re-export-removed.js +41 -11
  110. package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
  111. package/dist/src/commands/re-export-scan-owner.js +116 -0
  112. package/dist/src/commands/re-export-scan.d.ts +17 -9
  113. package/dist/src/commands/re-export-scan.js +56 -23
  114. package/dist/src/commands/re-export-single.js +22 -6
  115. package/dist/src/commands/re-export-summary.d.ts +28 -0
  116. package/dist/src/commands/re-export-summary.js +36 -0
  117. package/dist/src/commands/re-export.js +37 -24
  118. package/dist/src/commands/rebase/index.js +17 -17
  119. package/dist/src/commands/run.js +16 -2
  120. package/dist/src/commands/status-check.d.ts +55 -16
  121. package/dist/src/commands/status-check.js +92 -37
  122. package/dist/src/commands/status-json.d.ts +22 -9
  123. package/dist/src/commands/status-json.js +47 -32
  124. package/dist/src/commands/status-output.js +3 -1
  125. package/dist/src/commands/status-scope.d.ts +37 -0
  126. package/dist/src/commands/status-scope.js +54 -0
  127. package/dist/src/commands/status.js +66 -23
  128. package/dist/src/commands/test-browser-preflight.js +19 -3
  129. package/dist/src/commands/test-build-furnace.d.ts +21 -0
  130. package/dist/src/commands/test-build-furnace.js +48 -0
  131. package/dist/src/commands/test-build-phase.js +47 -41
  132. package/dist/src/commands/test-checkjs.js +7 -0
  133. package/dist/src/commands/test-diagnose.js +7 -0
  134. package/dist/src/commands/test-modes.d.ts +16 -1
  135. package/dist/src/commands/test-modes.js +24 -1
  136. package/dist/src/commands/test-multi-verdict.d.ts +61 -0
  137. package/dist/src/commands/test-multi-verdict.js +111 -0
  138. package/dist/src/commands/test-orchestrate.d.ts +43 -0
  139. package/dist/src/commands/test-orchestrate.js +222 -0
  140. package/dist/src/commands/test-perf-scope.d.ts +29 -0
  141. package/dist/src/commands/test-perf-scope.js +83 -0
  142. package/dist/src/commands/test-prelock.d.ts +51 -0
  143. package/dist/src/commands/test-prelock.js +177 -0
  144. package/dist/src/commands/test-register.js +54 -10
  145. package/dist/src/commands/test-run.js +4 -1
  146. package/dist/src/commands/test-stale-gate.js +57 -27
  147. package/dist/src/commands/test-verdict.d.ts +10 -1
  148. package/dist/src/commands/test-verdict.js +28 -1
  149. package/dist/src/commands/test.d.ts +6 -1
  150. package/dist/src/commands/test.js +66 -60
  151. package/dist/src/commands/token-lint.d.ts +14 -0
  152. package/dist/src/commands/token-lint.js +111 -0
  153. package/dist/src/commands/token.js +10 -0
  154. package/dist/src/commands/verify-scope.d.ts +28 -0
  155. package/dist/src/commands/verify-scope.js +67 -0
  156. package/dist/src/commands/verify.d.ts +10 -2
  157. package/dist/src/commands/verify.js +59 -35
  158. package/dist/src/core/build-baseline-types.d.ts +46 -0
  159. package/dist/src/core/build-baseline-validate.js +19 -0
  160. package/dist/src/core/build-baseline.d.ts +5 -1
  161. package/dist/src/core/build-baseline.js +3 -1
  162. package/dist/src/core/build-overwrite-guard.d.ts +50 -0
  163. package/dist/src/core/build-overwrite-guard.js +84 -4
  164. package/dist/src/core/build-prepare.d.ts +7 -0
  165. package/dist/src/core/build-prepare.js +14 -8
  166. package/dist/src/core/build-run-record.d.ts +74 -0
  167. package/dist/src/core/build-run-record.js +136 -0
  168. package/dist/src/core/checkjs-support-heads.d.ts +41 -0
  169. package/dist/src/core/checkjs-support-heads.js +104 -0
  170. package/dist/src/core/config-paths.d.ts +2 -2
  171. package/dist/src/core/config-paths.js +8 -0
  172. package/dist/src/core/config-validate-check.d.ts +8 -0
  173. package/dist/src/core/config-validate-check.js +143 -0
  174. package/dist/src/core/config-validate-file-size.d.ts +9 -6
  175. package/dist/src/core/config-validate-file-size.js +91 -2
  176. package/dist/src/core/config-validate-history.d.ts +11 -0
  177. package/dist/src/core/config-validate-history.js +38 -0
  178. package/dist/src/core/config-validate-patch-policy.js +100 -0
  179. package/dist/src/core/config-validate-test-toolchains.js +7 -0
  180. package/dist/src/core/config-validate-token-scales.d.ts +6 -0
  181. package/dist/src/core/config-validate-token-scales.js +50 -0
  182. package/dist/src/core/config-validate.js +14 -5
  183. package/dist/src/core/config.d.ts +5 -0
  184. package/dist/src/core/config.js +16 -0
  185. package/dist/src/core/coverage-dispatch.d.ts +24 -0
  186. package/dist/src/core/coverage-dispatch.js +44 -0
  187. package/dist/src/core/coverage-extend.d.ts +70 -45
  188. package/dist/src/core/coverage-extend.js +199 -144
  189. package/dist/src/core/coverage-records.d.ts +97 -0
  190. package/dist/src/core/coverage-records.js +350 -0
  191. package/dist/src/core/destructive.d.ts +7 -4
  192. package/dist/src/core/destructive.js +10 -10
  193. package/dist/src/core/engine-session-lock.d.ts +10 -1
  194. package/dist/src/core/engine-session-lock.js +28 -8
  195. package/dist/src/core/export-moved-code-shape.d.ts +49 -11
  196. package/dist/src/core/export-moved-code-shape.js +135 -44
  197. package/dist/src/core/file-lock-queue.d.ts +67 -0
  198. package/dist/src/core/file-lock-queue.js +221 -0
  199. package/dist/src/core/file-lock.d.ts +4 -25
  200. package/dist/src/core/file-lock.js +105 -138
  201. package/dist/src/core/furnace-apply-output.d.ts +10 -0
  202. package/dist/src/core/furnace-apply-output.js +31 -1
  203. package/dist/src/core/furnace-apply.js +12 -2
  204. package/dist/src/core/furnace-composed-source.d.ts +76 -0
  205. package/dist/src/core/furnace-composed-source.js +167 -0
  206. package/dist/src/core/furnace-config-validate-exceptions.d.ts +9 -0
  207. package/dist/src/core/furnace-config-validate-exceptions.js +71 -0
  208. package/dist/src/core/furnace-config-validate.js +8 -0
  209. package/dist/src/core/furnace-css-fragments.d.ts +10 -0
  210. package/dist/src/core/furnace-css-fragments.js +26 -0
  211. package/dist/src/core/furnace-operation.d.ts +3 -3
  212. package/dist/src/core/furnace-registration-remove.d.ts +22 -1
  213. package/dist/src/core/furnace-registration-remove.js +53 -17
  214. package/dist/src/core/furnace-registration-rename.d.ts +26 -0
  215. package/dist/src/core/furnace-registration-rename.js +77 -0
  216. package/dist/src/core/furnace-registration.d.ts +14 -4
  217. package/dist/src/core/furnace-registration.js +44 -10
  218. package/dist/src/core/furnace-staleness.d.ts +7 -0
  219. package/dist/src/core/furnace-staleness.js +34 -24
  220. package/dist/src/core/furnace-validate-accessibility.d.ts +4 -2
  221. package/dist/src/core/furnace-validate-accessibility.js +10 -1
  222. package/dist/src/core/furnace-validate-exceptions.d.ts +40 -0
  223. package/dist/src/core/furnace-validate-exceptions.js +43 -0
  224. package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
  225. package/dist/src/core/furnace-validate-icon-button.js +49 -0
  226. package/dist/src/core/furnace-validate.js +1 -1
  227. package/dist/src/core/harness-port-fallback.d.ts +60 -0
  228. package/dist/src/core/harness-port-fallback.js +96 -0
  229. package/dist/src/core/history-log-file.d.ts +61 -0
  230. package/dist/src/core/history-log-file.js +157 -0
  231. package/dist/src/core/history-log.d.ts +2 -1
  232. package/dist/src/core/history-log.js +2 -1
  233. package/dist/src/core/json-preserving-edit.d.ts +22 -0
  234. package/dist/src/core/json-preserving-edit.js +199 -0
  235. package/dist/src/core/mach.js +8 -1
  236. package/dist/src/core/negative-control-diff.d.ts +39 -0
  237. package/dist/src/core/negative-control-diff.js +136 -0
  238. package/dist/src/core/negative-control.d.ts +138 -0
  239. package/dist/src/core/negative-control.js +356 -0
  240. package/dist/src/core/objdir-install-manifests.d.ts +73 -0
  241. package/dist/src/core/objdir-install-manifests.js +226 -0
  242. package/dist/src/core/patch-export-update.d.ts +2 -0
  243. package/dist/src/core/patch-export-update.js +1 -0
  244. package/dist/src/core/patch-export.js +3 -4
  245. package/dist/src/core/patch-lint-checkjs-any-import.d.ts +37 -0
  246. package/dist/src/core/patch-lint-checkjs-any-import.js +61 -0
  247. package/dist/src/core/patch-lint-checkjs.js +8 -11
  248. package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
  249. package/dist/src/core/patch-lint-code-lines.js +105 -0
  250. package/dist/src/core/patch-lint-cross.js +12 -4
  251. package/dist/src/core/patch-lint-css.d.ts +8 -2
  252. package/dist/src/core/patch-lint-css.js +10 -4
  253. package/dist/src/core/patch-lint-file-size.d.ts +12 -5
  254. package/dist/src/core/patch-lint-file-size.js +22 -11
  255. package/dist/src/core/patch-lint-forward-registration-extended.d.ts +18 -0
  256. package/dist/src/core/patch-lint-forward-registration-extended.js +35 -10
  257. package/dist/src/core/patch-lint-forward-registration.js +10 -0
  258. package/dist/src/core/patch-lint-ignore.d.ts +152 -0
  259. package/dist/src/core/patch-lint-ignore.js +299 -0
  260. package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
  261. package/dist/src/core/patch-lint-size-thresholds.js +110 -0
  262. package/dist/src/core/patch-lint-size.d.ts +117 -0
  263. package/dist/src/core/patch-lint-size.js +323 -0
  264. package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
  265. package/dist/src/core/patch-lint-staged-registration.js +20 -0
  266. package/dist/src/core/patch-lint-token-scale.d.ts +39 -0
  267. package/dist/src/core/patch-lint-token-scale.js +109 -0
  268. package/dist/src/core/patch-lint.d.ts +11 -83
  269. package/dist/src/core/patch-lint.js +64 -286
  270. package/dist/src/core/patch-lock.js +7 -6
  271. package/dist/src/core/patch-manifest-consistency.js +7 -6
  272. package/dist/src/core/patch-manifest-io.d.ts +6 -3
  273. package/dist/src/core/patch-manifest-io.js +19 -8
  274. package/dist/src/core/patch-manifest-validate.js +34 -2
  275. package/dist/src/core/patch-order-legal.d.ts +25 -0
  276. package/dist/src/core/patch-order-legal.js +83 -0
  277. package/dist/src/core/patch-policy-content.d.ts +40 -0
  278. package/dist/src/core/patch-policy-content.js +216 -0
  279. package/dist/src/core/patch-policy.d.ts +16 -3
  280. package/dist/src/core/patch-policy.js +16 -9
  281. package/dist/src/core/patch-retired.d.ts +35 -0
  282. package/dist/src/core/patch-retired.js +54 -0
  283. package/dist/src/core/patch-subset.d.ts +16 -0
  284. package/dist/src/core/patch-subset.js +66 -0
  285. package/dist/src/core/preflight-wait.d.ts +20 -0
  286. package/dist/src/core/preflight-wait.js +38 -0
  287. package/dist/src/core/rebase-from-version.d.ts +46 -0
  288. package/dist/src/core/rebase-from-version.js +71 -0
  289. package/dist/src/core/run-log.d.ts +20 -0
  290. package/dist/src/core/run-log.js +89 -3
  291. package/dist/src/core/run-profile.d.ts +7 -0
  292. package/dist/src/core/run-profile.js +7 -1
  293. package/dist/src/core/smoke-patterns.d.ts +5 -0
  294. package/dist/src/core/smoke-patterns.js +13 -0
  295. package/dist/src/core/test-harness-crash.d.ts +18 -1
  296. package/dist/src/core/test-harness-crash.js +6 -4
  297. package/dist/src/core/test-harness-signature.d.ts +17 -0
  298. package/dist/src/core/test-harness-signature.js +4 -0
  299. package/dist/src/core/test-run-completeness.d.ts +57 -0
  300. package/dist/src/core/test-run-completeness.js +144 -0
  301. package/dist/src/core/test-stale-check.d.ts +27 -1
  302. package/dist/src/core/test-stale-check.js +39 -49
  303. package/dist/src/core/test-stale-exempt.d.ts +40 -0
  304. package/dist/src/core/test-stale-exempt.js +70 -0
  305. package/dist/src/core/token-lint.d.ts +50 -0
  306. package/dist/src/core/token-lint.js +126 -0
  307. package/dist/src/core/tree-guard.js +3 -0
  308. package/dist/src/core/typecheck-shim.d.ts +1 -1
  309. package/dist/src/core/typecheck-shim.js +90 -2
  310. package/dist/src/index.d.ts +8 -3
  311. package/dist/src/index.js +3 -1
  312. package/dist/src/types/commands/index.d.ts +2 -2
  313. package/dist/src/types/commands/options.d.ts +148 -21
  314. package/dist/src/types/commands/patches.d.ts +75 -1
  315. package/dist/src/types/config.d.ts +192 -0
  316. package/dist/src/types/config.js +9 -0
  317. package/dist/src/types/furnace.d.ts +43 -1
  318. package/dist/src/types/furnace.js +7 -0
  319. package/dist/src/types/index.d.ts +1 -1
  320. package/dist/src/utils/options.d.ts +34 -6
  321. package/dist/src/utils/options.js +86 -5
  322. package/dist/src/utils/path-glob.d.ts +15 -0
  323. package/dist/src/utils/path-glob.js +47 -0
  324. package/dist/src/utils/regex.d.ts +15 -3
  325. package/dist/src/utils/regex.js +37 -16
  326. package/docs/configuration.md +282 -9
  327. package/docs/exit-codes.md +18 -1
  328. package/docs/furnace.md +97 -3
  329. package/docs/lifecycle-invariants.md +52 -19
  330. package/docs/machine-output.md +74 -12
  331. package/docs/patch-workflow.md +297 -22
  332. package/docs/run-logs.md +20 -1
  333. package/docs/testing.md +276 -20
  334. package/package.json +1 -1
@@ -0,0 +1,126 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * `fireforge token lint`: structural checks over the design-token system.
4
+ *
5
+ * - `token-lockstep`: a themed override block (the dark `@media` mirror, a
6
+ * `:root[variant]` block) declares only tokens the base `:root` declares.
7
+ * An override with no base is a token that exists only in one theme, so
8
+ * every other theme resolves it to nothing.
9
+ * - `token-undeclared`: a `var(--<prefix>…)` reference in the tokens CSS or
10
+ * in a CSS file the patch queue touches names a token no block of the
11
+ * tokens CSS declares. Same-file declarations, `furnace.json`
12
+ * `tokenAllowlist` and `runtimeVariables` are exempt, matching the
13
+ * `token-prefix-violation` rule's notion of a runtime channel.
14
+ *
15
+ * Built on `token-category.ts`'s base-block and comment-mask primitives, so
16
+ * this report and `token add` agree on which block is the base.
17
+ */
18
+ import { findBaseRootBounds, maskCommentLines } from './token-category.js';
19
+ const DECLARATION = /(?:^|[{;\s])(--[A-Za-z0-9_-]+)\s*:/g;
20
+ function selectorBefore(text) {
21
+ const selector = text.slice(Math.max(text.lastIndexOf('}'), text.lastIndexOf(';')) + 1).trim();
22
+ return selector.length > 0 ? selector.replace(/\s+/g, ' ') : '(block)';
23
+ }
24
+ /**
25
+ * Every custom-property declaration in a CSS file with the selector trail
26
+ * of the block holding it, outermost first, joined with ` > `.
27
+ */
28
+ function collectDeclarations(lines) {
29
+ const masked = maskCommentLines(lines);
30
+ const trail = [];
31
+ const found = [];
32
+ let pending = '';
33
+ for (const [index, line] of masked.entries()) {
34
+ for (const match of line.matchAll(DECLARATION)) {
35
+ found.push({
36
+ name: match[1] ?? '',
37
+ line: index + 1,
38
+ block: trail.length > 0 ? trail.join(' > ') : '(file scope)',
39
+ });
40
+ }
41
+ for (const char of line) {
42
+ if (char === '{') {
43
+ trail.push(selectorBefore(pending));
44
+ pending = '';
45
+ }
46
+ else if (char === '}') {
47
+ trail.pop();
48
+ pending = '';
49
+ }
50
+ else {
51
+ pending += char;
52
+ }
53
+ }
54
+ pending += '\n';
55
+ }
56
+ return found;
57
+ }
58
+ /** Every custom property the tokens CSS declares, in any block. */
59
+ export function collectDeclaredTokenNames(lines) {
60
+ return new Set(collectDeclarations(lines).map((declaration) => declaration.name));
61
+ }
62
+ /**
63
+ * Themed override declarations of `tokenPrefix` tokens that the base
64
+ * `:root` block does not declare.
65
+ *
66
+ * @param lines - Tokens CSS split into lines
67
+ * @param tokensCssPath - Engine-relative path, for the findings
68
+ * @param tokenPrefix - Only tokens with this prefix are held to lockstep;
69
+ * a themed block may still override platform properties
70
+ */
71
+ export function lintTokenLockstep(lines, tokensCssPath, tokenPrefix) {
72
+ const bounds = findBaseRootBounds(lines);
73
+ if (bounds === undefined)
74
+ return [];
75
+ const close = bounds.close === -1 ? lines.length : bounds.close;
76
+ const declarations = collectDeclarations(lines);
77
+ const inBase = (line) => line - 1 >= bounds.open && line - 1 <= close;
78
+ const base = new Set(declarations.filter((d) => inBase(d.line)).map((d) => d.name));
79
+ return declarations
80
+ .filter((d) => !inBase(d.line) && d.name.startsWith(tokenPrefix) && !base.has(d.name))
81
+ .map((d) => ({
82
+ check: 'token-lockstep',
83
+ file: tokensCssPath,
84
+ line: d.line,
85
+ token: d.name,
86
+ message: `${d.name} is declared in ${d.block} but not in the base :root block, so every other ` +
87
+ 'theme resolves it to nothing. Declare the base value first (fireforge token add), ' +
88
+ 'or drop the override.',
89
+ }));
90
+ }
91
+ /**
92
+ * `var(--<prefix>…)` references naming a token the tokens CSS never
93
+ * declares.
94
+ *
95
+ * @param file - Engine-relative path, for the findings
96
+ * @param css - The file's content
97
+ * @param tokenPrefix - Only references with this prefix are checked
98
+ * @param declared - {@link collectDeclaredTokenNames} of the tokens CSS
99
+ * @param exempt - `tokenAllowlist` and `runtimeVariables` names
100
+ */
101
+ export function lintUndeclaredTokenReferences(file, css, tokenPrefix, declared, exempt) {
102
+ const lines = css.split('\n');
103
+ const local = collectDeclaredTokenNames(lines);
104
+ const findings = [];
105
+ const seen = new Set();
106
+ for (const [index, line] of maskCommentLines(lines).entries()) {
107
+ for (const match of line.matchAll(/var\(\s*(--[A-Za-z0-9_-]+)/g)) {
108
+ const name = match[1] ?? '';
109
+ if (!name.startsWith(tokenPrefix) || declared.has(name) || local.has(name))
110
+ continue;
111
+ if (exempt.has(name) || seen.has(name))
112
+ continue;
113
+ seen.add(name);
114
+ findings.push({
115
+ check: 'token-undeclared',
116
+ file,
117
+ line: index + 1,
118
+ token: name,
119
+ message: `var(${name}) names a token the tokens CSS does not declare, so it resolves to ` +
120
+ 'nothing. Add the token, fix the name, or list a runtime channel in furnace.json ' +
121
+ 'runtimeVariables.',
122
+ });
123
+ }
124
+ }
125
+ return findings;
126
+ }
@@ -51,6 +51,9 @@ export const TREE_COMMAND_VERDICTS = {
51
51
  lint: 'allowed',
52
52
  typecheck: 'allowed',
53
53
  verify: 'allowed',
54
+ // Its furnace-validate and token-coverage steps are commands a tree
55
+ // refuses. Run the allowed gates one by one in a tree.
56
+ check: 'refused',
54
57
  furnace: 'refused',
55
58
  // No nesting. `tree list` inside a tree is pointless but harmless, and
56
59
  // the subcommand check below allows only `list`.
@@ -24,7 +24,7 @@ export declare const SHIM_FILENAME = "__fireforge_firefox_globals.d.ts";
24
24
  * `declare var` redeclarations to the first declaration, the typed
25
25
  * consumer declaration wins over the loose fallback here.
26
26
  */
27
- export declare const TEST_HARNESS_SHIM = "\n// \u2500\u2500 FireForge test-harness shim (loose baseline) \u2500\u2500\ndeclare var TestUtils: any;\ndeclare var BrowserTestUtils: any;\ndeclare var SpecialPowers: any;\ndeclare var EventUtils: any;\ndeclare var SimpleTest: any;\ndeclare var Assert: any;\ndeclare var gTestPath: string;\ndeclare var content: any;\ndeclare function add_task(task: (...args: any[]) => any): any;\ndeclare function add_setup(task: (...args: any[]) => any): any;\ndeclare function ok(condition: any, message?: string): void;\ndeclare function is(actual: any, expected: any, message?: string): void;\ndeclare function isnot(actual: any, expected: any, message?: string): void;\ndeclare function todo(condition: any, message?: string): void;\ndeclare function todo_is(actual: any, expected: any, message?: string): void;\ndeclare function info(message: string): void;\ndeclare function registerCleanupFunction(callback: (...args: any[]) => any): void;\ndeclare function waitForExplicitFinish(): void;\ndeclare function finish(): void;\ndeclare function do_get_profile(): any;\ndeclare function run_test(): any;\n\n";
27
+ export declare const TEST_HARNESS_SHIM = "\n// \u2500\u2500 FireForge test-harness shim (loose baseline) \u2500\u2500\ndeclare var TestUtils: any;\ndeclare var BrowserTestUtils: any;\ndeclare var SpecialPowers: any;\ndeclare var EventUtils: any;\ndeclare var SimpleTest: any;\ndeclare var Assert: any;\ndeclare var gTestPath: string;\ndeclare var content: any;\ndeclare function add_task(task: (...args: any[]) => any): any;\ndeclare function add_setup(task: (...args: any[]) => any): any;\ndeclare function ok(condition: any, message?: string): void;\ndeclare function is(actual: any, expected: any, message?: string): void;\ndeclare function isnot(actual: any, expected: any, message?: string): void;\ndeclare function todo(condition: any, message?: string): void;\ndeclare function todo_is(actual: any, expected: any, message?: string): void;\ndeclare function todo_isnot(actual: any, expected: any, message?: string): void;\ndeclare function info(message: string): void;\ndeclare function registerCleanupFunction(callback: (...args: any[]) => any): void;\ndeclare function waitForExplicitFinish(): void;\ndeclare function finish(): void;\ndeclare function do_get_profile(): any;\ndeclare function run_test(): any;\n\n";
28
28
  /**
29
29
  * TS diagnostic codes suppressed by both the patch-lint checkJs pass
30
30
  * and the whole-project typecheck command. Each is a known false
@@ -43,7 +43,9 @@ export const SHIM_FILENAME = '__fireforge_firefox_globals.d.ts';
43
43
  * - `const lazy = {};` + `ChromeUtils.defineESModuleGetters(lazy, { ... })`
44
44
  * populates `lazy` at runtime, so we declare it as `Record<string, any>`.
45
45
  * - `Services.obs`, `Services.prefs`, etc. are XPCOM service accessors.
46
- * - `Ci`, `Cc`, `Cr`, `Cu` are XPCOM component shortcuts.
46
+ * - `Ci`, `Cc`, `Cr`, `Cu` are XPCOM component shortcuts. `Cu` alone is
47
+ * typed (`CuShim`, the members of `nsIXPCComponents_Utils`), so an
48
+ * unknown `Cu.*` member is an error rather than a runtime TypeError.
47
49
  * - Browser chrome globals like `gBrowser`, `gURLBar` are common in
48
50
  * content scripts wired via `browser.js`.
49
51
  * - Dynamic `import("resource:-…")` / `import("chrome:-…")` under patch
@@ -72,7 +74,92 @@ interface ChromeUtilsShim {
72
74
  predictRemoteTypeForURI(uri: string | object | null, options?: object): string | null;
73
75
  }
74
76
  declare var ChromeUtils: ChromeUtilsShim;
75
- declare var Cu: any;
77
+ // Components.utils (js/xpconnect/idl/xpccomponents.idl, nsIXPCComponents_Utils,
78
+ // Firefox 153). Every member is listed with loose (any) jsval parameters and
79
+ // results, so Xray-waived and cloned objects keep their expandos, but the
80
+ // member set is closed: a call to a member Cu does not have (Cu.now() is
81
+ // ChromeUtils.now()) is a typecheck error instead of a runtime TypeError.
82
+ // Extensible via interface merging, same as ChromeUtilsShim.
83
+ interface CuShim {
84
+ QueryInterface(iid: any): any;
85
+ printStderr(message: string): void;
86
+ reportError(error: any, stack?: any): void;
87
+ readonly Sandbox: { new (principal: any, options?: any): any; (principal: any, options?: any): any };
88
+ evalInSandbox(
89
+ source: string,
90
+ sandbox: any,
91
+ version?: any,
92
+ filename?: string,
93
+ lineNo?: number,
94
+ enforceFilenameRestrictions?: boolean
95
+ ): any;
96
+ getUAWidgetScope(principal: any): any;
97
+ getSandboxMetadata(sandbox: any): any;
98
+ setSandboxMetadata(sandbox: any, metadata: any): void;
99
+ setSandboxLocaleOverride(sandbox: any, locale: string): void;
100
+ setSandboxTimezoneOverride(sandbox: any, timezone: string): void;
101
+ isESModuleLoaded(resourceURI: string): boolean;
102
+ importGlobalProperties(propertyList: any): void;
103
+ getWeakReference(obj: any): { get(): any };
104
+ forceGC(): void;
105
+ forceCC(listener?: any): void;
106
+ createCCLogger(): any;
107
+ finishCC(): void;
108
+ ccSlice(budget: number): void;
109
+ getMaxCCSliceTimeSinceClear(): number;
110
+ clearMaxCCTime(): void;
111
+ forceShrinkingGC(): void;
112
+ schedulePreciseGC(callback: any): void;
113
+ schedulePreciseShrinkingGC(callback: any): void;
114
+ unlinkGhostWindows(): void;
115
+ intentionallyLeak(): void;
116
+ getJSTestingFunctions(): any;
117
+ getFunctionSourceLocation(func: any): any;
118
+ callFunctionWithAsyncStack(func: any, stack: any, asyncCause: string): any;
119
+ getGlobalForObject(obj: any): any;
120
+ isProxy(obj: any): boolean;
121
+ exportFunction(func: any, scope: any, options?: any): any;
122
+ createObjectIn(scope: any, options?: any): any;
123
+ makeObjectPropsNormal(obj: any): void;
124
+ isDeadWrapper(obj: any): boolean;
125
+ isRemoteProxy(val: any): boolean;
126
+ recomputeWrappers(obj?: any): void;
127
+ setWantXrays(scope: any): void;
128
+ dispatch(runnable: any, scope?: any): void;
129
+ readonly isInAutomation: boolean;
130
+ exitIfInAutomation(): void;
131
+ crashIfNotInAutomation(): void;
132
+ setGCZeal(zeal: number): void;
133
+ nukeSandbox(obj: any): void;
134
+ blockScriptForGlobal(global: any): void;
135
+ unblockScriptForGlobal(global: any): void;
136
+ isOpaqueWrapper(obj: any): boolean;
137
+ isXrayWrapper(obj: any): boolean;
138
+ waiveXrays(val: any): any;
139
+ unwaiveXrays(val: any): any;
140
+ getClassName(obj: any, unwrap: boolean): string;
141
+ getIncumbentGlobal(callback?: any): any;
142
+ getDebugName(obj: any): string;
143
+ getWatchdogTimestamp(category: string): number;
144
+ getJSEngineTelemetryValue(): any;
145
+ cloneInto(value: any, scope: any, options?: any): any;
146
+ getWebIDLCallerPrincipal(): any;
147
+ getObjectPrincipal(obj: any): any;
148
+ getRealmLocation(obj: any): string;
149
+ readUTF8File(file: any): string;
150
+ readUTF8URI(url: any): string;
151
+ createSpellChecker(): any;
152
+ createCommandLine(args: string[], workingDir: any, state: number): any;
153
+ createCommandParams(): any;
154
+ createLoadContext(): any;
155
+ createPrivateLoadContext(): any;
156
+ createPersistentProperties(): any;
157
+ createDocumentEncoder(contentType: string): any;
158
+ createHTMLCopyEncoder(): any;
159
+ readonly loadedESModules: string[];
160
+ getModuleImportStack(location: string): string;
161
+ }
162
+ declare var Cu: CuShim;
76
163
  declare var Ci: any;
77
164
  declare var Cc: any;
78
165
  declare var Cr: any;
@@ -159,6 +246,7 @@ declare function is(actual: any, expected: any, message?: string): void;
159
246
  declare function isnot(actual: any, expected: any, message?: string): void;
160
247
  declare function todo(condition: any, message?: string): void;
161
248
  declare function todo_is(actual: any, expected: any, message?: string): void;
249
+ declare function todo_isnot(actual: any, expected: any, message?: string): void;
162
250
  declare function info(message: string): void;
163
251
  declare function registerCleanupFunction(callback: (...args: any[]) => any): void;
164
252
  declare function waitForExplicitFinish(): void;
@@ -22,10 +22,15 @@ export type { ApplyAllComponentsResult } from './core/furnace-apply.js';
22
22
  export { applyAllComponents } from './core/furnace-apply.js';
23
23
  export { ensureFurnaceConfig, loadFurnaceConfig, loadFurnaceState, saveFurnaceState, validateFurnaceConfig, } from './core/furnace-config.js';
24
24
  export { validateAllComponents, validateComponent } from './core/furnace-validate.js';
25
- export type { PatchSizeTierDecision } from './core/patch-lint.js';
26
- export { countNonBinaryDiffLines, filesMeasuredByFileTooLarge, getPatchSizeThresholds, resolvePatchSizeTier, } from './core/patch-lint.js';
25
+ export type { PatchSizeThresholds, PatchSizeTierDecision } from './core/patch-lint.js';
26
+ export { countNonBinaryDiffLines, filesMeasuredByFileTooLarge, getPatchSizeThresholds, isTestFile, resolvePatchSizeTier, } from './core/patch-lint.js';
27
+ export type { CodeLineCount } from './core/patch-lint-code-lines.js';
28
+ export { countCodeLines } from './core/patch-lint-code-lines.js';
29
+ export type { ResolvedFileSizeTier } from './core/patch-lint-file-size.js';
30
+ export { resolveFileSizeThresholds } from './core/patch-lint-file-size.js';
31
+ export type { ResolvedPatchFilesTier } from './core/patch-lint-size-thresholds.js';
27
32
  export type { AddTokenOptions, AddTokenResult, TokenMode } from './core/token-manager.js';
28
33
  export { addToken, getTokensCssPath, validateTokenAdd } from './core/token-manager.js';
29
34
  export { CancellationError, CommandError, FireForgeError, GeneralError, InvalidArgumentError, ResolutionError, } from './errors/base.js';
30
35
  export { ExitCode } from './errors/codes.js';
31
- export type { ApplyResult, BuildConfig, ComponentType, CustomComponentConfig, DryRunAction, FireForgeConfig, FirefoxConfig, FirefoxProduct, FurnaceConfig, FurnaceState, OverrideComponentConfig, OverrideType, ProjectLicense, StepError, ValidationIssue, WireConfig, } from './types/index.js';
36
+ export type { ApplyResult, BuildConfig, ComponentType, CustomComponentConfig, DryRunAction, FireForgeConfig, FirefoxConfig, FirefoxProduct, FurnaceConfig, FurnaceState, OverrideComponentConfig, OverrideType, PatchLintFileSizeThresholds, PatchLintFileSizeTier, ProjectLicense, StepError, ValidationIssue, WireConfig, } from './types/index.js';
package/dist/src/index.js CHANGED
@@ -21,7 +21,9 @@ export { findForwardRegistrations } from './core/forward-registrations.js';
21
21
  export { applyAllComponents } from './core/furnace-apply.js';
22
22
  export { ensureFurnaceConfig, loadFurnaceConfig, loadFurnaceState, saveFurnaceState, validateFurnaceConfig, } from './core/furnace-config.js';
23
23
  export { validateAllComponents, validateComponent } from './core/furnace-validate.js';
24
- export { countNonBinaryDiffLines, filesMeasuredByFileTooLarge, getPatchSizeThresholds, resolvePatchSizeTier, } from './core/patch-lint.js';
24
+ export { countNonBinaryDiffLines, filesMeasuredByFileTooLarge, getPatchSizeThresholds, isTestFile, resolvePatchSizeTier, } from './core/patch-lint.js';
25
+ export { countCodeLines } from './core/patch-lint-code-lines.js';
26
+ export { resolveFileSizeThresholds } from './core/patch-lint-file-size.js';
25
27
  export { addToken, getTokensCssPath, validateTokenAdd } from './core/token-manager.js';
26
28
  export { CancellationError, CommandError, FireForgeError, GeneralError, InvalidArgumentError, ResolutionError, } from './errors/base.js';
27
29
  export { ExitCode } from './errors/codes.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Re-exports all command-related types from focused sub-modules.
3
3
  */
4
- export type { BuildOptions, DiscardOptions, DoctorOptions, DownloadOptions, ExportOptions, FurnaceApplyOptions, FurnaceCreateOptions, FurnaceDeployOptions, FurnaceOverrideOptions, FurnacePreviewOptions, FurnaceRefreshOptions, FurnaceRemoveOptions, FurnaceSyncOptions, FurnaceValidateOptions, ImportOptions, LintCommandOptions, PackageOptions, PatchCompactOptions, PatchDeleteOptions, PatchLintIgnoreOptions, PatchMoveFilesOptions, PatchRenameOptions, PatchReorderOptions, PatchSplitOptions, PatchStagedDependencyOptions, PatchTierOptions, RebaseOptions, ReExportOptions, RegisterOptions, ResetOptions, RunOptions, SetupOptions, SourceSetOptions, StatusOptions, TestOptions, TokenAddOptions, WireOptions, } from './options.js';
5
- export type { ImportSummary, PatchCategory, PatchesManifest, PatchInfo, PatchLintIssue, PatchMetadata, PatchResult, PatchStagedDependencies, PatchStagedForwardImport, PatchStagedRegistration, } from './patches.js';
4
+ export type { BuildOptions, DiscardOptions, DoctorOptions, DownloadOptions, ExportOptions, FurnaceApplyOptions, FurnaceCreateOptions, FurnaceDeployOptions, FurnaceLockWaitable, FurnaceOverrideOptions, FurnacePreviewOptions, FurnaceRefreshOptions, FurnaceRemoveOptions, FurnaceRenameOptions, FurnaceSyncOptions, FurnaceValidateOptions, ImportOptions, LintCommandOptions, PackageOptions, PatchCompactOptions, PatchDeleteOptions, PatchLintIgnoreOptions, PatchMoveFilesOptions, PatchRenameOptions, PatchReorderOptions, PatchRetireOptions, PatchSplitOptions, PatchStagedDependencyOptions, PatchTierOptions, RebaseOptions, ReExportOptions, RegisterOptions, ResetOptions, RunOptions, SetupOptions, SourceSetOptions, StatusOptions, TestOptions, TokenAddOptions, WireOptions, } from './options.js';
5
+ export type { ImportSummary, LintIgnoreCeiling, LintIgnoreEntry, PatchCategory, PatchesManifest, PatchInfo, PatchLintIssue, PatchLintStagedDeclaration, PatchMetadata, PatchResult, PatchRetiredSlot, PatchStagedDependencies, PatchStagedForwardImport, PatchStagedRegistration, } from './patches.js';
6
6
  export type { DoctorCheck, TokenCoverageFileEntry, TokenCoverageReport } from './project.js';
@@ -111,6 +111,8 @@ export interface SourceSetOptions {
111
111
  export interface BuildOptions extends EngineLockWaitable {
112
112
  /** Wait for the objdir browser before deploying/building. */
113
113
  waitBrowser?: number | boolean;
114
+ /** Also write the run log here, outside `.fireforge/logs/` retention. */
115
+ keepLog?: string;
114
116
  /** Fast UI-only rebuild */
115
117
  ui?: boolean;
116
118
  /** Number of parallel jobs */
@@ -263,7 +265,9 @@ export interface ReExportOptions extends EngineLockWaitable {
263
265
  /**
264
266
  * Explicit engine-relative files to add while scanning. Unlike broad
265
267
  * `--scan`, this does not collect adjacent files from the same directory.
266
- * Requires `--scan` and exactly one target patch.
268
+ * Requires `--scan`. A `<patch>:<path>` entry names its owning patch, which
269
+ * a call with several target patches requires; selected patches without
270
+ * an entry then refresh scan-less.
267
271
  */
268
272
  scanFiles?: string[];
269
273
  /**
@@ -343,11 +347,13 @@ export interface ReExportOptions extends EngineLockWaitable {
343
347
  noCache?: boolean;
344
348
  /**
345
349
  * Refuse (non-zero exit, patch not written) a re-export whose refreshed
346
- * body would absorb engine lines not present in the old patch body.
350
+ * body would add or drop lines, in a file the patch already owns, that
351
+ * the old patch body did not carry.
347
352
  * This is the multi-session guard against silently capturing another
348
353
  * session's uncommitted edits in files the patch already owns. The drift preview
349
- * always prints. This flag turns it into a hard stop. Mutually
350
- * exclusive with `--scan` and `--files`.
354
+ * always prints. This flag turns it into a hard stop. Beside `--scan` it
355
+ * guards the files each patch already owns (adopted files count as
356
+ * expected). Mutually exclusive with `--files`.
351
357
  */
352
358
  refuseForeignDrift?: boolean;
353
359
  /**
@@ -370,7 +376,26 @@ export interface ReExportOptions extends EngineLockWaitable {
370
376
  * rather than hidden.
371
377
  */
372
378
  expectUnmanaged?: string[];
373
- /** Explicitly retire named absent patch-created files in a scan-less export. */
379
+ /**
380
+ * Engine-relative files this session created and will adopt with its next
381
+ * export (`--expect-new`, repeatable). Requires
382
+ * `--refuse-adjacent-unmanaged`. Lets the owners of a new file's
383
+ * registration re-export first while a peer's unmanaged neighbour still
384
+ * refuses. The path must be changed in engine/ and owned by no patch.
385
+ */
386
+ expectNew?: string[];
387
+ /**
388
+ * Reviewed ignore list for `--refuse-adjacent-unmanaged`
389
+ * (`--adjacent-ignore`, repeatable): an engine-relative path, or a glob
390
+ * whose `*` stays within one segment (`**` is refused). Matches are
391
+ * reported, never refused.
392
+ */
393
+ adjacentIgnore?: string[];
394
+ /**
395
+ * Explicitly retire named files in a scan-less export: an absent file the
396
+ * patch created, or a patch-modified upstream file whose engine copy is
397
+ * back at HEAD.
398
+ */
374
399
  expectRemoved?: string[];
375
400
  }
376
401
  /**
@@ -396,6 +421,13 @@ export interface PatchLintIgnoreOptions extends Confirmable, DryRunnable, PatchL
396
421
  remove?: string[];
397
422
  /** Drop the `lintIgnore` field entirely. */
398
423
  clear?: boolean;
424
+ /**
425
+ * With `add`: write `{ rule, max, reason }` ceiling entries instead of
426
+ * bare IDs. Only the measured size rules accept one. Requires `reason`.
427
+ */
428
+ max?: number;
429
+ /** Why the ceiling exists. Required with `max`. */
430
+ reason?: string;
399
431
  }
400
432
  /**
401
433
  * Options for the `fireforge patch staged-dependency` subcommand. Modes are
@@ -430,6 +462,11 @@ export interface PatchStagedDependencyOptions extends Confirmable, DryRunnable,
430
462
  owner?: string;
431
463
  /** Optional human-readable rationale stored with the declaration. */
432
464
  reason?: string;
465
+ /**
466
+ * JSON file of declarations to add in one call (`--add` only): a
467
+ * `lint --per-patch --report` document or a plain array of declarations.
468
+ */
469
+ from?: string;
433
470
  }
434
471
  /**
435
472
  * Options for the `patch split` command.
@@ -510,6 +547,16 @@ export interface RunOptions {
510
547
  * Implied by `smokeExit` unless `profile` is given.
511
548
  */
512
549
  tempProfile?: boolean;
550
+ /**
551
+ * Pass `-purgecaches` with a named `profile`, so Firefox discards the
552
+ * profile's startup cache (compiled chrome scripts) before it runs. A UI
553
+ * build keeps the build id the cache is keyed on, so a kept profile would
554
+ * otherwise run the chrome scripts an earlier build cached. Default
555
+ * `true`; `--no-purge-caches` sets `false`. Fresh temporary profiles and
556
+ * mach's own profile-default (cleaned before every run) have no cache to
557
+ * purge.
558
+ */
559
+ purgeCaches?: boolean;
513
560
  /**
514
561
  * Enable smoke-run mode. Launches the browser, streams the console,
515
562
  * sends SIGTERM to the whole process group after `smokeExit` seconds,
@@ -520,7 +567,9 @@ export interface RunOptions {
520
567
  * - `ExitCode.SMOKE_LAUNCH_FAILURE` (13): the browser exited with a
521
568
  * non-clean status before the smoke window elapsed (a launch-side
522
569
  * failure we cannot observe as a console line, such as a crash
523
- * before console wiring, or a missing profile).
570
+ * before console wiring, or a missing profile), or exited before the
571
+ * window with no browser console output at all (a launch that never
572
+ * reached a window, such as a profile in a startup-crash streak).
524
573
  *
525
574
  * POSIX only (process-group semantics do not map cleanly onto Windows).
526
575
  * `runSmokeExit` rejects the flag up front on `win32`.
@@ -559,6 +608,10 @@ export interface TestOptions extends EngineLockWaitable {
559
608
  waitBrowser?: number | boolean;
560
609
  /** Queue behind the mochitest/Marionette port holder. */
561
610
  waitPort?: number | boolean;
611
+ /** Also write the run log here, outside `.fireforge/logs/` retention. */
612
+ keepLog?: string;
613
+ /** Forward mochitest's `--restart-between-tests` (mochitest dispatch only). */
614
+ restartBetweenTests?: boolean;
562
615
  /** Copy a file into the profile at a relative destination (repeatable source=destination). */
563
616
  profileFile?: string[];
564
617
  /** Run tests in headless mode */
@@ -580,13 +633,11 @@ export interface TestOptions extends EngineLockWaitable {
580
633
  */
581
634
  buildOnly?: boolean;
582
635
  /**
583
- * Union the paths this build packages onto the recorded coverage claim
584
- * instead of REPLACING it, so successive scoped builds accumulate and an
585
- * earlier slice's build-less runs stay covered. Valid only with
586
- * `--build`/`--build-only` and explicit paths. Refused fail-closed unless
587
- * the build anchor is unchanged: same engine HEAD, same `engine/mozconfig`,
588
- * and every previously fingerprinted packageable file byte-identical. See
589
- * `src/core/coverage-extend.ts`.
636
+ * Refuse, before the build, instead of dropping any earlier packaging
637
+ * claim. Scoped builds keep earlier paths by default while each path's own
638
+ * staging inputs and `engine/mozconfig` are unchanged; this flag makes a
639
+ * drop fail-closed. Valid only with `--build`/`--build-only` and explicit
640
+ * paths. See `src/core/coverage-extend.ts`.
590
641
  */
591
642
  extendCoverage?: boolean;
592
643
  /**
@@ -598,6 +649,12 @@ export interface TestOptions extends EngineLockWaitable {
598
649
  * that could not arm the refusal. Valid only with `--build`/`--build-only`.
599
650
  */
600
651
  refuseUnexportedDrift?: boolean;
652
+ /**
653
+ * `false` under `--no-furnace-apply`: the pre-test build skips the Furnace
654
+ * apply and packages engine/ as it stands. Valid only with
655
+ * `--build`/`--build-only`.
656
+ */
657
+ furnaceApply?: boolean;
601
658
  /** Forward mach's pathless auto-selection mode. Valid only with no explicit paths. */
602
659
  auto?: boolean;
603
660
  /**
@@ -691,6 +748,26 @@ export interface TestOptions extends EngineLockWaitable {
691
748
  * `FIREFORGE_SHUFFLE_SEED` for custom task code and recorded on the verdict.
692
749
  */
693
750
  shuffle?: number | boolean;
751
+ /**
752
+ * Run the same selection this many times under one engine-lock hold (the
753
+ * pre-test build, when requested, runs once). Each run prints a
754
+ * `FIREFORGE-RUN:` line; the verdict aggregates them with `runs=<p>/<n>`.
755
+ */
756
+ repeat?: number;
757
+ /**
758
+ * Project-relative path of a reviewed `git diff` against `engine/` or
759
+ * `components/` sources, applied after the pre-test build and before each
760
+ * dispatch, and restored byte-exact before the lock is released. The
761
+ * verdict carries `control=caught|missed|inconclusive`. See
762
+ * `src/core/negative-control.ts`.
763
+ */
764
+ negativeControl?: string;
765
+ /**
766
+ * Let a browser run proceed when the mochitest server or Marionette port
767
+ * is held by something that is not this checkout's own, relying on the
768
+ * engine harness's busy-port fallback. Same as `test.mochitestPorts: "auto"`.
769
+ */
770
+ autoPorts?: boolean;
694
771
  }
695
772
  /**
696
773
  * Options for the furnace apply command.
@@ -707,10 +784,24 @@ export interface FurnaceApplyOptions extends DryRunnable {
707
784
  */
708
785
  waitLockSeconds?: number;
709
786
  }
787
+ /**
788
+ * `--wait-lock` budget for a furnace command that takes only Furnace's own
789
+ * lock (`.fireforge/furnace.lock`, via `runFurnaceMutation`): `create`,
790
+ * `remove`, `rename`, `refresh`, `scan`, `override`, `preview` and the
791
+ * `chrome-doc` pair. Without it the flag was accepted and ignored on those
792
+ * commands, so their lock budget came only from `FIREFORGE_WAIT_LOCK`.
793
+ */
794
+ export interface FurnaceLockWaitable {
795
+ /**
796
+ * Resolved `--wait-lock` budget in seconds, or undefined for the default
797
+ * (`FIREFORGE_WAIT_LOCK`, else the furnace lock's own 30 s).
798
+ */
799
+ waitLockSeconds?: number;
800
+ }
710
801
  /**
711
802
  * Options for the furnace preview command.
712
803
  */
713
- export interface FurnacePreviewOptions {
804
+ export interface FurnacePreviewOptions extends FurnaceLockWaitable {
714
805
  /** Force reinstall Storybook dependencies */
715
806
  install?: boolean;
716
807
  }
@@ -732,7 +823,7 @@ export interface FurnaceDeployOptions extends DryRunnable {
732
823
  /**
733
824
  * Options for the furnace refresh command.
734
825
  */
735
- export interface FurnaceRefreshOptions extends DryRunnable {
826
+ export interface FurnaceRefreshOptions extends DryRunnable, FurnaceLockWaitable {
736
827
  /** Refresh all overrides in a single batch */
737
828
  all?: boolean;
738
829
  /** Conflict resolution strategy for automated use (ours = keep local, theirs = accept upstream) */
@@ -740,6 +831,11 @@ export interface FurnaceRefreshOptions extends DryRunnable {
740
831
  /** Reset the override's baseline to the current engine HEAD, skipping three-way merge */
741
832
  resetBase?: boolean;
742
833
  }
834
+ /**
835
+ * Options for the furnace rename command.
836
+ */
837
+ export interface FurnaceRenameOptions extends DryRunnable, FurnaceLockWaitable {
838
+ }
743
839
  /**
744
840
  * Options for the furnace sync command.
745
841
  */
@@ -759,25 +855,30 @@ export interface FurnaceSyncOptions extends DryRunnable {
759
855
  export interface FurnaceValidateOptions {
760
856
  /** Auto-fix registration issues (missing jar.mn entries, customElements.js registration) */
761
857
  fix?: boolean;
858
+ /** Write one schemaVersion-1 JSON document to stdout instead of the human report. */
859
+ json?: boolean;
860
+ /** Fail when more than this many warnings remain after `validationExceptions`. */
861
+ maxWarnings?: number;
762
862
  }
763
863
  /**
764
864
  * Options for the furnace override command.
765
865
  */
766
- export interface FurnaceOverrideOptions {
866
+ export interface FurnaceOverrideOptions extends FurnaceLockWaitable {
767
867
  /** Override type: css-only or full */
768
868
  type?: 'css-only' | 'full';
769
869
  /** Description of the override */
770
870
  description?: string;
771
871
  }
772
872
  /**
773
- * Options for the furnace remove command. `--yes` is the only flag it takes,
774
- * so the type is the confirmation mixin under a command-specific name.
873
+ * Options for the furnace remove command: the confirmation mixin plus the
874
+ * furnace lock budget.
775
875
  */
776
- export type FurnaceRemoveOptions = Confirmable;
876
+ export interface FurnaceRemoveOptions extends Confirmable, FurnaceLockWaitable {
877
+ }
777
878
  /**
778
879
  * Options for the furnace create command.
779
880
  */
780
- export interface FurnaceCreateOptions {
881
+ export interface FurnaceCreateOptions extends FurnaceLockWaitable {
781
882
  /** Component description */
782
883
  description?: string;
783
884
  /**
@@ -884,6 +985,14 @@ export interface PatchDeleteOptions extends Confirmable, DryRunnable, PatchLockW
884
985
  /** Bypass the hard refusal when later patches depend on the target. */
885
986
  forceUnsafe?: boolean;
886
987
  }
988
+ /**
989
+ * Options for the `fireforge patch retire` subcommand: `patch delete` that
990
+ * keeps the removed patch's order as a retired slot in `patches.json`.
991
+ */
992
+ export interface PatchRetireOptions extends PatchDeleteOptions {
993
+ /** Optional human-readable rationale stored on the retired slot. */
994
+ reason?: string;
995
+ }
887
996
  /**
888
997
  * Options for the `fireforge patch rename` subcommand. Updates the patch's
889
998
  * filename, manifest `name`, and (optionally) `description` atomically
@@ -991,9 +1100,27 @@ export interface StatusOptions {
991
1100
  /**
992
1101
  * Comma-separated classification list replacing the default `--check`
993
1102
  * policy. Implies `--check`. Unknown values refuse, naming the valid
994
- * classification set.
1103
+ * classification set. `ownership-conflict` (a path claimed by more than
1104
+ * one patch) is accepted beside the file classifications.
995
1105
  */
996
1106
  failOn?: string;
1107
+ /**
1108
+ * Comma-separated classification list that never fails `--check`,
1109
+ * removed from the fail set. Implies `--check`. Naming a class in both
1110
+ * `failOn` and `allow` refuses.
1111
+ */
1112
+ allow?: string;
1113
+ /**
1114
+ * With `allow`: fail on every present classification that neither
1115
+ * `failOn` nor `allow` names, so a classification added by a later
1116
+ * FireForge fails closed. Implies `--check`.
1117
+ */
1118
+ failOnUnknown?: boolean;
1119
+ /**
1120
+ * Judge only these patches (`--patches`, each value may be a comma
1121
+ * list): files they own or claim and ownership rows naming them.
1122
+ */
1123
+ patches?: string[];
997
1124
  }
998
1125
  /**
999
1126
  * Options for the token add command.