@hominis/fireforge 0.49.2 → 0.51.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 (400) hide show
  1. package/CHANGELOG.md +87 -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 +41 -0
  10. package/dist/src/commands/check-report.js +91 -0
  11. package/dist/src/commands/check-run.d.ts +33 -0
  12. package/dist/src/commands/check-run.js +112 -0
  13. package/dist/src/commands/check-steps.d.ts +75 -0
  14. package/dist/src/commands/check-steps.js +200 -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/declarations-report.d.ts +43 -0
  19. package/dist/src/commands/declarations-report.js +76 -0
  20. package/dist/src/commands/doctor-engine-eslint.js +6 -1
  21. package/dist/src/commands/doctor-source-pin.js +4 -1
  22. package/dist/src/commands/export-adopt-check.d.ts +38 -0
  23. package/dist/src/commands/export-adopt-check.js +98 -0
  24. package/dist/src/commands/export-flow.js +29 -14
  25. package/dist/src/commands/export-placement-gate.d.ts +6 -2
  26. package/dist/src/commands/export-placement-gate.js +8 -0
  27. package/dist/src/commands/export-shared.d.ts +2 -1
  28. package/dist/src/commands/export-staged-registration.d.ts +46 -0
  29. package/dist/src/commands/export-staged-registration.js +91 -0
  30. package/dist/src/commands/forward-import-remedy.d.ts +32 -0
  31. package/dist/src/commands/forward-import-remedy.js +63 -0
  32. package/dist/src/commands/furnace/apply.js +3 -3
  33. package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
  34. package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
  35. package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
  36. package/dist/src/commands/furnace/chrome-doc.js +2 -2
  37. package/dist/src/commands/furnace/create.js +2 -2
  38. package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
  39. package/dist/src/commands/furnace/deploy-named.js +107 -0
  40. package/dist/src/commands/furnace/deploy.d.ts +3 -2
  41. package/dist/src/commands/furnace/deploy.js +50 -86
  42. package/dist/src/commands/furnace/diff.js +26 -3
  43. package/dist/src/commands/furnace/index.js +66 -41
  44. package/dist/src/commands/furnace/init.js +13 -6
  45. package/dist/src/commands/furnace/override.js +4 -2
  46. package/dist/src/commands/furnace/preview.js +4 -3
  47. package/dist/src/commands/furnace/refresh.js +2 -2
  48. package/dist/src/commands/furnace/remove.js +4 -3
  49. package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
  50. package/dist/src/commands/furnace/rename-dry-run.js +127 -0
  51. package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
  52. package/dist/src/commands/furnace/rename-helpers.js +15 -1
  53. package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
  54. package/dist/src/commands/furnace/rename-registrations.js +98 -0
  55. package/dist/src/commands/furnace/rename.d.ts +2 -1
  56. package/dist/src/commands/furnace/rename.js +28 -66
  57. package/dist/src/commands/furnace/scan.d.ts +9 -4
  58. package/dist/src/commands/furnace/scan.js +11 -9
  59. package/dist/src/commands/furnace/sync.js +3 -2
  60. package/dist/src/commands/furnace/validate-report.d.ts +45 -0
  61. package/dist/src/commands/furnace/validate-report.js +91 -0
  62. package/dist/src/commands/furnace/validate.d.ts +1 -1
  63. package/dist/src/commands/furnace/validate.js +151 -113
  64. package/dist/src/commands/furnace/validation-output.js +14 -3
  65. package/dist/src/commands/lint-per-patch.js +36 -61
  66. package/dist/src/commands/lint-per-run-checkjs.js +2 -2
  67. package/dist/src/commands/lint-report.d.ts +8 -4
  68. package/dist/src/commands/lint-report.js +20 -5
  69. package/dist/src/commands/lint.js +15 -34
  70. package/dist/src/commands/manifest.js +4 -0
  71. package/dist/src/commands/park.d.ts +22 -0
  72. package/dist/src/commands/park.js +232 -0
  73. package/dist/src/commands/patch/delete-gap-notice.js +3 -1
  74. package/dist/src/commands/patch/delete.d.ts +1 -1
  75. package/dist/src/commands/patch/delete.js +24 -104
  76. package/dist/src/commands/patch/index.d.ts +3 -3
  77. package/dist/src/commands/patch/index.js +10 -4
  78. package/dist/src/commands/patch/lint-ignore-from.d.ts +44 -0
  79. package/dist/src/commands/patch/lint-ignore-from.js +142 -0
  80. package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
  81. package/dist/src/commands/patch/lint-ignore.js +78 -31
  82. package/dist/src/commands/patch/move-files-create.js +34 -18
  83. package/dist/src/commands/patch/move-files-into.js +15 -7
  84. package/dist/src/commands/patch/move-files.js +6 -2
  85. package/dist/src/commands/patch/removal-created-files.d.ts +48 -0
  86. package/dist/src/commands/patch/removal-created-files.js +83 -0
  87. package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
  88. package/dist/src/commands/patch/removal-dependents.js +133 -0
  89. package/dist/src/commands/patch/rename.js +20 -1
  90. package/dist/src/commands/patch/reserve.d.ts +33 -0
  91. package/dist/src/commands/patch/reserve.js +152 -0
  92. package/dist/src/commands/patch/retire.d.ts +34 -0
  93. package/dist/src/commands/patch/retire.js +133 -0
  94. package/dist/src/commands/patch/split-plan.d.ts +17 -8
  95. package/dist/src/commands/patch/split-plan.js +60 -28
  96. package/dist/src/commands/patch/split-staged.d.ts +108 -0
  97. package/dist/src/commands/patch/split-staged.js +202 -0
  98. package/dist/src/commands/patch/split.d.ts +5 -0
  99. package/dist/src/commands/patch/split.js +62 -17
  100. package/dist/src/commands/patch/stage.d.ts +34 -0
  101. package/dist/src/commands/patch/stage.js +153 -0
  102. package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
  103. package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
  104. package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
  105. package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
  106. package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
  107. package/dist/src/commands/patch/staged-dependency-from.js +108 -0
  108. package/dist/src/commands/patch/staged-dependency-remove.d.ts +31 -0
  109. package/dist/src/commands/patch/staged-dependency-remove.js +80 -0
  110. package/dist/src/commands/patch/staged-dependency.js +54 -139
  111. package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
  112. package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
  113. package/dist/src/commands/re-export-adjacent.d.ts +50 -2
  114. package/dist/src/commands/re-export-adjacent.js +160 -40
  115. package/dist/src/commands/re-export-bulk-scan.d.ts +2 -1
  116. package/dist/src/commands/re-export-bulk-scan.js +22 -7
  117. package/dist/src/commands/re-export-drift.d.ts +36 -8
  118. package/dist/src/commands/re-export-drift.js +73 -49
  119. package/dist/src/commands/re-export-files.js +11 -8
  120. package/dist/src/commands/re-export-hunks.d.ts +35 -0
  121. package/dist/src/commands/re-export-hunks.js +220 -0
  122. package/dist/src/commands/re-export-lint.d.ts +2 -1
  123. package/dist/src/commands/re-export-lint.js +23 -10
  124. package/dist/src/commands/re-export-options.js +74 -6
  125. package/dist/src/commands/re-export-preflight.d.ts +23 -0
  126. package/dist/src/commands/re-export-preflight.js +160 -0
  127. package/dist/src/commands/re-export-purity-scope.d.ts +18 -0
  128. package/dist/src/commands/re-export-purity-scope.js +131 -0
  129. package/dist/src/commands/re-export-register.js +56 -17
  130. package/dist/src/commands/re-export-removed.d.ts +13 -2
  131. package/dist/src/commands/re-export-removed.js +57 -12
  132. package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
  133. package/dist/src/commands/re-export-scan-owner.js +116 -0
  134. package/dist/src/commands/re-export-scan.d.ts +19 -9
  135. package/dist/src/commands/re-export-scan.js +60 -24
  136. package/dist/src/commands/re-export-single.js +122 -46
  137. package/dist/src/commands/re-export-stale-registrations.d.ts +18 -0
  138. package/dist/src/commands/re-export-stale-registrations.js +62 -0
  139. package/dist/src/commands/re-export-summary.d.ts +28 -0
  140. package/dist/src/commands/re-export-summary.js +36 -0
  141. package/dist/src/commands/re-export.js +71 -57
  142. package/dist/src/commands/rebase/index.js +17 -17
  143. package/dist/src/commands/run.js +16 -2
  144. package/dist/src/commands/status-check.d.ts +55 -16
  145. package/dist/src/commands/status-check.js +92 -37
  146. package/dist/src/commands/status-json.d.ts +22 -9
  147. package/dist/src/commands/status-json.js +47 -32
  148. package/dist/src/commands/status-output.js +3 -1
  149. package/dist/src/commands/status-scope.d.ts +37 -0
  150. package/dist/src/commands/status-scope.js +54 -0
  151. package/dist/src/commands/status.js +66 -23
  152. package/dist/src/commands/test-browser-preflight.js +19 -3
  153. package/dist/src/commands/test-build-furnace.d.ts +22 -0
  154. package/dist/src/commands/test-build-furnace.js +54 -0
  155. package/dist/src/commands/test-build-phase.js +64 -41
  156. package/dist/src/commands/test-checkjs.js +10 -2
  157. package/dist/src/commands/test-diagnose.js +7 -0
  158. package/dist/src/commands/test-dispatch.d.ts +39 -0
  159. package/dist/src/commands/test-dispatch.js +211 -0
  160. package/dist/src/commands/test-furnace-reach.d.ts +37 -0
  161. package/dist/src/commands/test-furnace-reach.js +132 -0
  162. package/dist/src/commands/test-modes.d.ts +16 -1
  163. package/dist/src/commands/test-modes.js +24 -1
  164. package/dist/src/commands/test-multi-verdict.d.ts +61 -0
  165. package/dist/src/commands/test-multi-verdict.js +111 -0
  166. package/dist/src/commands/test-orchestrate.d.ts +52 -0
  167. package/dist/src/commands/test-orchestrate.js +240 -0
  168. package/dist/src/commands/test-perf-scope.d.ts +50 -0
  169. package/dist/src/commands/test-perf-scope.js +138 -0
  170. package/dist/src/commands/test-prelock.d.ts +51 -0
  171. package/dist/src/commands/test-prelock.js +273 -0
  172. package/dist/src/commands/test-register.js +69 -10
  173. package/dist/src/commands/test-results.d.ts +62 -0
  174. package/dist/src/commands/test-results.js +62 -0
  175. package/dist/src/commands/test-run.d.ts +13 -55
  176. package/dist/src/commands/test-run.js +74 -99
  177. package/dist/src/commands/test-shards.d.ts +110 -0
  178. package/dist/src/commands/test-shards.js +255 -0
  179. package/dist/src/commands/test-stale-gate.d.ts +5 -1
  180. package/dist/src/commands/test-stale-gate.js +60 -28
  181. package/dist/src/commands/test-verdict.d.ts +11 -1
  182. package/dist/src/commands/test-verdict.js +28 -1
  183. package/dist/src/commands/test.d.ts +6 -1
  184. package/dist/src/commands/test.js +120 -124
  185. package/dist/src/commands/token-lint.d.ts +14 -0
  186. package/dist/src/commands/token-lint.js +151 -0
  187. package/dist/src/commands/token.js +10 -0
  188. package/dist/src/commands/verify-scope.d.ts +28 -0
  189. package/dist/src/commands/verify-scope.js +67 -0
  190. package/dist/src/commands/verify.d.ts +10 -2
  191. package/dist/src/commands/verify.js +67 -36
  192. package/dist/src/core/build-baseline-types.d.ts +46 -0
  193. package/dist/src/core/build-baseline-validate.js +19 -0
  194. package/dist/src/core/build-baseline.d.ts +5 -1
  195. package/dist/src/core/build-baseline.js +3 -1
  196. package/dist/src/core/build-overwrite-guard.d.ts +50 -0
  197. package/dist/src/core/build-overwrite-guard.js +84 -4
  198. package/dist/src/core/build-prepare.d.ts +13 -0
  199. package/dist/src/core/build-prepare.js +42 -10
  200. package/dist/src/core/build-run-record.d.ts +74 -0
  201. package/dist/src/core/build-run-record.js +136 -0
  202. package/dist/src/core/checkjs-support-heads.d.ts +41 -0
  203. package/dist/src/core/checkjs-support-heads.js +104 -0
  204. package/dist/src/core/config-paths.d.ts +2 -2
  205. package/dist/src/core/config-paths.js +10 -0
  206. package/dist/src/core/config-validate-check.d.ts +8 -0
  207. package/dist/src/core/config-validate-check.js +219 -0
  208. package/dist/src/core/config-validate-file-size.d.ts +9 -6
  209. package/dist/src/core/config-validate-file-size.js +91 -2
  210. package/dist/src/core/config-validate-history.d.ts +11 -0
  211. package/dist/src/core/config-validate-history.js +38 -0
  212. package/dist/src/core/config-validate-patch-policy.js +100 -0
  213. package/dist/src/core/config-validate-test-toolchains.js +21 -0
  214. package/dist/src/core/config-validate-token-scales.d.ts +9 -0
  215. package/dist/src/core/config-validate-token-scales.js +92 -0
  216. package/dist/src/core/config-validate.js +20 -5
  217. package/dist/src/core/config.d.ts +5 -0
  218. package/dist/src/core/config.js +16 -0
  219. package/dist/src/core/coverage-dispatch.d.ts +24 -0
  220. package/dist/src/core/coverage-dispatch.js +44 -0
  221. package/dist/src/core/coverage-extend.d.ts +70 -45
  222. package/dist/src/core/coverage-extend.js +199 -144
  223. package/dist/src/core/coverage-records.d.ts +108 -0
  224. package/dist/src/core/coverage-records.js +384 -0
  225. package/dist/src/core/destructive.d.ts +7 -4
  226. package/dist/src/core/destructive.js +10 -10
  227. package/dist/src/core/engine-session-lock.d.ts +16 -1
  228. package/dist/src/core/engine-session-lock.js +38 -10
  229. package/dist/src/core/export-moved-code-shape.d.ts +49 -11
  230. package/dist/src/core/export-moved-code-shape.js +135 -44
  231. package/dist/src/core/file-lock-queue.d.ts +67 -0
  232. package/dist/src/core/file-lock-queue.js +221 -0
  233. package/dist/src/core/file-lock-shared.d.ts +57 -0
  234. package/dist/src/core/file-lock-shared.js +117 -0
  235. package/dist/src/core/file-lock.d.ts +13 -25
  236. package/dist/src/core/file-lock.js +163 -140
  237. package/dist/src/core/furnace-apply-output.d.ts +10 -0
  238. package/dist/src/core/furnace-apply-output.js +31 -1
  239. package/dist/src/core/furnace-apply.js +22 -12
  240. package/dist/src/core/furnace-composed-source.d.ts +76 -0
  241. package/dist/src/core/furnace-composed-source.js +167 -0
  242. package/dist/src/core/furnace-config-validate-exceptions.d.ts +16 -0
  243. package/dist/src/core/furnace-config-validate-exceptions.js +102 -0
  244. package/dist/src/core/furnace-config-validate.js +14 -0
  245. package/dist/src/core/furnace-css-fragments.d.ts +10 -0
  246. package/dist/src/core/furnace-css-fragments.js +26 -0
  247. package/dist/src/core/furnace-operation.d.ts +3 -3
  248. package/dist/src/core/furnace-registration-jar-inplace.d.ts +12 -0
  249. package/dist/src/core/furnace-registration-jar-inplace.js +80 -0
  250. package/dist/src/core/furnace-registration-remove.d.ts +22 -1
  251. package/dist/src/core/furnace-registration-remove.js +53 -17
  252. package/dist/src/core/furnace-registration-rename.d.ts +26 -0
  253. package/dist/src/core/furnace-registration-rename.js +77 -0
  254. package/dist/src/core/furnace-registration.d.ts +14 -4
  255. package/dist/src/core/furnace-registration.js +44 -10
  256. package/dist/src/core/furnace-staleness.d.ts +7 -0
  257. package/dist/src/core/furnace-staleness.js +34 -24
  258. package/dist/src/core/furnace-validate-accessibility.d.ts +8 -4
  259. package/dist/src/core/furnace-validate-accessibility.js +71 -28
  260. package/dist/src/core/furnace-validate-exceptions.d.ts +48 -0
  261. package/dist/src/core/furnace-validate-exceptions.js +80 -0
  262. package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
  263. package/dist/src/core/furnace-validate-icon-button.js +78 -0
  264. package/dist/src/core/furnace-validate.js +1 -1
  265. package/dist/src/core/harness-port-fallback.d.ts +60 -0
  266. package/dist/src/core/harness-port-fallback.js +96 -0
  267. package/dist/src/core/history-log-file.d.ts +61 -0
  268. package/dist/src/core/history-log-file.js +164 -0
  269. package/dist/src/core/history-log.d.ts +2 -1
  270. package/dist/src/core/history-log.js +2 -1
  271. package/dist/src/core/json-preserving-edit.d.ts +22 -0
  272. package/dist/src/core/json-preserving-edit.js +199 -0
  273. package/dist/src/core/line-prefixer.d.ts +18 -0
  274. package/dist/src/core/line-prefixer.js +34 -0
  275. package/dist/src/core/mach.d.ts +7 -0
  276. package/dist/src/core/mach.js +32 -10
  277. package/dist/src/core/module-resolution-preflight.d.ts +19 -3
  278. package/dist/src/core/module-resolution-preflight.js +24 -6
  279. package/dist/src/core/negative-control-compiled.d.ts +27 -0
  280. package/dist/src/core/negative-control-compiled.js +73 -0
  281. package/dist/src/core/negative-control-diff.d.ts +51 -0
  282. package/dist/src/core/negative-control-diff.js +179 -0
  283. package/dist/src/core/negative-control.d.ts +150 -0
  284. package/dist/src/core/negative-control.js +399 -0
  285. package/dist/src/core/objdir-install-manifests.d.ts +73 -0
  286. package/dist/src/core/objdir-install-manifests.js +226 -0
  287. package/dist/src/core/park-store.d.ts +63 -0
  288. package/dist/src/core/park-store.js +216 -0
  289. package/dist/src/core/patch-export-update.d.ts +6 -0
  290. package/dist/src/core/patch-export-update.js +13 -3
  291. package/dist/src/core/patch-export.d.ts +3 -2
  292. package/dist/src/core/patch-export.js +22 -16
  293. package/dist/src/core/patch-files.js +26 -8
  294. package/dist/src/core/patch-lint-checkjs-any-import.d.ts +39 -0
  295. package/dist/src/core/patch-lint-checkjs-any-import.js +63 -0
  296. package/dist/src/core/patch-lint-checkjs-diagnostics.d.ts +17 -0
  297. package/dist/src/core/patch-lint-checkjs-diagnostics.js +32 -0
  298. package/dist/src/core/patch-lint-checkjs.d.ts +10 -1
  299. package/dist/src/core/patch-lint-checkjs.js +20 -13
  300. package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
  301. package/dist/src/core/patch-lint-code-lines.js +105 -0
  302. package/dist/src/core/patch-lint-cross.d.ts +3 -1
  303. package/dist/src/core/patch-lint-cross.js +17 -4
  304. package/dist/src/core/patch-lint-css.d.ts +8 -2
  305. package/dist/src/core/patch-lint-css.js +24 -14
  306. package/dist/src/core/patch-lint-file-size.d.ts +12 -5
  307. package/dist/src/core/patch-lint-file-size.js +22 -11
  308. package/dist/src/core/patch-lint-forward-registration-extended.d.ts +23 -0
  309. package/dist/src/core/patch-lint-forward-registration-extended.js +36 -11
  310. package/dist/src/core/patch-lint-forward-registration.js +10 -0
  311. package/dist/src/core/patch-lint-ignore.d.ts +196 -0
  312. package/dist/src/core/patch-lint-ignore.js +429 -0
  313. package/dist/src/core/patch-lint-ownership.d.ts +10 -0
  314. package/dist/src/core/patch-lint-ownership.js +26 -0
  315. package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
  316. package/dist/src/core/patch-lint-size-thresholds.js +110 -0
  317. package/dist/src/core/patch-lint-size.d.ts +117 -0
  318. package/dist/src/core/patch-lint-size.js +331 -0
  319. package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
  320. package/dist/src/core/patch-lint-staged-registration.js +20 -0
  321. package/dist/src/core/patch-lint-token-scale.d.ts +57 -0
  322. package/dist/src/core/patch-lint-token-scale.js +286 -0
  323. package/dist/src/core/patch-lint.d.ts +11 -83
  324. package/dist/src/core/patch-lint.js +66 -288
  325. package/dist/src/core/patch-lock.js +7 -6
  326. package/dist/src/core/patch-manifest-consistency.js +7 -6
  327. package/dist/src/core/patch-manifest-io.d.ts +6 -3
  328. package/dist/src/core/patch-manifest-io.js +27 -9
  329. package/dist/src/core/patch-manifest-key-order.d.ts +24 -0
  330. package/dist/src/core/patch-manifest-key-order.js +61 -0
  331. package/dist/src/core/patch-manifest-stage.d.ts +46 -0
  332. package/dist/src/core/patch-manifest-stage.js +96 -0
  333. package/dist/src/core/patch-manifest-validate.js +61 -3
  334. package/dist/src/core/patch-order-legal.d.ts +26 -0
  335. package/dist/src/core/patch-order-legal.js +88 -0
  336. package/dist/src/core/patch-policy-content.d.ts +40 -0
  337. package/dist/src/core/patch-policy-content.js +216 -0
  338. package/dist/src/core/patch-policy.d.ts +22 -5
  339. package/dist/src/core/patch-policy.js +26 -12
  340. package/dist/src/core/patch-retired.d.ts +57 -0
  341. package/dist/src/core/patch-retired.js +92 -0
  342. package/dist/src/core/patch-subset.d.ts +16 -0
  343. package/dist/src/core/patch-subset.js +66 -0
  344. package/dist/src/core/preflight-wait.d.ts +20 -0
  345. package/dist/src/core/preflight-wait.js +38 -0
  346. package/dist/src/core/rebase-from-version.d.ts +46 -0
  347. package/dist/src/core/rebase-from-version.js +71 -0
  348. package/dist/src/core/run-log.d.ts +26 -0
  349. package/dist/src/core/run-log.js +106 -4
  350. package/dist/src/core/run-profile.d.ts +7 -0
  351. package/dist/src/core/run-profile.js +7 -1
  352. package/dist/src/core/smoke-patterns.d.ts +5 -0
  353. package/dist/src/core/smoke-patterns.js +13 -0
  354. package/dist/src/core/test-harness-crash.d.ts +19 -1
  355. package/dist/src/core/test-harness-crash.js +8 -9
  356. package/dist/src/core/test-harness-signature.d.ts +33 -0
  357. package/dist/src/core/test-harness-signature.js +23 -1
  358. package/dist/src/core/test-raw-log.d.ts +66 -0
  359. package/dist/src/core/test-raw-log.js +122 -0
  360. package/dist/src/core/test-run-completeness.d.ts +90 -0
  361. package/dist/src/core/test-run-completeness.js +184 -0
  362. package/dist/src/core/test-stale-check.d.ts +27 -1
  363. package/dist/src/core/test-stale-check.js +39 -49
  364. package/dist/src/core/test-stale-exempt.d.ts +40 -0
  365. package/dist/src/core/test-stale-exempt.js +70 -0
  366. package/dist/src/core/test-xpcshell-retry.d.ts +2 -0
  367. package/dist/src/core/test-xpcshell-retry.js +2 -1
  368. package/dist/src/core/token-lint.d.ts +53 -0
  369. package/dist/src/core/token-lint.js +131 -0
  370. package/dist/src/core/token-sibling-sheets.d.ts +46 -0
  371. package/dist/src/core/token-sibling-sheets.js +119 -0
  372. package/dist/src/core/tree-guard.js +5 -0
  373. package/dist/src/core/typecheck-shim.d.ts +6 -2
  374. package/dist/src/core/typecheck-shim.js +205 -4
  375. package/dist/src/core/typecheck.js +3 -1
  376. package/dist/src/index.d.ts +14 -3
  377. package/dist/src/index.js +6 -1
  378. package/dist/src/types/commands/index.d.ts +2 -2
  379. package/dist/src/types/commands/options.d.ts +212 -24
  380. package/dist/src/types/commands/patches.d.ts +118 -1
  381. package/dist/src/types/config.d.ts +264 -0
  382. package/dist/src/types/config.js +9 -0
  383. package/dist/src/types/furnace.d.ts +63 -1
  384. package/dist/src/types/furnace.js +7 -0
  385. package/dist/src/types/index.d.ts +1 -1
  386. package/dist/src/utils/options.d.ts +36 -7
  387. package/dist/src/utils/options.js +94 -8
  388. package/dist/src/utils/path-glob.d.ts +15 -0
  389. package/dist/src/utils/path-glob.js +47 -0
  390. package/dist/src/utils/regex.d.ts +15 -3
  391. package/dist/src/utils/regex.js +37 -16
  392. package/docs/configuration.md +369 -11
  393. package/docs/exit-codes.md +18 -1
  394. package/docs/furnace.md +139 -3
  395. package/docs/lifecycle-invariants.md +65 -30
  396. package/docs/machine-output.md +120 -12
  397. package/docs/patch-workflow.md +458 -22
  398. package/docs/run-logs.md +20 -1
  399. package/docs/testing.md +346 -21
  400. package/package.json +3 -3
@@ -0,0 +1,429 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * `PatchMetadata.lintIgnore` entries and the waiver filter they drive.
4
+ *
5
+ * An entry is either a bare check ID, which suppresses that rule at any
6
+ * size, or a ceiling `{ rule, max, reason }`, which suppresses a size rule
7
+ * only while its measurement is at or below `max`. The bare form let a
8
+ * waived file grow without bound: nothing re-examined the waiver once it
9
+ * was written, so a 950-line file waived at review time was still waived at
10
+ * 2,000 lines. The ceiling makes growth past the reviewed size fire again,
11
+ * and lets a gate say when the waiver is no longer needed at all.
12
+ *
13
+ * Its own module so every caller that turns a manifest list into a filter
14
+ * (export, re-export, per-patch and aggregate lint, split, move-files)
15
+ * shares one reading of an entry, and so `patch-lint.ts` only calls into it.
16
+ */
17
+ /**
18
+ * The size rules whose findings carry a `measurement`, and therefore the
19
+ * only rules a ceiling can bound. A ceiling on any other rule could never
20
+ * suppress anything, so the manifest validator refuses it.
21
+ */
22
+ export const LINT_IGNORE_CEILING_RULES = [
23
+ 'file-too-large',
24
+ 'large-patch-lines',
25
+ 'large-patch-files',
26
+ ];
27
+ /** The only rule whose findings carry a {@link PatchSizeShare}. */
28
+ const SHARE_RULE = 'large-patch-lines';
29
+ const SHARES = ['product', 'test'];
30
+ /**
31
+ * The key a ceiling is stored under: the rule, plus `#<share>` for a
32
+ * share-scoped ceiling, so one rule can carry a ceiling per share.
33
+ */
34
+ function ceilingKey(rule, share) {
35
+ return share === undefined ? rule : `${rule}#${share}`;
36
+ }
37
+ /** Narrows an entry to the ceiling form. */
38
+ export function isLintIgnoreCeiling(entry) {
39
+ return typeof entry !== 'string';
40
+ }
41
+ /** The check ID an entry names, whatever its form. */
42
+ export function lintIgnoreRule(entry) {
43
+ return isLintIgnoreCeiling(entry) ? entry.rule : entry;
44
+ }
45
+ /**
46
+ * One-word rendering for summaries and dry runs: `file-too-large<=1200`,
47
+ * `large-patch-lines[test]<=900`.
48
+ */
49
+ export function formatLintIgnoreEntry(entry) {
50
+ if (!isLintIgnoreCeiling(entry))
51
+ return entry;
52
+ const share = entry.share === undefined ? '' : `[${entry.share}]`;
53
+ return `${entry.rule}${share}<=${String(entry.max)}`;
54
+ }
55
+ /** The identity an entry is upserted and deduplicated by. */
56
+ function entryKey(entry) {
57
+ return isLintIgnoreCeiling(entry) ? ceilingKey(entry.rule, entry.share) : entry;
58
+ }
59
+ /** Renders a list for messages, `(empty)` when there is nothing to show. */
60
+ export function formatLintIgnoreList(entries) {
61
+ return entries.length > 0 ? entries.map(formatLintIgnoreEntry).join(', ') : '(empty)';
62
+ }
63
+ function sameEntry(a, b) {
64
+ if (!isLintIgnoreCeiling(a) || !isLintIgnoreCeiling(b))
65
+ return a === b;
66
+ return (a.rule === b.rule &&
67
+ a.max === b.max &&
68
+ a.reason === b.reason &&
69
+ a.share === b.share &&
70
+ JSON.stringify(a.meta) === JSON.stringify(b.meta));
71
+ }
72
+ /**
73
+ * Validates a raw `lintIgnore` value from `patches.json`.
74
+ *
75
+ * Bare strings keep their historical leniency (any ID, duplicates collapse
76
+ * later). A ceiling must name a measured size rule, carry a positive
77
+ * integer `max` and a non-empty `reason`, and must be the only entry for
78
+ * its rule: a bare ID beside a ceiling for the same rule would make the
79
+ * ceiling dead, and two ceilings would leave the effective one ambiguous.
80
+ * The one exception is `large-patch-lines`, which may carry one ceiling
81
+ * per `share` instead of a single share-less one. `meta` must be a JSON
82
+ * object and is kept verbatim.
83
+ *
84
+ * @param raw - The value under `lintIgnore`
85
+ * @param label - Field label for error messages, e.g. `patches[3].lintIgnore`
86
+ * @returns The validated entries
87
+ * @throws Error naming the offending element
88
+ */
89
+ export function parseLintIgnoreEntries(raw, label) {
90
+ if (!Array.isArray(raw)) {
91
+ throw new Error(`${label} must be an array`);
92
+ }
93
+ const entries = [];
94
+ const ceilingRules = new Set();
95
+ for (const [index, item] of raw.entries()) {
96
+ const itemLabel = `${label}[${String(index)}]`;
97
+ if (typeof item === 'string') {
98
+ entries.push(item);
99
+ continue;
100
+ }
101
+ entries.push(parseCeiling(item, itemLabel));
102
+ }
103
+ const sharedRules = new Set();
104
+ for (const entry of entries) {
105
+ if (!isLintIgnoreCeiling(entry))
106
+ continue;
107
+ const key = ceilingKey(entry.rule, entry.share);
108
+ const clash = ceilingRules.has(key) ||
109
+ entries.includes(entry.rule) ||
110
+ (entry.share === undefined ? sharedRules.has(entry.rule) : ceilingRules.has(entry.rule));
111
+ if (clash) {
112
+ throw new Error(`${label} names "${entry.rule}" more than once; keep a single entry (bare or ceiling) ` +
113
+ 'per rule, or one ceiling per share');
114
+ }
115
+ ceilingRules.add(key);
116
+ if (entry.share !== undefined)
117
+ sharedRules.add(entry.rule);
118
+ }
119
+ return entries;
120
+ }
121
+ function parseCeiling(item, label) {
122
+ if (item === null || typeof item !== 'object' || Array.isArray(item)) {
123
+ throw new Error(`${label} must be a check ID string or a { rule, max, reason } object`);
124
+ }
125
+ const record = item;
126
+ const unknownKeys = Object.keys(record).filter((key) => !CEILING_KEYS.has(key));
127
+ if (unknownKeys.length > 0) {
128
+ throw new Error(`${label} has unknown field(s): ${unknownKeys.join(', ')}`);
129
+ }
130
+ const { rule, max, reason, share, meta } = record;
131
+ if (typeof rule !== 'string' || !LINT_IGNORE_CEILING_RULES.includes(rule)) {
132
+ throw new Error(`${label}.rule must be one of the measured size rules: ${LINT_IGNORE_CEILING_RULES.join(', ')}`);
133
+ }
134
+ if (typeof max !== 'number' || !Number.isInteger(max) || max <= 0) {
135
+ throw new Error(`${label}.max must be a positive integer`);
136
+ }
137
+ if (typeof reason !== 'string' || reason.trim() === '') {
138
+ throw new Error(`${label}.reason must be a non-empty string`);
139
+ }
140
+ const ceiling = { rule, max, reason };
141
+ if (share !== undefined) {
142
+ if (rule !== SHARE_RULE || !SHARES.includes(share)) {
143
+ throw new Error(`${label}.share must be "product" or "test", and only on ${SHARE_RULE}`);
144
+ }
145
+ ceiling.share = share;
146
+ }
147
+ if (meta !== undefined) {
148
+ if (meta === null || typeof meta !== 'object' || Array.isArray(meta)) {
149
+ throw new Error(`${label}.meta must be a JSON object`);
150
+ }
151
+ ceiling.meta = meta;
152
+ }
153
+ return ceiling;
154
+ }
155
+ const CEILING_KEYS = new Set(['rule', 'max', 'reason', 'share', 'meta']);
156
+ /**
157
+ * Builds the waiver filter for a patch's entries.
158
+ *
159
+ * @param entries - `PatchMetadata.lintIgnore`, possibly merged with CLI flags
160
+ * @returns The filter, or undefined when there is nothing to waive
161
+ */
162
+ export function buildLintIgnoreChecks(entries) {
163
+ if (entries === undefined || entries.length === 0)
164
+ return undefined;
165
+ const plain = new Set();
166
+ const ceilings = new Map();
167
+ for (const entry of entries) {
168
+ if (isLintIgnoreCeiling(entry))
169
+ ceilings.set(ceilingKey(entry.rule, entry.share), entry);
170
+ else
171
+ plain.add(entry);
172
+ }
173
+ return ceilings.size > 0 ? Object.assign(plain, { ceilings }) : plain;
174
+ }
175
+ /**
176
+ * Canonical string form of a filter for the per-patch lint cache's waiver
177
+ * comparison: bare IDs as themselves, ceilings as `rule<=max`.
178
+ */
179
+ export function lintIgnoreCacheTokens(checks) {
180
+ if (checks === undefined)
181
+ return [];
182
+ const ceilings = [...(checks.ceilings?.values() ?? [])].map(formatLintIgnoreEntry);
183
+ return [...checks, ...ceilings];
184
+ }
185
+ /**
186
+ * Unions CLI-flag check IDs into an existing list, the append-only
187
+ * semantics `export` / `re-export --lint-ignore` have always had. A bare ID
188
+ * whose rule already has an entry (bare or ceiling) is a no-op: widening a
189
+ * reviewed ceiling to an unbounded waiver is `patch lint-ignore`'s job, where
190
+ * it is confirmed explicitly. Ceilings deduplicate by rule and share, so a
191
+ * rule's per-share ceilings all survive.
192
+ *
193
+ * @returns The merged list, or undefined when it is empty
194
+ */
195
+ export function mergeLintIgnoreIds(existing, ids) {
196
+ const merged = [];
197
+ const keys = new Set();
198
+ const rules = new Set();
199
+ for (const entry of [...(existing ?? []), ...(ids ?? [])]) {
200
+ const blocked = isLintIgnoreCeiling(entry) ? keys.has(entryKey(entry)) : rules.has(entry);
201
+ if (blocked)
202
+ continue;
203
+ keys.add(entryKey(entry));
204
+ rules.add(lintIgnoreRule(entry));
205
+ merged.push(entry);
206
+ }
207
+ return merged.length > 0 ? merged : undefined;
208
+ }
209
+ /**
210
+ * Adds or replaces entries, the `patch lint-ignore --add` semantics: an
211
+ * explicit `--add <rule>` turns a rule's ceilings into a bare waiver,
212
+ * `--add <rule> --max <n>` sets or moves the rule's ceiling, and a
213
+ * share-scoped ceiling replaces only its own share (plus any bare or
214
+ * share-less entry for the rule, which it would contradict).
215
+ *
216
+ * @returns The new list, or undefined when it is empty
217
+ */
218
+ export function upsertLintIgnoreEntries(existing, additions) {
219
+ let next = [...existing];
220
+ for (const addition of additions) {
221
+ const rule = lintIgnoreRule(addition);
222
+ const scoped = isLintIgnoreCeiling(addition) && addition.share !== undefined;
223
+ const replaced = (entry) => lintIgnoreRule(entry) === rule &&
224
+ (!scoped ||
225
+ !isLintIgnoreCeiling(entry) ||
226
+ entry.share === undefined ||
227
+ entryKey(entry) === entryKey(addition));
228
+ const index = next.findIndex(replaced);
229
+ const kept = next.filter((entry) => !replaced(entry));
230
+ // The addition takes the first replaced entry's place, so a moved
231
+ // ceiling keeps its position in the list.
232
+ next =
233
+ index === -1
234
+ ? [...kept, addition]
235
+ : [...kept.slice(0, index), addition, ...kept.slice(index)];
236
+ }
237
+ return mergeLintIgnoreIds(next, undefined);
238
+ }
239
+ /** Removes every entry naming one of `rules`. Undefined when nothing is left. */
240
+ export function removeLintIgnoreRules(existing, rules) {
241
+ const drop = new Set(rules);
242
+ const remaining = existing.filter((entry) => !drop.has(lintIgnoreRule(entry)));
243
+ return remaining.length > 0 ? remaining : undefined;
244
+ }
245
+ /** Entries present in `after` but not (identically) in `before`. */
246
+ export function addedLintIgnoreEntries(before, after) {
247
+ return after.filter((entry) => !before.some((prior) => sameEntry(prior, entry)));
248
+ }
249
+ /**
250
+ * Splits issues into the ones that still surface and the ones a waiver
251
+ * drops.
252
+ *
253
+ * A bare ID drops every finding of its rule. A ceiling drops a finding only
254
+ * when it carries a measurement at or below `max`. Past the ceiling the
255
+ * finding surfaces at its own severity with the ceiling named, and a
256
+ * finding with no measurement surfaces too (fail closed).
257
+ */
258
+ export function partitionLintIgnoredIssues(issues, checks) {
259
+ const kept = [];
260
+ const suppressed = [];
261
+ for (const issue of issues) {
262
+ if (checks?.has(issue.check) === true) {
263
+ suppressed.push(issue);
264
+ continue;
265
+ }
266
+ const ceiling = ceilingFor(issue, checks);
267
+ if (ceiling === undefined) {
268
+ kept.push(issue);
269
+ continue;
270
+ }
271
+ if (issue.measurement !== undefined && issue.measurement <= ceiling.max) {
272
+ suppressed.push(issue);
273
+ continue;
274
+ }
275
+ // A notice never gates, so a ceiling does not bound it: one past `max`
276
+ // surfaces as the plain notice it is, not as an exceeded waiver.
277
+ if (issue.severity === 'notice') {
278
+ kept.push(issue);
279
+ continue;
280
+ }
281
+ kept.push({ ...issue, message: `${issue.message} ${describeExceededCeiling(ceiling)}` });
282
+ }
283
+ return { kept, suppressed };
284
+ }
285
+ /** The ceiling that governs `issue`: its share's, else the rule's share-less one. */
286
+ function ceilingFor(issue, checks) {
287
+ const ceilings = checks?.ceilings;
288
+ if (ceilings === undefined)
289
+ return undefined;
290
+ if (issue.share !== undefined) {
291
+ const scoped = ceilings.get(ceilingKey(issue.check, issue.share));
292
+ if (scoped !== undefined)
293
+ return scoped;
294
+ }
295
+ return ceilings.get(issue.check);
296
+ }
297
+ function describeExceededCeiling(ceiling) {
298
+ const share = ceiling.share === undefined ? '' : ` (${ceiling.share} share)`;
299
+ return (`This exceeds the patch's lintIgnore ceiling for ${ceiling.rule}${share} ` +
300
+ `(max ${String(ceiling.max)}: ${ceiling.reason}).`);
301
+ }
302
+ /** True when `checks` waives `issue` (see {@link partitionLintIgnoredIssues}). */
303
+ export function isLintIgnored(issue, checks) {
304
+ return partitionLintIgnoredIssues([issue], checks).suppressed.length === 1;
305
+ }
306
+ /**
307
+ * Drops issues that a patch owning `issue.file` (via `filesAffected`)
308
+ * waives. Used where findings carry no patch scope of their own (aggregate
309
+ * `lint`). Each owner keeps its own filter, so a ceiling stays bounded by
310
+ * the patch that declared it instead of merging into a file-wide union; an
311
+ * issue is dropped when any owner waives it.
312
+ */
313
+ export function filterLintIgnoredByOwners(issues, owners) {
314
+ const byFile = new Map();
315
+ for (const owner of owners) {
316
+ const checks = buildLintIgnoreChecks(owner?.lintIgnore);
317
+ if (!checks)
318
+ continue;
319
+ for (const file of owner?.filesAffected ?? []) {
320
+ byFile.set(file, [...(byFile.get(file) ?? []), checks]);
321
+ }
322
+ }
323
+ if (byFile.size === 0)
324
+ return issues;
325
+ return issues.filter((issue) => !(byFile.get(issue.file) ?? []).some((checks) => isLintIgnored(issue, checks)));
326
+ }
327
+ function judgeWaiver(rule, max, share, issues, patchMeasurements) {
328
+ const findings = issues.filter((issue) => issue.check === rule && (share === undefined || issue.share === share));
329
+ const measured = findings
330
+ .map((issue) => issue.measurement)
331
+ .filter((value) => value !== undefined);
332
+ // A share-scoped ceiling has no patch-level fallback: the total counts
333
+ // both shares, which is not what it bounds.
334
+ const fallback = share !== undefined
335
+ ? null
336
+ : rule === 'large-patch-lines'
337
+ ? patchMeasurements.lines
338
+ : rule === 'large-patch-files'
339
+ ? patchMeasurements.files
340
+ : null;
341
+ const measurement = measured.length > 0 ? Math.max(...measured) : fallback;
342
+ const gating = findings.filter((issue) => issue.severity !== 'notice');
343
+ const exceeded = max !== null &&
344
+ gating.some((issue) => issue.measurement !== undefined && issue.measurement > max);
345
+ const state = exceeded
346
+ ? 'exceeded'
347
+ : gating.length > 0
348
+ ? 'within'
349
+ : 'removable';
350
+ return { measurement, state };
351
+ }
352
+ /**
353
+ * Evaluates each ceiling on a patch against the findings its lint produced
354
+ * (both surfaced and suppressed).
355
+ *
356
+ * @param entries - The patch's `lintIgnore`
357
+ * @param issues - Every finding of the patch's lint, surfaced and suppressed
358
+ * @param patchMeasurements - Patch-level measurements used when a
359
+ * patch-size rule did not fire at all, so the report still shows the
360
+ * current number
361
+ */
362
+ export function evaluateLintIgnoreCeilings(entries, issues, patchMeasurements) {
363
+ const statuses = [];
364
+ for (const entry of entries ?? []) {
365
+ if (!isLintIgnoreCeiling(entry))
366
+ continue;
367
+ statuses.push({
368
+ rule: entry.rule,
369
+ max: entry.max,
370
+ reason: entry.reason,
371
+ ...(entry.share !== undefined ? { share: entry.share } : {}),
372
+ ...(entry.meta !== undefined ? { meta: entry.meta } : {}),
373
+ ...judgeWaiver(entry.rule, entry.max, entry.share, issues, patchMeasurements),
374
+ });
375
+ }
376
+ return statuses;
377
+ }
378
+ /**
379
+ * Evaluates every waiver on a patch, bare IDs included, so a waiver audit
380
+ * reads each one's current measurement from the report instead of
381
+ * re-measuring bare waivers from `suppressedIssues`.
382
+ *
383
+ * @param entries - The patch's `lintIgnore`
384
+ * @param issues - Every finding of the patch's lint, surfaced and suppressed
385
+ * @param patchMeasurements - See {@link evaluateLintIgnoreCeilings}
386
+ */
387
+ export function evaluateLintIgnoreWaivers(entries, issues, patchMeasurements) {
388
+ return (entries ?? []).map((entry) => {
389
+ if (!isLintIgnoreCeiling(entry)) {
390
+ return {
391
+ kind: 'bare',
392
+ rule: entry,
393
+ max: null,
394
+ reason: null,
395
+ ...judgeWaiver(entry, null, undefined, issues, patchMeasurements),
396
+ };
397
+ }
398
+ return {
399
+ kind: 'ceiling',
400
+ rule: entry.rule,
401
+ max: entry.max,
402
+ reason: entry.reason,
403
+ ...(entry.share !== undefined ? { share: entry.share } : {}),
404
+ ...(entry.meta !== undefined ? { meta: entry.meta } : {}),
405
+ ...judgeWaiver(entry.rule, entry.max, entry.share, issues, patchMeasurements),
406
+ };
407
+ });
408
+ }
409
+ /**
410
+ * Notice-severity findings for ceilings that are no longer needed, so the
411
+ * waiver's removal is prompted by the tool that enforces it rather than
412
+ * discovered by hand.
413
+ */
414
+ export function removableCeilingIssues(patchFilename, statuses) {
415
+ return statuses
416
+ .filter((status) => status.state === 'removable')
417
+ .map((status) => {
418
+ const now = status.measurement === null ? 'below its notice band' : `at ${String(status.measurement)}`;
419
+ const share = status.share === undefined ? '' : `, ${status.share} share`;
420
+ return {
421
+ file: patchFilename,
422
+ check: 'lint-ignore-removable',
423
+ message: `lintIgnore ceiling for ${status.rule} (max ${String(status.max)}${share}) is no longer needed: ` +
424
+ `the measurement is ${now}, under the rule's own warning threshold. ` +
425
+ `Remove it with "fireforge patch lint-ignore ${patchFilename} --remove ${status.rule}".`,
426
+ severity: 'notice',
427
+ };
428
+ });
429
+ }
@@ -51,6 +51,16 @@ export declare function resolvePatchOwnedChromeScripts(currentNewFiles: Set<stri
51
51
  * `patch-lint-ownership.test.ts`.
52
52
  */
53
53
  export declare function isTestScriptFile(file: string): boolean;
54
+ /**
55
+ * For each test script, the ES modules (`.mjs`, `.sys.mjs`) the queue
56
+ * creates up to and including the test's own patch: what its imports can
57
+ * resolve to in a per-test checkJs program. A test no patch touches sees
58
+ * every module the queue creates.
59
+ *
60
+ * @param patchQueueCtx - The queue, in apply order
61
+ * @returns Lookup from a test script path to its resolvable modules
62
+ */
63
+ export declare function resolvableModulesForTests(patchQueueCtx: PatchQueueContext): (testFile: string) => ReadonlySet<string>;
54
64
  /**
55
65
  * Returns the set of patch-owned test `.js` files. Same
56
66
  * ownership semantics as {@link resolvePatchOwnedSysMjs}. Consumed by the
@@ -84,6 +84,32 @@ export function isTestScriptFile(file) {
84
84
  const basename = file.split('/').pop() ?? '';
85
85
  return /^(?:browser_|test_|xpcshell_).*\.js$/.test(basename);
86
86
  }
87
+ /**
88
+ * For each test script, the ES modules (`.mjs`, `.sys.mjs`) the queue
89
+ * creates up to and including the test's own patch: what its imports can
90
+ * resolve to in a per-test checkJs program. A test no patch touches sees
91
+ * every module the queue creates.
92
+ *
93
+ * @param patchQueueCtx - The queue, in apply order
94
+ * @returns Lookup from a test script path to its resolvable modules
95
+ */
96
+ export function resolvableModulesForTests(patchQueueCtx) {
97
+ const byTest = new Map();
98
+ const upTo = new Set();
99
+ for (const entry of patchQueueCtx.entries) {
100
+ for (const file of entry.newFiles.keys()) {
101
+ if (file.endsWith('.mjs') && !isTestScriptFile(file))
102
+ upTo.add(file);
103
+ }
104
+ const snapshot = new Set(upTo);
105
+ for (const file of [...entry.newFiles.keys(), ...entry.modifiedFileAdditions.keys()]) {
106
+ if (isTestScriptFile(file) && !byTest.has(file))
107
+ byTest.set(file, snapshot);
108
+ }
109
+ }
110
+ const all = upTo;
111
+ return (testFile) => byTest.get(testFile) ?? all;
112
+ }
87
113
  /**
88
114
  * Returns the set of patch-owned test `.js` files. Same
89
115
  * ownership semantics as {@link resolvePatchOwnedSysMjs}. Consumed by the
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The patch-size thresholds (`large-patch-lines`, `large-patch-files`) and
3
+ * their merge with `patchLint.patchSizeThresholds`.
4
+ *
5
+ * Its own dependency-free module for the same reason as
6
+ * `patch-lint-file-size.ts`: the config validator checks an override
7
+ * against the merged result, and importing the rule bodies from config
8
+ * parsing would draw an import cycle.
9
+ */
10
+ import type { PatchLintPatchSizeThresholds } from '../types/config.js';
11
+ import type { ResolvedFileSizeTier } from './patch-lint-file-size.js';
12
+ /** The tiers a patch is measured against. */
13
+ export type PatchSizeTierName = 'general' | 'test' | 'branding';
14
+ /**
15
+ * A resolved `large-patch-files` triple. `error` is `null` when no error
16
+ * band is configured. `notice` equal to `warning` means no notice band.
17
+ */
18
+ export interface ResolvedPatchFilesTier {
19
+ notice: number;
20
+ warning: number;
21
+ error: number | null;
22
+ }
23
+ /** Line and file-count thresholds governing one patch-size tier. */
24
+ export interface PatchSizeThresholds {
25
+ /** Line counts at which `large-patch-lines` notice/warning/error issues fire. */
26
+ lines: ResolvedFileSizeTier;
27
+ /** File counts at which `large-patch-files` notice/warning/error issues fire. */
28
+ files: ResolvedPatchFilesTier;
29
+ /**
30
+ * The `files.warning` threshold, kept for consumers written before the
31
+ * files triple existed.
32
+ */
33
+ maxFiles: number;
34
+ }
35
+ /**
36
+ * Merges the configured overrides over the defaults for one tier.
37
+ *
38
+ * @param tier - The tier to resolve
39
+ * @param overrides - The `patchLint.patchSizeThresholds` block, if any
40
+ * @returns The effective thresholds
41
+ */
42
+ export declare function resolvePatchSizeThresholds(tier: PatchSizeTierName, overrides: PatchLintPatchSizeThresholds | undefined): PatchSizeThresholds;
43
+ /**
44
+ * True when `file` matches one of the `patchLint.patchSizeRegistrationFiles`
45
+ * globs. A glob without `/` matches the basename, one with `/` the whole
46
+ * engine-relative path. `*` and `?` stay within a segment, `**` crosses
47
+ * segments.
48
+ *
49
+ * @param file - Engine-relative POSIX path
50
+ * @param globs - The configured globs
51
+ */
52
+ export declare function isPatchSizeRegistrationFile(file: string, globs: readonly string[]): boolean;
@@ -0,0 +1,110 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * The patch-size thresholds (`large-patch-lines`, `large-patch-files`) and
4
+ * their merge with `patchLint.patchSizeThresholds`.
5
+ *
6
+ * Its own dependency-free module for the same reason as
7
+ * `patch-lint-file-size.ts`: the config validator checks an override
8
+ * against the merged result, and importing the rule bodies from config
9
+ * parsing would draw an import cycle.
10
+ */
11
+ import { escapeRegex } from '../utils/regex.js';
12
+ /**
13
+ * Built-in line-count bands per tier.
14
+ *
15
+ * Branding patches have a legitimate reason to be large: they include
16
+ * every locale's `brand.ftl`, copied upstream CSS/PNG assets, and the
17
+ * fork-specific `configure.sh` / `brand.properties` under a single
18
+ * `browser/branding/<name>/` subtree. The tier is calibrated against a
19
+ * fresh-fork branding export, which lands around 15-16k lines (localized
20
+ * brand.ftl across many locales + SVG path data + copied upstream CSS).
21
+ * That is the minimum branding diff, so it must surface as a `notice`
22
+ * rather than a `warning`: the warning band sits above it with ~13%
23
+ * headroom, and the error band at roughly 2× the baseline, where
24
+ * non-branding work is probably bundled in.
25
+ *
26
+ * Permissive thresholds are safe because the *gate* into this tier is
27
+ * narrow: auto-detect requires every file under `browser/branding/` plus a
28
+ * tight registration allowlist, or an explicit `PatchMetadata.tier:
29
+ * "branding"` opt-in. A non-branding patch cannot accidentally land here.
30
+ */
31
+ const DEFAULT_PATCH_LINE_THRESHOLDS = {
32
+ general: { notice: 800, warning: 1500, error: 3000 },
33
+ test: { notice: 1500, warning: 3000, error: 6000 },
34
+ branding: { notice: 8000, warning: 18000, error: 30000 },
35
+ };
36
+ /**
37
+ * Built-in `large-patch-files` warning band per tier. There is no notice or
38
+ * error band by default: file count expresses scope, not blast radius.
39
+ *
40
+ * The branding tier sits well above the typical floor because branding
41
+ * patches inherently span many files: PNG/ICO icon assets in 7+ sizes, MSIX
42
+ * manifests, channel-specific configs, locale `.ftl` files, Windows/macOS
43
+ * launcher resources. A fresh-fork branding bundle is around 56 files, so 60
44
+ * leaves headroom for additional channels/locales while still firing on a
45
+ * genuinely bloated patch. The test tier matches general because a
46
+ * test-only patch rarely touches many files.
47
+ */
48
+ const DEFAULT_PATCH_FILES_WARNING = {
49
+ general: 5,
50
+ test: 5,
51
+ branding: 60,
52
+ };
53
+ /**
54
+ * Merges the configured overrides over the defaults for one tier.
55
+ *
56
+ * @param tier - The tier to resolve
57
+ * @param overrides - The `patchLint.patchSizeThresholds` block, if any
58
+ * @returns The effective thresholds
59
+ */
60
+ export function resolvePatchSizeThresholds(tier, overrides) {
61
+ const configured = overrides?.[tier];
62
+ const lines = { ...DEFAULT_PATCH_LINE_THRESHOLDS[tier], ...configured?.lines };
63
+ const files = resolveFilesTier(DEFAULT_PATCH_FILES_WARNING[tier], configured?.files);
64
+ return { lines, files, maxFiles: files.warning };
65
+ }
66
+ function resolveFilesTier(defaultWarning, configured) {
67
+ const warning = configured?.warning ?? defaultWarning;
68
+ return {
69
+ notice: configured?.notice ?? warning,
70
+ warning,
71
+ error: configured?.error ?? null,
72
+ };
73
+ }
74
+ /**
75
+ * True when `file` matches one of the `patchLint.patchSizeRegistrationFiles`
76
+ * globs. A glob without `/` matches the basename, one with `/` the whole
77
+ * engine-relative path. `*` and `?` stay within a segment, `**` crosses
78
+ * segments.
79
+ *
80
+ * @param file - Engine-relative POSIX path
81
+ * @param globs - The configured globs
82
+ */
83
+ export function isPatchSizeRegistrationFile(file, globs) {
84
+ if (globs.length === 0)
85
+ return false;
86
+ const basename = file.slice(file.lastIndexOf('/') + 1);
87
+ return globs.some((glob) => globToRegExp(glob).test(glob.includes('/') ? file : basename));
88
+ }
89
+ function globToRegExp(glob) {
90
+ let source = '';
91
+ for (let i = 0; i < glob.length; i++) {
92
+ const char = glob.charAt(i);
93
+ if (char === '*' && glob.charAt(i + 1) === '*') {
94
+ // `**/` matches zero or more whole segments, a bare `**` anything.
95
+ const slash = glob.charAt(i + 2) === '/';
96
+ source += slash ? '(?:.*/)?' : '.*';
97
+ i += slash ? 2 : 1;
98
+ }
99
+ else if (char === '*') {
100
+ source += '[^/]*';
101
+ }
102
+ else if (char === '?') {
103
+ source += '[^/]';
104
+ }
105
+ else {
106
+ source += escapeRegex(char);
107
+ }
108
+ }
109
+ return new RegExp(`^${source}$`);
110
+ }