@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
@@ -51,6 +51,23 @@ export interface PatchResult {
51
51
  */
52
52
  autoResolvedOriginals?: Map<string, Uint8Array>;
53
53
  }
54
+ /**
55
+ * A size waiver with a ceiling, one form of a `PatchMetadata.lintIgnore`
56
+ * entry. The named size rule stays suppressed while its measurement is at
57
+ * or below `max` and fires again past it, so a waived file cannot grow
58
+ * without bound. Only the measured size rules (`file-too-large`,
59
+ * `large-patch-lines`, `large-patch-files`) accept a ceiling.
60
+ */
61
+ export interface LintIgnoreCeiling {
62
+ /** Size rule check ID the waiver applies to. */
63
+ rule: string;
64
+ /** Largest measurement (lines or files) the waiver still suppresses. */
65
+ max: number;
66
+ /** Why the waiver exists. Required so the ceiling is reviewable. */
67
+ reason: string;
68
+ }
69
+ /** One `PatchMetadata.lintIgnore` entry: a bare check ID or a ceiling waiver. */
70
+ export type LintIgnoreEntry = string | LintIgnoreCeiling;
54
71
  /**
55
72
  * Extended patch information with metadata.
56
73
  */
@@ -91,8 +108,11 @@ export interface PatchMetadata {
91
108
  * listed here still run normally. An entry for an unknown check ID is a
92
109
  * no-op: the metadata documents the intent to suppress even if the check
93
110
  * is later renamed or removed.
111
+ *
112
+ * An entry may instead be a {@link LintIgnoreCeiling}, which waives a size
113
+ * rule only up to a measured maximum.
94
114
  */
95
- lintIgnore?: string[];
115
+ lintIgnore?: LintIgnoreEntry[];
96
116
  /**
97
117
  * Optional per-patch threshold-tier override for the `large-patch-lines`
98
118
  * rule. Exists for branding patches that must touch a small number of
@@ -171,6 +191,26 @@ export interface PatchStagedRegistration {
171
191
  /** Optional human-readable rationale for the staged dependency. */
172
192
  reason?: string;
173
193
  }
194
+ /**
195
+ * A staged-dependency declaration a lint finding proposes: the arguments of
196
+ * one `patch staged-dependency <patch> --add` call.
197
+ */
198
+ export interface PatchLintStagedDeclaration {
199
+ /** Patch that must carry the declaration. */
200
+ patch: string;
201
+ /** Declaration kind (`--kind`). */
202
+ kind: 'import' | 'registration';
203
+ /** Declaring file path relative to engine/. */
204
+ file: string;
205
+ /** Import specifier (`kind: import`). */
206
+ specifier?: string;
207
+ /** Registration line as the patch adds it (`kind: registration`). */
208
+ line?: string;
209
+ /** Later-created file path relative to engine/. */
210
+ creates: string;
211
+ /** Later patch that creates it. */
212
+ owner: string;
213
+ }
174
214
  /**
175
215
  * Schema for patches/patches.json file.
176
216
  */
@@ -179,6 +219,27 @@ export interface PatchesManifest {
179
219
  version: 1;
180
220
  /** Array of patch metadata entries */
181
221
  patches: PatchMetadata[];
222
+ /**
223
+ * Order numbers a `patch retire` emptied on purpose. A retired slot has no
224
+ * `.patch` file and no row in `patches`, so apply, export, lint and rebase
225
+ * never see it; only the `patchPolicy.allowGaps: false` gap check reads it,
226
+ * as an occupied order. A patch that later takes the same order fills the
227
+ * slot, and the slot is dropped on that write.
228
+ */
229
+ retired?: PatchRetiredSlot[];
230
+ }
231
+ /** One placeholder left by `patch retire`. */
232
+ export interface PatchRetiredSlot {
233
+ /** The order number the retired patch held. */
234
+ order: number;
235
+ /** Category of the retired patch, kept so the slot stays in its range. */
236
+ category: PatchCategory;
237
+ /** Filename of the retired patch, for the audit trail only. */
238
+ filename: string;
239
+ /** ISO timestamp of the retirement. */
240
+ retiredAt: string;
241
+ /** Optional human-readable rationale. */
242
+ reason?: string;
182
243
  }
183
244
  /**
184
245
  * Summary of import operation with continue mode.
@@ -221,6 +282,13 @@ export interface PatchLintIssue {
221
282
  * rename-sensitive fingerprint.
222
283
  */
223
284
  patches?: string[];
285
+ /**
286
+ * For a `forward-import` or `forward-registration` finding: the staged
287
+ * declarations that would discharge it, one per later creator. The same
288
+ * records the printed `patch staged-dependency --add` commands carry, in
289
+ * the shape `patch staged-dependency --from` reads back.
290
+ */
291
+ stagedDeclarations?: PatchLintStagedDeclaration[];
224
292
  /** Severity: errors block export, warnings are advisory, notices are informational (not counted) */
225
293
  severity: 'error' | 'warning' | 'notice';
226
294
  /**
@@ -232,4 +300,10 @@ export interface PatchLintIssue {
232
300
  * introduced by the current task.
233
301
  */
234
302
  tag?: 'introduced' | 'cumulative';
303
+ /**
304
+ * The value a size rule measured (lines for `file-too-large` and
305
+ * `large-patch-lines`, files for `large-patch-files`). A lintIgnore
306
+ * ceiling compares against it, and `lint --report` carries it.
307
+ */
308
+ measurement?: number;
235
309
  }
@@ -54,6 +54,22 @@ export interface TestConfig {
54
54
  * verdict line. `--reap-orphans` is the per-invocation equivalent.
55
55
  */
56
56
  reapOrphans?: 'report' | 'reap';
57
+ /**
58
+ * `fixed` (the default) refuses a browser run while the mochitest server
59
+ * or Marionette port is held. `auto` lets a run proceed when the holder is
60
+ * not this checkout's own and the engine's mochitest harness can fall back
61
+ * to a free port itself, so sibling checkouts can run browser suites at
62
+ * once. `--auto-ports` is the per-invocation equivalent.
63
+ */
64
+ mochitestPorts?: 'fixed' | 'auto';
65
+ }
66
+ /** Patch history log settings stored in fireforge.json. */
67
+ export interface HistoryConfig {
68
+ /**
69
+ * Project-relative path of the append-only patch history log. Defaults to
70
+ * `.fireforge/history.jsonl`, outside the tracked patch queue.
71
+ */
72
+ path?: string;
57
73
  }
58
74
  /** A single external executable required by a project-specific toolchain. */
59
75
  export interface ExternalToolRequirement {
@@ -104,6 +120,35 @@ export interface PatchPolicyReservedRange {
104
120
  /** Exact patch exceptions allowed in this reserved range. */
105
121
  allowed: PatchPolicyReservedAllowedPatch[];
106
122
  }
123
+ /**
124
+ * One `patchPolicy.upstreamAllowlist` entry in object form. The string form
125
+ * is shorthand for `{ path }`.
126
+ */
127
+ export interface PatchPolicyUpstreamAllowEntry {
128
+ /** Engine-relative path glob (`*` within a segment, `**` across segments). */
129
+ path: string;
130
+ /**
131
+ * Registration-only mode: a patch may only add lines to a matching file
132
+ * (no removals, no deletion), and every non-blank added line must carry
133
+ * `marker` (or `patchPolicy.addOnlyMarker`), case-insensitively.
134
+ */
135
+ addOnly?: boolean;
136
+ /** Marker an added line must contain. Overrides `patchPolicy.addOnlyMarker`. */
137
+ marker?: string;
138
+ }
139
+ /**
140
+ * Limits the files a class of patch may touch, e.g. a `-tests` patch to test
141
+ * directories and test manifests. A rule applies to a patch when every
142
+ * selector it sets matches. At least one selector is required.
143
+ */
144
+ export interface PatchPolicyScopeRule {
145
+ /** Applies to patches in this category. */
146
+ category?: string;
147
+ /** Applies to patches whose filename matches this regular expression. */
148
+ filenamePattern?: string;
149
+ /** Engine-relative path globs every file the patch touches must match. */
150
+ files: string[];
151
+ }
107
152
  /**
108
153
  * Optional project-specific patch queue policy. When absent, FireForge keeps
109
154
  * its historical broad category + numeric ordering behaviour.
@@ -121,6 +166,21 @@ export interface PatchPolicyConfig {
121
166
  ranges: PatchPolicyRange[];
122
167
  /** Reserved exception ranges. */
123
168
  reservedRanges?: PatchPolicyReservedRange[];
169
+ /**
170
+ * Globs naming the upstream files a non-reserved patch may modify. A file
171
+ * counts as upstream when no patch in the queue creates it. Unset: no
172
+ * restriction.
173
+ */
174
+ upstreamAllowlist?: Array<string | PatchPolicyUpstreamAllowEntry>;
175
+ /** Default marker for `addOnly` allowlist entries that set none. */
176
+ addOnlyMarker?: string;
177
+ /** File-scope rules per patch class (category and/or filename pattern). */
178
+ scopeRules?: PatchPolicyScopeRule[];
179
+ /**
180
+ * Require every reserved allowlist entry's `adr` and `documentation` path
181
+ * to exist under the project root. Default false.
182
+ */
183
+ verifyDocumentPaths?: boolean;
124
184
  }
125
185
  /**
126
186
  * Main fireforge.json configuration schema.
@@ -142,6 +202,8 @@ export interface FireForgeConfig {
142
202
  buildAudit?: BuildAuditConfig;
143
203
  /** Test command defaults */
144
204
  test?: TestConfig;
205
+ /** Patch history log settings */
206
+ history?: HistoryConfig;
145
207
  /** Optional project-specific external toolchains checked by doctor. */
146
208
  externalToolchains?: ExternalToolchainConfig[];
147
209
  /** Project license SPDX identifier */
@@ -154,6 +216,8 @@ export interface FireForgeConfig {
154
216
  patchPolicy?: PatchPolicyConfig;
155
217
  /** Typecheck command configuration (CI-grade, whole-project) */
156
218
  typecheck?: TypecheckConfig;
219
+ /** `fireforge check`: the composed static gate's policy and project steps */
220
+ check?: CheckConfig;
157
221
  /**
158
222
  * Project marker prefix appended to lines FireForge writes into
159
223
  * upstream Firefox source files (e.g. the `customElements.js` tag list).
@@ -162,6 +226,45 @@ export interface FireForgeConfig {
162
226
  */
163
227
  markerComment?: string;
164
228
  }
229
+ /** The built-in steps of `fireforge check`, in table order. */
230
+ export declare const CHECK_BUILTIN_STEPS: readonly ["status", "verify", "lint", "typecheck", "furnace-validate", "token-coverage"];
231
+ /** One built-in `fireforge check` step name. */
232
+ export type CheckBuiltinStep = (typeof CHECK_BUILTIN_STEPS)[number];
233
+ /**
234
+ * A project command `fireforge check` runs beside its built-in steps
235
+ * (`check.extraSteps[]`): an audit FireForge does not ship.
236
+ */
237
+ export interface CheckExtraStep {
238
+ /** Unique step name shown in the table (lowercase, digits and dashes). */
239
+ name: string;
240
+ /** Shell command line, run with `/bin/sh -c` (`cmd.exe /c` on Windows). */
241
+ command: string;
242
+ /** Project-relative working directory. Defaults to the project root. */
243
+ cwd?: string;
244
+ /** Seconds before the step is killed and fails. Unbounded when unset. */
245
+ timeout?: number;
246
+ }
247
+ /** Configuration for `fireforge check`. Every field is optional. */
248
+ export interface CheckConfig {
249
+ /** Built-in steps this project never runs. */
250
+ skip?: CheckBuiltinStep[];
251
+ /**
252
+ * The status step's `--check` policy: `--fail-on`, `--allow` and
253
+ * `--fail-on-unknown`. Unset runs the default `status --check` policy.
254
+ */
255
+ status?: {
256
+ failOn?: string[];
257
+ allow?: string[];
258
+ failOnUnknown?: boolean;
259
+ };
260
+ /** `--max-warnings` for the lint and furnace-validate steps. */
261
+ maxWarnings?: {
262
+ lint?: number;
263
+ furnace?: number;
264
+ };
265
+ /** Project commands run as further steps. */
266
+ extraSteps?: CheckExtraStep[];
267
+ }
165
268
  /**
166
269
  * Configuration for the `fireforge typecheck` command. Distinct from
167
270
  * `patchLint.checkJs`: patch-lint runs every time `fireforge lint` runs
@@ -297,6 +400,37 @@ export interface PatchLintFileSizeThresholds {
297
400
  /** Test files. Defaults: notice 1200, warning 1400, error 1600. */
298
401
  test?: PatchLintFileSizeTier;
299
402
  }
403
+ /** What `file-too-large` counts: every line, or only lines that carry code. */
404
+ export type PatchLintFileSizeMetric = 'raw' | 'code';
405
+ /**
406
+ * What `large-patch-lines` counts: every non-binary line of the unified
407
+ * diff, or only the added lines that carry code.
408
+ */
409
+ export type PatchLintPatchSizeMetric = 'diff' | 'addedCode';
410
+ /**
411
+ * Overrides for one patch-size tier. `lines` drives `large-patch-lines`,
412
+ * `files` drives `large-patch-files`. Each is a `notice`/`warning`/`error`
413
+ * triple shaped like {@link PatchLintFileSizeTier}, merged over the
414
+ * defaults and validated `notice <= warning <= error`.
415
+ */
416
+ export interface PatchLintPatchSizeTier {
417
+ lines?: PatchLintFileSizeTier;
418
+ files?: PatchLintFileSizeTier;
419
+ }
420
+ /**
421
+ * Overrides for the patch-size thresholds, per tier.
422
+ *
423
+ * Module constants until 0.50.0. The defaults are the old constants: lines
424
+ * general 800/1500/3000, test 1500/3000/6000, branding 8000/18000/30000;
425
+ * files warn above 5 (general, test) or 60 (branding). The files triple has
426
+ * no notice or error band by default: an unset `files.notice` follows the
427
+ * effective `files.warning`, and an unset `files.error` never fires.
428
+ */
429
+ export interface PatchLintPatchSizeThresholds {
430
+ general?: PatchLintPatchSizeTier;
431
+ test?: PatchLintPatchSizeTier;
432
+ branding?: PatchLintPatchSizeTier;
433
+ }
300
434
  /**
301
435
  * Configuration for patch lint rules.
302
436
  */
@@ -348,6 +482,34 @@ export interface PatchLintConfig {
348
482
  * Unset fields keep the built-in defaults.
349
483
  */
350
484
  fileSizeThresholds?: PatchLintFileSizeThresholds;
485
+ /**
486
+ * What `file-too-large` counts. `'raw'` (the default) counts every line
487
+ * the way `wc -l` does. `'code'` counts the lines left non-blank once
488
+ * comments are blanked, so JSDoc and blank lines stop counting.
489
+ */
490
+ fileSizeMetric?: PatchLintFileSizeMetric;
491
+ /**
492
+ * Per-tier overrides for `large-patch-lines` and `large-patch-files`.
493
+ * Unset fields keep the built-in defaults.
494
+ */
495
+ patchSizeThresholds?: PatchLintPatchSizeThresholds;
496
+ /**
497
+ * What `large-patch-lines` counts. `'diff'` (the default) counts every
498
+ * non-binary line of the unified diff. `'addedCode'` counts added lines
499
+ * that are neither blank nor comment, judged on each file's post-image,
500
+ * skips lines inside Furnace `@fireforge-include` spans, and counts test
501
+ * files against the test tier and everything else against the general
502
+ * tier inside a mixed patch.
503
+ */
504
+ patchSizeMetric?: PatchLintPatchSizeMetric;
505
+ /**
506
+ * Globs naming registration files (`jar.mn`, `moz.build`, manifests)
507
+ * that count toward neither `large-patch-lines` nor `large-patch-files`.
508
+ * A glob without a `/` matches the basename, one with a `/` matches the
509
+ * engine-relative path. `*` stays within a path segment, `**` crosses
510
+ * segments. Default: none.
511
+ */
512
+ patchSizeRegistrationFiles?: string[];
351
513
  /**
352
514
  * Run the project's Prettier over patch-owned `.sys.mjs` modules, from
353
515
  * inside `engine/` so the engine's own `.prettierrc*`/`.prettierignore`
@@ -387,6 +549,36 @@ export interface PatchLintConfig {
387
549
  * and fail the other. Default: 'warning'.
388
550
  */
389
551
  undefinedIdentifiers?: PatchLintSeverityGate;
552
+ /**
553
+ * Token scales enforced over CSS files a patch creates (`token-scale`).
554
+ * Each scale names properties whose values must resolve to its tokens.
555
+ * Default unset: no scale is enforced.
556
+ */
557
+ tokenScales?: PatchLintTokenScale[];
558
+ }
559
+ /**
560
+ * One `patchLint.tokenScales[]` entry: a property family (spacing, type,
561
+ * radius, opacity, z-index, …) whose declared values must be tokens on one
562
+ * scale.
563
+ */
564
+ export interface PatchLintTokenScale {
565
+ /** CSS properties governed (exact names, or a `*`-suffixed prefix such as `padding*`). */
566
+ properties: string[];
567
+ /** Custom-property prefix of the scale's tokens, e.g. `--mybrowser-space-`. */
568
+ prefix: string;
569
+ /**
570
+ * Literal value atoms accepted without a token (`0`, `auto`, `1px`).
571
+ * `*` is a wildcard, so `*%` accepts any percentage. CSS-wide keywords
572
+ * (`inherit`, `initial`, `unset`, `revert`, `revert-layer`) are always
573
+ * accepted.
574
+ */
575
+ allowLiterals?: string[];
576
+ /**
577
+ * Further custom properties accepted besides `prefix` (exact names, or a
578
+ * `*`-suffixed prefix such as `--moz-*`), for reviewed upstream or recipe
579
+ * tokens.
580
+ */
581
+ allow?: string[];
390
582
  }
391
583
  /**
392
584
  * Build modes for mach.
@@ -1,3 +1,12 @@
1
+ /** The built-in steps of `fireforge check`, in table order. */
2
+ export const CHECK_BUILTIN_STEPS = [
3
+ 'status',
4
+ 'verify',
5
+ 'lint',
6
+ 'typecheck',
7
+ 'furnace-validate',
8
+ 'token-coverage',
9
+ ];
1
10
  /**
2
11
  * Build modes for mach.
3
12
  *
@@ -103,6 +103,24 @@ export interface CustomComponentConfig {
103
103
  */
104
104
  sharedFtl?: string;
105
105
  }
106
+ /** A reviewed `furnace validate` finding (`furnace.json#validationExceptions[]`). */
107
+ export interface FurnaceValidationException {
108
+ /** Component tag name the finding is reported on. */
109
+ component: string;
110
+ /** The finding's check name, as `furnace validate` prints it in brackets. */
111
+ check: string;
112
+ /** Why the finding is accepted. Required: the entry is the review record. */
113
+ reason: string;
114
+ }
115
+ /**
116
+ * `furnace validate` checks that run only when `furnace.json#optInChecks`
117
+ * names them. `icon-button-label`: an icon-only `<moz-button>` (an
118
+ * `iconsrc` or an `icon` type, no `label` and no text content) carries
119
+ * neither `data-l10n-id` nor `aria-label`.
120
+ */
121
+ export declare const FURNACE_OPT_IN_CHECKS: readonly ["icon-button-label"];
122
+ /** One opt-in check name. */
123
+ export type FurnaceOptInCheck = (typeof FURNACE_OPT_IN_CHECKS)[number];
106
124
  /**
107
125
  * The furnace.json schema.
108
126
  */
@@ -161,6 +179,17 @@ export interface FurnaceConfig {
161
179
  * Unset disables jsconfig maintenance.
162
180
  */
163
181
  typecheckJsconfig?: string;
182
+ /**
183
+ * Reviewed `furnace validate` findings. Each entry suppresses every
184
+ * finding of `check` on `component`, and `furnace validate` reports an
185
+ * entry that no longer matches anything as a stale-exception warning.
186
+ */
187
+ validationExceptions?: FurnaceValidationException[];
188
+ /**
189
+ * Opt-in `furnace validate` checks (see {@link FURNACE_OPT_IN_CHECKS}).
190
+ * Unset runs only the default checks.
191
+ */
192
+ optInChecks?: FurnaceOptInCheck[];
164
193
  /** Stock components tracked for preview */
165
194
  stock: string[];
166
195
  /** Override components */
@@ -234,6 +263,15 @@ export interface StepError {
234
263
  */
235
264
  advisory?: boolean;
236
265
  }
266
+ /**
267
+ * Why `applyAllComponents` skipped a component, so a summary can say more
268
+ * than "skipped". `already-current`: the source is unchanged since the last
269
+ * recorded apply and the engine copy still matches it (fragment expansions
270
+ * included), so there was nothing to write. The apply state is shared by
271
+ * every session on the checkout, so the recorded apply may be another
272
+ * session's, e.g. a peer's `test --build`.
273
+ */
274
+ export type ApplySkipKind = 'already-current';
237
275
  /**
238
276
  * Result of applying all components to the engine source tree.
239
277
  */
@@ -246,10 +284,14 @@ export interface ApplyResult {
246
284
  /** Non-fatal registration step errors */
247
285
  stepErrors?: StepError[];
248
286
  }>;
249
- /** Components that were skipped (e.g., no changes) */
287
+ /**
288
+ * Components that were skipped (e.g., no changes). `kind` names the
289
+ * reason for summaries; `reason` is the stable free-text form.
290
+ */
250
291
  skipped: Array<{
251
292
  name: string;
252
293
  reason: string;
294
+ kind?: ApplySkipKind;
253
295
  }>;
254
296
  /** Components that failed to apply */
255
297
  errors: Array<{
@@ -1,3 +1,10 @@
1
+ /**
2
+ * `furnace validate` checks that run only when `furnace.json#optInChecks`
3
+ * names them. `icon-button-label`: an icon-only `<moz-button>` (an
4
+ * `iconsrc` or an `icon` type, no `label` and no text content) carries
5
+ * neither `data-l10n-id` nor `aria-label`.
6
+ */
7
+ export const FURNACE_OPT_IN_CHECKS = ['icon-button-label'];
1
8
  /**
2
9
  * Operations that can leave a pending-repair marker when they fail to roll
3
10
  * back cleanly. The marker is consumed by `fireforge doctor`, which either
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Re-exports all type definitions.
3
3
  */
4
- export type { BuildConfig, FireForgeConfig, FirefoxConfig, FirefoxProduct, ProjectLicense, ProjectPaths, WireConfig, } from './config.js';
4
+ export type { BuildConfig, FireForgeConfig, FirefoxConfig, FirefoxProduct, PatchLintFileSizeThresholds, PatchLintFileSizeTier, ProjectLicense, ProjectPaths, WireConfig, } from './config.js';
5
5
  export type { ApplyResult, ComponentType, CustomComponentConfig, DryRunAction, FurnaceConfig, FurnaceState, OverrideComponentConfig, OverrideType, StepError, ValidationIssue, } from './furnace.js';
@@ -13,7 +13,9 @@ import type { Command } from 'commander';
13
13
  * @param obj - Object to filter
14
14
  * @returns The same object with every `undefined`-valued key removed
15
15
  */
16
- export declare function pickDefined<T extends object>(obj: T): {
16
+ export declare function pickDefined<T extends object>(obj: T): DefinedProps<T>;
17
+ /** `T` with every property optional and `undefined` removed: what {@link pickDefined} returns. */
18
+ export type DefinedProps<T> = {
17
19
  [K in keyof T]+?: Exclude<T[K], undefined>;
18
20
  };
19
21
  /**
@@ -74,11 +76,11 @@ export declare function resolveWaitLockSeconds(value: string | number | boolean
74
76
  * `--wait-lock` flag was threaded to. A furnace mutation takes the engine
75
77
  * session lock and then `.fireforge/furnace.lock`. Only the first had a
76
78
  * budget, so a contended deploy died on the file lock's fixed 30 s while
77
- * its operator believed a much larger budget was in force. Threading the
78
- * flag reaches the three furnace commands that declare it. This reaches
79
- * the twelve that do not (`create`, `remove`, `rename`, `refresh`, `scan`,
80
- * `override`, `chrome-doc`, …), which have no flag to thread but are just
81
- * as contended.
79
+ * its operator believed a much larger budget was in force. Every furnace
80
+ * leaf that takes the furnace lock threads its own `--wait-lock` into it;
81
+ * this is the fallback when the flag is absent, and the only budget for
82
+ * callers that do not thread one (doctor's furnace repair, the
83
+ * pre-build apply).
82
84
  *
83
85
  * Env-only: a flag belongs to one invocation and is passed
84
86
  * explicitly where it applies, while the environment is exactly the place a
@@ -109,6 +111,32 @@ export declare function addAcceptedWaitLockOption(command: Command): Command;
109
111
  * one everywhere else.
110
112
  */
111
113
  export declare function ensureWaitLockOptionEverywhere(command: Command): void;
114
+ /**
115
+ * True for a token that reads as a path rather than a number: it has a
116
+ * path separator or a file extension, or is `.`/`..`. A bare word such as
117
+ * `nonsense` is not path-shaped, so `--wait-lock nonsense` stays the usage
118
+ * error it always was.
119
+ */
120
+ export declare function isPathShapedArgument(token: string): boolean;
121
+ /**
122
+ * Rewrites a command's option tokens so a path-shaped argument after a
123
+ * numeric optional-value flag is an operand, and the flag takes its
124
+ * default.
125
+ *
126
+ * Commander binds the next non-dash token to an optional value, so
127
+ * `fireforge test --build --wait-lock browser_x.js` failed with
128
+ * "--wait-lock must be an integer in 1..3600 (got "browser_x.js")": the
129
+ * help says the value is optional, and the obvious spelling refused. The
130
+ * released flag moves to the end of the option section (before any `--`),
131
+ * where nothing follows it, so commander reads it bare. Operands keep their
132
+ * relative order. The value of a required-value option (`--mach-arg
133
+ * --wait-lock`) is never mistaken for a flag.
134
+ *
135
+ * @param argv - The tokens commander passes to `parseOptions`
136
+ * @param requiredValueFlags - This command's flags that always take a value
137
+ * @returns The rewritten tokens
138
+ */
139
+ export declare function releasePathShapedValues(argv: readonly string[], requiredValueFlags: ReadonlySet<string>): string[];
112
140
  /**
113
141
  * Registers the shared `--wait-lock [seconds]` flag on a lock-taking
114
142
  * command (the engine session lock for engine-mutating commands, the patch
@@ -151,11 +151,11 @@ export function resolveWaitLockSeconds(value) {
151
151
  * `--wait-lock` flag was threaded to. A furnace mutation takes the engine
152
152
  * session lock and then `.fireforge/furnace.lock`. Only the first had a
153
153
  * budget, so a contended deploy died on the file lock's fixed 30 s while
154
- * its operator believed a much larger budget was in force. Threading the
155
- * flag reaches the three furnace commands that declare it. This reaches
156
- * the twelve that do not (`create`, `remove`, `rename`, `refresh`, `scan`,
157
- * `override`, `chrome-doc`, …), which have no flag to thread but are just
158
- * as contended.
154
+ * its operator believed a much larger budget was in force. Every furnace
155
+ * leaf that takes the furnace lock threads its own `--wait-lock` into it;
156
+ * this is the fallback when the flag is absent, and the only budget for
157
+ * callers that do not thread one (doctor's furnace repair, the
158
+ * pre-build apply).
159
159
  *
160
160
  * Env-only: a flag belongs to one invocation and is passed
161
161
  * explicitly where it applies, while the environment is exactly the place a
@@ -204,9 +204,90 @@ export function ensureWaitLockOptionEverywhere(command) {
204
204
  // action can see it. Register the uniform flag only on actionable leaves.
205
205
  if (sub.commands.length === 0 && !hasWaitLockOption(sub))
206
206
  addAcceptedWaitLockOption(sub);
207
+ releasePathShapedOptionalValues(sub);
207
208
  ensureWaitLockOptionEverywhere(sub);
208
209
  }
209
210
  }
211
+ /**
212
+ * Optional-value flags whose value is a number and therefore never a path.
213
+ * `--canary [path]` is the counter-example that must stay out: its value IS
214
+ * a path.
215
+ */
216
+ const NUMERIC_OPTIONAL_VALUE_FLAGS = new Set([
217
+ '--wait-lock',
218
+ '--wait-browser',
219
+ '--wait-port',
220
+ '--shuffle',
221
+ ]);
222
+ /**
223
+ * True for a token that reads as a path rather than a number: it has a
224
+ * path separator or a file extension, or is `.`/`..`. A bare word such as
225
+ * `nonsense` is not path-shaped, so `--wait-lock nonsense` stays the usage
226
+ * error it always was.
227
+ */
228
+ export function isPathShapedArgument(token) {
229
+ if (/^\d+(?:\.\d+)?$/.test(token))
230
+ return false;
231
+ return token === '.' || token === '..' || /[\\/]/.test(token) || /\.[A-Za-z][\w-]*$/.test(token);
232
+ }
233
+ /**
234
+ * Rewrites a command's option tokens so a path-shaped argument after a
235
+ * numeric optional-value flag is an operand, and the flag takes its
236
+ * default.
237
+ *
238
+ * Commander binds the next non-dash token to an optional value, so
239
+ * `fireforge test --build --wait-lock browser_x.js` failed with
240
+ * "--wait-lock must be an integer in 1..3600 (got "browser_x.js")": the
241
+ * help says the value is optional, and the obvious spelling refused. The
242
+ * released flag moves to the end of the option section (before any `--`),
243
+ * where nothing follows it, so commander reads it bare. Operands keep their
244
+ * relative order. The value of a required-value option (`--mach-arg
245
+ * --wait-lock`) is never mistaken for a flag.
246
+ *
247
+ * @param argv - The tokens commander passes to `parseOptions`
248
+ * @param requiredValueFlags - This command's flags that always take a value
249
+ * @returns The rewritten tokens
250
+ */
251
+ export function releasePathShapedValues(argv, requiredValueFlags) {
252
+ const kept = [];
253
+ const released = [];
254
+ let index = 0;
255
+ for (; index < argv.length; index += 1) {
256
+ const token = argv[index] ?? '';
257
+ if (token === '--')
258
+ break;
259
+ const next = argv[index + 1];
260
+ if (requiredValueFlags.has(token) && next !== undefined) {
261
+ kept.push(token, next);
262
+ index += 1;
263
+ continue;
264
+ }
265
+ if (NUMERIC_OPTIONAL_VALUE_FLAGS.has(token) &&
266
+ next !== undefined &&
267
+ !next.startsWith('-') &&
268
+ isPathShapedArgument(next)) {
269
+ released.push(token);
270
+ continue;
271
+ }
272
+ kept.push(token);
273
+ }
274
+ return [...kept, ...released, ...argv.slice(index)];
275
+ }
276
+ /**
277
+ * Installs {@link releasePathShapedValues} on `command` when it registers
278
+ * any numeric optional-value flag.
279
+ */
280
+ function releasePathShapedOptionalValues(command) {
281
+ const flags = command.options.filter((option) => option.long !== undefined && NUMERIC_OPTIONAL_VALUE_FLAGS.has(option.long));
282
+ if (flags.length === 0)
283
+ return;
284
+ const requiredValueFlags = new Set(command.options
285
+ .filter((option) => option.required)
286
+ .flatMap((option) => [option.long, option.short])
287
+ .filter((flag) => flag !== undefined));
288
+ const parseOptions = command.parseOptions.bind(command);
289
+ command.parseOptions = (argv) => parseOptions(releasePathShapedValues(argv, requiredValueFlags));
290
+ }
210
291
  /**
211
292
  * Registers the shared `--wait-lock [seconds]` flag on a lock-taking
212
293
  * command (the engine session lock for engine-mutating commands, the patch
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Minimal path globs for policy config: `*` matches within one path segment,
3
+ * `?` one character of a segment, and `**` any number of whole segments
4
+ * (`a/**` is everything under `a/`, `**\/x` is `x` in any directory). A
5
+ * pattern is anchored at both ends against an engine-relative POSIX path;
6
+ * there is no basename-anywhere matching, so `*.toml` matches only top-level
7
+ * files and `**\/*.toml` matches them everywhere.
8
+ *
9
+ * Hand-rolled because the policy surface needs exactly this much and the
10
+ * package keeps its dependency list short.
11
+ */
12
+ /** Compiles one glob to an anchored regular expression. */
13
+ export declare function globToRegExp(glob: string): RegExp;
14
+ /** True when `path` matches at least one of `globs`. */
15
+ export declare function matchesAnyGlob(path: string, globs: readonly string[]): boolean;