@hominis/fireforge 0.49.1 → 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 (336) hide show
  1. package/CHANGELOG.md +41 -0
  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-templates.d.ts +8 -3
  35. package/dist/src/commands/furnace/create-templates.js +8 -23
  36. package/dist/src/commands/furnace/create.js +2 -2
  37. package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
  38. package/dist/src/commands/furnace/deploy-named.js +107 -0
  39. package/dist/src/commands/furnace/deploy.d.ts +3 -2
  40. package/dist/src/commands/furnace/deploy.js +50 -86
  41. package/dist/src/commands/furnace/diff.js +26 -3
  42. package/dist/src/commands/furnace/index.js +66 -41
  43. package/dist/src/commands/furnace/init.js +13 -6
  44. package/dist/src/commands/furnace/override.js +4 -2
  45. package/dist/src/commands/furnace/preview.js +4 -3
  46. package/dist/src/commands/furnace/refresh.js +2 -2
  47. package/dist/src/commands/furnace/remove.js +4 -3
  48. package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
  49. package/dist/src/commands/furnace/rename-dry-run.js +127 -0
  50. package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
  51. package/dist/src/commands/furnace/rename-helpers.js +15 -1
  52. package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
  53. package/dist/src/commands/furnace/rename-registrations.js +93 -0
  54. package/dist/src/commands/furnace/rename.d.ts +2 -1
  55. package/dist/src/commands/furnace/rename.js +28 -66
  56. package/dist/src/commands/furnace/scan.d.ts +9 -4
  57. package/dist/src/commands/furnace/scan.js +11 -9
  58. package/dist/src/commands/furnace/sync.js +3 -2
  59. package/dist/src/commands/furnace/validate-report.d.ts +45 -0
  60. package/dist/src/commands/furnace/validate-report.js +91 -0
  61. package/dist/src/commands/furnace/validate.d.ts +1 -1
  62. package/dist/src/commands/furnace/validate.js +127 -114
  63. package/dist/src/commands/furnace/validation-output.js +14 -3
  64. package/dist/src/commands/lint-per-patch.js +31 -60
  65. package/dist/src/commands/lint-report.d.ts +8 -4
  66. package/dist/src/commands/lint-report.js +15 -5
  67. package/dist/src/commands/lint.js +15 -34
  68. package/dist/src/commands/manifest.js +2 -0
  69. package/dist/src/commands/patch/delete-gap-notice.js +3 -1
  70. package/dist/src/commands/patch/delete.d.ts +1 -1
  71. package/dist/src/commands/patch/delete.js +13 -98
  72. package/dist/src/commands/patch/index.d.ts +1 -1
  73. package/dist/src/commands/patch/index.js +4 -2
  74. package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
  75. package/dist/src/commands/patch/lint-ignore.js +65 -28
  76. package/dist/src/commands/patch/move-files-create.js +26 -17
  77. package/dist/src/commands/patch/move-files-into.js +5 -3
  78. package/dist/src/commands/patch/move-files.js +3 -1
  79. package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
  80. package/dist/src/commands/patch/removal-dependents.js +133 -0
  81. package/dist/src/commands/patch/rename.js +7 -0
  82. package/dist/src/commands/patch/retire.d.ts +34 -0
  83. package/dist/src/commands/patch/retire.js +128 -0
  84. package/dist/src/commands/patch/split-plan.d.ts +7 -7
  85. package/dist/src/commands/patch/split-plan.js +45 -28
  86. package/dist/src/commands/patch/split-staged.d.ts +108 -0
  87. package/dist/src/commands/patch/split-staged.js +202 -0
  88. package/dist/src/commands/patch/split.d.ts +5 -0
  89. package/dist/src/commands/patch/split.js +51 -15
  90. package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
  91. package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
  92. package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
  93. package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
  94. package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
  95. package/dist/src/commands/patch/staged-dependency-from.js +108 -0
  96. package/dist/src/commands/patch/staged-dependency.js +14 -41
  97. package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
  98. package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
  99. package/dist/src/commands/re-export-adjacent.d.ts +45 -2
  100. package/dist/src/commands/re-export-adjacent.js +156 -37
  101. package/dist/src/commands/re-export-drift.d.ts +36 -8
  102. package/dist/src/commands/re-export-drift.js +73 -49
  103. package/dist/src/commands/re-export-files.js +11 -8
  104. package/dist/src/commands/re-export-lint.d.ts +2 -1
  105. package/dist/src/commands/re-export-lint.js +23 -10
  106. package/dist/src/commands/re-export-options.js +45 -4
  107. package/dist/src/commands/re-export-preflight.d.ts +23 -0
  108. package/dist/src/commands/re-export-preflight.js +160 -0
  109. package/dist/src/commands/re-export-register.js +42 -17
  110. package/dist/src/commands/re-export-removed.d.ts +12 -1
  111. package/dist/src/commands/re-export-removed.js +41 -11
  112. package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
  113. package/dist/src/commands/re-export-scan-owner.js +116 -0
  114. package/dist/src/commands/re-export-scan.d.ts +17 -9
  115. package/dist/src/commands/re-export-scan.js +56 -23
  116. package/dist/src/commands/re-export-single.js +22 -6
  117. package/dist/src/commands/re-export-summary.d.ts +28 -0
  118. package/dist/src/commands/re-export-summary.js +36 -0
  119. package/dist/src/commands/re-export.js +37 -24
  120. package/dist/src/commands/rebase/index.js +17 -17
  121. package/dist/src/commands/run.js +16 -2
  122. package/dist/src/commands/status-check.d.ts +55 -16
  123. package/dist/src/commands/status-check.js +92 -37
  124. package/dist/src/commands/status-json.d.ts +22 -9
  125. package/dist/src/commands/status-json.js +47 -32
  126. package/dist/src/commands/status-output.js +3 -1
  127. package/dist/src/commands/status-scope.d.ts +37 -0
  128. package/dist/src/commands/status-scope.js +54 -0
  129. package/dist/src/commands/status.js +66 -23
  130. package/dist/src/commands/test-browser-preflight.js +19 -3
  131. package/dist/src/commands/test-build-furnace.d.ts +21 -0
  132. package/dist/src/commands/test-build-furnace.js +48 -0
  133. package/dist/src/commands/test-build-phase.js +47 -41
  134. package/dist/src/commands/test-checkjs.js +7 -0
  135. package/dist/src/commands/test-diagnose.js +7 -0
  136. package/dist/src/commands/test-modes.d.ts +16 -1
  137. package/dist/src/commands/test-modes.js +24 -1
  138. package/dist/src/commands/test-multi-verdict.d.ts +61 -0
  139. package/dist/src/commands/test-multi-verdict.js +111 -0
  140. package/dist/src/commands/test-orchestrate.d.ts +43 -0
  141. package/dist/src/commands/test-orchestrate.js +222 -0
  142. package/dist/src/commands/test-perf-scope.d.ts +29 -0
  143. package/dist/src/commands/test-perf-scope.js +83 -0
  144. package/dist/src/commands/test-prelock.d.ts +51 -0
  145. package/dist/src/commands/test-prelock.js +177 -0
  146. package/dist/src/commands/test-register.js +54 -10
  147. package/dist/src/commands/test-run.js +4 -1
  148. package/dist/src/commands/test-stale-gate.js +57 -27
  149. package/dist/src/commands/test-verdict.d.ts +10 -1
  150. package/dist/src/commands/test-verdict.js +28 -1
  151. package/dist/src/commands/test.d.ts +6 -1
  152. package/dist/src/commands/test.js +66 -60
  153. package/dist/src/commands/token-lint.d.ts +14 -0
  154. package/dist/src/commands/token-lint.js +111 -0
  155. package/dist/src/commands/token.js +10 -0
  156. package/dist/src/commands/verify-scope.d.ts +28 -0
  157. package/dist/src/commands/verify-scope.js +67 -0
  158. package/dist/src/commands/verify.d.ts +10 -2
  159. package/dist/src/commands/verify.js +59 -35
  160. package/dist/src/core/build-baseline-types.d.ts +46 -0
  161. package/dist/src/core/build-baseline-validate.js +19 -0
  162. package/dist/src/core/build-baseline.d.ts +5 -1
  163. package/dist/src/core/build-baseline.js +3 -1
  164. package/dist/src/core/build-overwrite-guard.d.ts +50 -0
  165. package/dist/src/core/build-overwrite-guard.js +84 -4
  166. package/dist/src/core/build-prepare.d.ts +7 -0
  167. package/dist/src/core/build-prepare.js +14 -8
  168. package/dist/src/core/build-run-record.d.ts +74 -0
  169. package/dist/src/core/build-run-record.js +136 -0
  170. package/dist/src/core/checkjs-support-heads.d.ts +41 -0
  171. package/dist/src/core/checkjs-support-heads.js +104 -0
  172. package/dist/src/core/config-paths.d.ts +2 -2
  173. package/dist/src/core/config-paths.js +8 -0
  174. package/dist/src/core/config-validate-check.d.ts +8 -0
  175. package/dist/src/core/config-validate-check.js +143 -0
  176. package/dist/src/core/config-validate-file-size.d.ts +9 -6
  177. package/dist/src/core/config-validate-file-size.js +91 -2
  178. package/dist/src/core/config-validate-history.d.ts +11 -0
  179. package/dist/src/core/config-validate-history.js +38 -0
  180. package/dist/src/core/config-validate-patch-policy.js +100 -0
  181. package/dist/src/core/config-validate-test-toolchains.js +7 -0
  182. package/dist/src/core/config-validate-token-scales.d.ts +6 -0
  183. package/dist/src/core/config-validate-token-scales.js +50 -0
  184. package/dist/src/core/config-validate.js +14 -5
  185. package/dist/src/core/config.d.ts +5 -0
  186. package/dist/src/core/config.js +16 -0
  187. package/dist/src/core/coverage-dispatch.d.ts +24 -0
  188. package/dist/src/core/coverage-dispatch.js +44 -0
  189. package/dist/src/core/coverage-extend.d.ts +70 -45
  190. package/dist/src/core/coverage-extend.js +199 -144
  191. package/dist/src/core/coverage-records.d.ts +97 -0
  192. package/dist/src/core/coverage-records.js +350 -0
  193. package/dist/src/core/destructive.d.ts +7 -4
  194. package/dist/src/core/destructive.js +10 -10
  195. package/dist/src/core/engine-session-lock.d.ts +10 -1
  196. package/dist/src/core/engine-session-lock.js +28 -8
  197. package/dist/src/core/export-moved-code-shape.d.ts +49 -11
  198. package/dist/src/core/export-moved-code-shape.js +135 -44
  199. package/dist/src/core/file-lock-queue.d.ts +67 -0
  200. package/dist/src/core/file-lock-queue.js +221 -0
  201. package/dist/src/core/file-lock.d.ts +4 -25
  202. package/dist/src/core/file-lock.js +105 -138
  203. package/dist/src/core/furnace-apply-output.d.ts +10 -0
  204. package/dist/src/core/furnace-apply-output.js +31 -1
  205. package/dist/src/core/furnace-apply.js +12 -2
  206. package/dist/src/core/furnace-composed-source.d.ts +76 -0
  207. package/dist/src/core/furnace-composed-source.js +167 -0
  208. package/dist/src/core/furnace-config-validate-exceptions.d.ts +9 -0
  209. package/dist/src/core/furnace-config-validate-exceptions.js +71 -0
  210. package/dist/src/core/furnace-config-validate.js +8 -0
  211. package/dist/src/core/furnace-css-fragments.d.ts +10 -0
  212. package/dist/src/core/furnace-css-fragments.js +26 -0
  213. package/dist/src/core/furnace-operation.d.ts +3 -3
  214. package/dist/src/core/furnace-registration-remove.d.ts +22 -1
  215. package/dist/src/core/furnace-registration-remove.js +53 -17
  216. package/dist/src/core/furnace-registration-rename.d.ts +26 -0
  217. package/dist/src/core/furnace-registration-rename.js +77 -0
  218. package/dist/src/core/furnace-registration.d.ts +14 -4
  219. package/dist/src/core/furnace-registration.js +44 -10
  220. package/dist/src/core/furnace-staleness.d.ts +7 -0
  221. package/dist/src/core/furnace-staleness.js +34 -24
  222. package/dist/src/core/furnace-validate-accessibility.d.ts +4 -2
  223. package/dist/src/core/furnace-validate-accessibility.js +10 -1
  224. package/dist/src/core/furnace-validate-exceptions.d.ts +40 -0
  225. package/dist/src/core/furnace-validate-exceptions.js +43 -0
  226. package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
  227. package/dist/src/core/furnace-validate-icon-button.js +49 -0
  228. package/dist/src/core/furnace-validate.js +1 -1
  229. package/dist/src/core/harness-port-fallback.d.ts +60 -0
  230. package/dist/src/core/harness-port-fallback.js +96 -0
  231. package/dist/src/core/history-log-file.d.ts +61 -0
  232. package/dist/src/core/history-log-file.js +157 -0
  233. package/dist/src/core/history-log.d.ts +2 -1
  234. package/dist/src/core/history-log.js +2 -1
  235. package/dist/src/core/json-preserving-edit.d.ts +22 -0
  236. package/dist/src/core/json-preserving-edit.js +199 -0
  237. package/dist/src/core/mach.js +8 -1
  238. package/dist/src/core/negative-control-diff.d.ts +39 -0
  239. package/dist/src/core/negative-control-diff.js +136 -0
  240. package/dist/src/core/negative-control.d.ts +138 -0
  241. package/dist/src/core/negative-control.js +356 -0
  242. package/dist/src/core/objdir-install-manifests.d.ts +73 -0
  243. package/dist/src/core/objdir-install-manifests.js +226 -0
  244. package/dist/src/core/patch-export-update.d.ts +2 -0
  245. package/dist/src/core/patch-export-update.js +1 -0
  246. package/dist/src/core/patch-export.js +3 -4
  247. package/dist/src/core/patch-lint-checkjs-any-import.d.ts +37 -0
  248. package/dist/src/core/patch-lint-checkjs-any-import.js +61 -0
  249. package/dist/src/core/patch-lint-checkjs.js +8 -11
  250. package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
  251. package/dist/src/core/patch-lint-code-lines.js +105 -0
  252. package/dist/src/core/patch-lint-cross.js +12 -4
  253. package/dist/src/core/patch-lint-css.d.ts +8 -2
  254. package/dist/src/core/patch-lint-css.js +10 -4
  255. package/dist/src/core/patch-lint-file-size.d.ts +12 -5
  256. package/dist/src/core/patch-lint-file-size.js +22 -11
  257. package/dist/src/core/patch-lint-forward-registration-extended.d.ts +18 -0
  258. package/dist/src/core/patch-lint-forward-registration-extended.js +35 -10
  259. package/dist/src/core/patch-lint-forward-registration.js +10 -0
  260. package/dist/src/core/patch-lint-ignore.d.ts +152 -0
  261. package/dist/src/core/patch-lint-ignore.js +299 -0
  262. package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
  263. package/dist/src/core/patch-lint-size-thresholds.js +110 -0
  264. package/dist/src/core/patch-lint-size.d.ts +117 -0
  265. package/dist/src/core/patch-lint-size.js +323 -0
  266. package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
  267. package/dist/src/core/patch-lint-staged-registration.js +20 -0
  268. package/dist/src/core/patch-lint-token-scale.d.ts +39 -0
  269. package/dist/src/core/patch-lint-token-scale.js +109 -0
  270. package/dist/src/core/patch-lint.d.ts +11 -83
  271. package/dist/src/core/patch-lint.js +64 -286
  272. package/dist/src/core/patch-lock.js +7 -6
  273. package/dist/src/core/patch-manifest-consistency.js +7 -6
  274. package/dist/src/core/patch-manifest-io.d.ts +6 -3
  275. package/dist/src/core/patch-manifest-io.js +19 -8
  276. package/dist/src/core/patch-manifest-validate.js +34 -2
  277. package/dist/src/core/patch-order-legal.d.ts +25 -0
  278. package/dist/src/core/patch-order-legal.js +83 -0
  279. package/dist/src/core/patch-policy-content.d.ts +40 -0
  280. package/dist/src/core/patch-policy-content.js +216 -0
  281. package/dist/src/core/patch-policy.d.ts +16 -3
  282. package/dist/src/core/patch-policy.js +16 -9
  283. package/dist/src/core/patch-retired.d.ts +35 -0
  284. package/dist/src/core/patch-retired.js +54 -0
  285. package/dist/src/core/patch-subset.d.ts +16 -0
  286. package/dist/src/core/patch-subset.js +66 -0
  287. package/dist/src/core/preflight-wait.d.ts +20 -0
  288. package/dist/src/core/preflight-wait.js +38 -0
  289. package/dist/src/core/rebase-from-version.d.ts +46 -0
  290. package/dist/src/core/rebase-from-version.js +71 -0
  291. package/dist/src/core/run-log.d.ts +20 -0
  292. package/dist/src/core/run-log.js +89 -3
  293. package/dist/src/core/run-profile.d.ts +7 -0
  294. package/dist/src/core/run-profile.js +7 -1
  295. package/dist/src/core/smoke-patterns.d.ts +5 -0
  296. package/dist/src/core/smoke-patterns.js +13 -0
  297. package/dist/src/core/test-harness-crash.d.ts +18 -1
  298. package/dist/src/core/test-harness-crash.js +6 -4
  299. package/dist/src/core/test-harness-signature.d.ts +17 -0
  300. package/dist/src/core/test-harness-signature.js +4 -0
  301. package/dist/src/core/test-run-completeness.d.ts +57 -0
  302. package/dist/src/core/test-run-completeness.js +144 -0
  303. package/dist/src/core/test-stale-check.d.ts +27 -1
  304. package/dist/src/core/test-stale-check.js +39 -49
  305. package/dist/src/core/test-stale-exempt.d.ts +40 -0
  306. package/dist/src/core/test-stale-exempt.js +70 -0
  307. package/dist/src/core/token-lint.d.ts +50 -0
  308. package/dist/src/core/token-lint.js +126 -0
  309. package/dist/src/core/tree-guard.js +3 -0
  310. package/dist/src/core/typecheck-shim.d.ts +1 -1
  311. package/dist/src/core/typecheck-shim.js +90 -2
  312. package/dist/src/index.d.ts +8 -3
  313. package/dist/src/index.js +3 -1
  314. package/dist/src/types/commands/index.d.ts +2 -2
  315. package/dist/src/types/commands/options.d.ts +148 -21
  316. package/dist/src/types/commands/patches.d.ts +75 -1
  317. package/dist/src/types/config.d.ts +192 -0
  318. package/dist/src/types/config.js +9 -0
  319. package/dist/src/types/furnace.d.ts +43 -1
  320. package/dist/src/types/furnace.js +7 -0
  321. package/dist/src/types/index.d.ts +1 -1
  322. package/dist/src/utils/options.d.ts +34 -6
  323. package/dist/src/utils/options.js +86 -5
  324. package/dist/src/utils/path-glob.d.ts +15 -0
  325. package/dist/src/utils/path-glob.js +47 -0
  326. package/dist/src/utils/regex.d.ts +15 -3
  327. package/dist/src/utils/regex.js +37 -16
  328. package/docs/configuration.md +282 -9
  329. package/docs/exit-codes.md +18 -1
  330. package/docs/furnace.md +97 -3
  331. package/docs/lifecycle-invariants.md +52 -19
  332. package/docs/machine-output.md +74 -12
  333. package/docs/patch-workflow.md +297 -22
  334. package/docs/run-logs.md +20 -1
  335. package/docs/testing.md +276 -20
  336. package/package.json +2 -2
@@ -1,12 +1,15 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
- * Validation for `patchLint.fileSizeThresholds`.
3
+ * Validation for the size-rule keys of `patchLint`: `fileSizeThresholds`,
4
+ * `fileSizeMetric`, `patchSizeThresholds`, `patchSizeMetric` and
5
+ * `patchSizeRegistrationFiles`.
4
6
  *
5
7
  * Split out of `config-validate.ts`, which is at its per-file line budget.
6
8
  */
7
9
  import { ConfigError } from '../errors/config.js';
8
10
  import { isObject } from '../utils/validation.js';
9
11
  import { DEFAULT_FILE_SIZE_THRESHOLDS } from './patch-lint-file-size.js';
12
+ import { resolvePatchSizeThresholds, } from './patch-lint-size-thresholds.js';
10
13
  /**
11
14
  * Parses one `patchLint.fileSizeThresholds.<tier>` triple. Every field is
12
15
  * optional and merges over the built-in default. Values must be positive
@@ -39,7 +42,7 @@ function parseFileSizeTier(raw, field) {
39
42
  * against the merged triple (overrides on top of the defaults), so setting
40
43
  * only `warning` cannot land below the default `notice`.
41
44
  */
42
- export function parsePatchLintFileSizeThresholds(raw) {
45
+ function parsePatchLintFileSizeThresholds(raw) {
43
46
  if (!isObject(raw)) {
44
47
  throw new ConfigError('Config field "patchLint.fileSizeThresholds" must be a plain object');
45
48
  }
@@ -63,3 +66,89 @@ export function parsePatchLintFileSizeThresholds(raw) {
63
66
  }
64
67
  return out;
65
68
  }
69
+ /** Throws unless `merged` is ordered `notice <= warning <= error` (a `null` error is unbounded). */
70
+ function assertOrderedTriple(merged, field) {
71
+ if (merged.notice > merged.warning || (merged.error !== null && merged.warning > merged.error)) {
72
+ throw new ConfigError(`Config field "${field}" must satisfy notice <= warning <= error ` +
73
+ `(resolved: ${merged.notice}/${merged.warning}/${merged.error ?? 'none'})`);
74
+ }
75
+ }
76
+ /**
77
+ * Parses one `patchLint.patchSizeThresholds.<tier>` entry: optional
78
+ * `lines` and `files` triples, each validated against its merged result.
79
+ */
80
+ function parsePatchSizeTier(raw, tier) {
81
+ const field = `patchLint.patchSizeThresholds.${tier}`;
82
+ if (!isObject(raw)) {
83
+ throw new ConfigError(`Config field "${field}" must be a plain object`);
84
+ }
85
+ const out = {};
86
+ for (const key of Object.keys(raw)) {
87
+ if (key !== 'lines' && key !== 'files') {
88
+ throw new ConfigError(`Config field "${field}" has unknown key "${key}"`);
89
+ }
90
+ out[key] = parseFileSizeTier(raw[key], `${field}.${key}`);
91
+ }
92
+ const merged = resolvePatchSizeThresholds(tier, { [tier]: out });
93
+ assertOrderedTriple(merged.lines, `${field}.lines`);
94
+ assertOrderedTriple(merged.files, `${field}.files`);
95
+ return out;
96
+ }
97
+ /** Parses the `patchLint.patchSizeThresholds` block. */
98
+ function parsePatchLintPatchSizeThresholds(raw) {
99
+ if (!isObject(raw)) {
100
+ throw new ConfigError('Config field "patchLint.patchSizeThresholds" must be a plain object');
101
+ }
102
+ const out = {};
103
+ for (const key of Object.keys(raw)) {
104
+ if (key !== 'general' && key !== 'test' && key !== 'branding') {
105
+ throw new ConfigError(`Config field "patchLint.patchSizeThresholds" has unknown key "${key}"`);
106
+ }
107
+ out[key] = parsePatchSizeTier(raw[key], key);
108
+ }
109
+ return out;
110
+ }
111
+ /** Parses an enum-valued `patchLint` key, naming the allowed values on failure. */
112
+ function parseEnum(raw, field, values) {
113
+ if (typeof raw !== 'string' || !values.includes(raw)) {
114
+ throw new ConfigError(`Config field "${field}" must be one of: ${values.join(', ')}`);
115
+ }
116
+ return raw;
117
+ }
118
+ /**
119
+ * Parses every size-rule key of the `patchLint` block.
120
+ *
121
+ * @param read - Reads one raw `patchLint` field by key
122
+ * @returns The parsed fields, only those present
123
+ */
124
+ export function parsePatchLintSizeFields(read) {
125
+ const out = {};
126
+ const fileSizeThresholds = read('fileSizeThresholds');
127
+ if (fileSizeThresholds !== undefined) {
128
+ out.fileSizeThresholds = parsePatchLintFileSizeThresholds(fileSizeThresholds);
129
+ }
130
+ const fileSizeMetric = read('fileSizeMetric');
131
+ if (fileSizeMetric !== undefined) {
132
+ out.fileSizeMetric = parseEnum(fileSizeMetric, 'patchLint.fileSizeMetric', ['raw', 'code']);
133
+ }
134
+ const patchSizeThresholds = read('patchSizeThresholds');
135
+ if (patchSizeThresholds !== undefined) {
136
+ out.patchSizeThresholds = parsePatchLintPatchSizeThresholds(patchSizeThresholds);
137
+ }
138
+ const patchSizeMetric = read('patchSizeMetric');
139
+ if (patchSizeMetric !== undefined) {
140
+ out.patchSizeMetric = parseEnum(patchSizeMetric, 'patchLint.patchSizeMetric', [
141
+ 'diff',
142
+ 'addedCode',
143
+ ]);
144
+ }
145
+ const registration = read('patchSizeRegistrationFiles');
146
+ if (registration !== undefined) {
147
+ if (!Array.isArray(registration) ||
148
+ registration.some((v) => typeof v !== 'string' || v.trim() === '')) {
149
+ throw new ConfigError('Config field "patchLint.patchSizeRegistrationFiles" must be an array of non-empty strings');
150
+ }
151
+ out.patchSizeRegistrationFiles = registration;
152
+ }
153
+ return out;
154
+ }
@@ -0,0 +1,11 @@
1
+ import type { FireForgeConfig } from '../types/config.js';
2
+ import type { ParsedRecord } from '../utils/parse.js';
3
+ /**
4
+ * Whether a `history.path` value is usable: a non-empty, project-relative
5
+ * path that stays inside the project and names a file, not the root.
6
+ * Shared with the history writer, which reads the raw config and must agree
7
+ * with validation about what it may write to.
8
+ */
9
+ export declare function isValidHistoryPath(value: unknown): value is string;
10
+ /** Parses the optional `history` block. */
11
+ export declare function parseHistoryBlock(rec: ParsedRecord, config: FireForgeConfig): void;
@@ -0,0 +1,38 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { ConfigError } from '../errors/config.js';
3
+ import { isContainedRelativePath } from '../utils/paths.js';
4
+ /**
5
+ * Whether a `history.path` value is usable: a non-empty, project-relative
6
+ * path that stays inside the project and names a file, not the root.
7
+ * Shared with the history writer, which reads the raw config and must agree
8
+ * with validation about what it may write to.
9
+ */
10
+ export function isValidHistoryPath(value) {
11
+ return (typeof value === 'string' &&
12
+ value.trim() !== '' &&
13
+ value.trim() !== '.' &&
14
+ !/[\n\r]/.test(value) &&
15
+ isContainedRelativePath(value));
16
+ }
17
+ /** Parses the optional `history` block. */
18
+ export function parseHistoryBlock(rec, config) {
19
+ const raw = rec.raw('history');
20
+ if (raw === undefined)
21
+ return;
22
+ let historyRec;
23
+ try {
24
+ historyRec = rec.object('history');
25
+ }
26
+ catch {
27
+ throw new ConfigError('Config field "history" must be an object');
28
+ }
29
+ const out = {};
30
+ const path = historyRec.raw('path');
31
+ if (path !== undefined) {
32
+ if (!isValidHistoryPath(path)) {
33
+ throw new ConfigError('Config field "history.path" must be a project-relative file path inside the project');
34
+ }
35
+ out.path = path;
36
+ }
37
+ config.history = out;
38
+ }
@@ -121,6 +121,105 @@ function assertPolicyRangesDoNotOverlap(ranges, label) {
121
121
  }
122
122
  }
123
123
  }
124
+ function parseNonEmptyString(raw, label) {
125
+ if (typeof raw !== 'string' || raw.trim() === '') {
126
+ throw new ConfigError(`Config field "${label}" must be a non-empty string`);
127
+ }
128
+ return raw;
129
+ }
130
+ function parseGlobList(raw, label) {
131
+ if (!Array.isArray(raw) || raw.length === 0) {
132
+ throw new ConfigError(`Config field "${label}" must be a non-empty array of path globs`);
133
+ }
134
+ return raw.map((entry, index) => parseNonEmptyString(entry, `${label}[${index}]`));
135
+ }
136
+ function parseUpstreamAllowEntry(raw, label, defaultMarker) {
137
+ if (typeof raw === 'string')
138
+ return parseNonEmptyString(raw, label);
139
+ let rec;
140
+ try {
141
+ rec = parseObject(raw, label);
142
+ }
143
+ catch {
144
+ throw new ConfigError(`Config field "${label}" must be a path glob or an object`);
145
+ }
146
+ const entry = {
147
+ path: parseNonEmptyString(rec.raw('path'), `${label}.path`),
148
+ };
149
+ const addOnly = rec.raw('addOnly');
150
+ if (addOnly !== undefined) {
151
+ if (typeof addOnly !== 'boolean') {
152
+ throw new ConfigError(`Config field "${label}.addOnly" must be a boolean`);
153
+ }
154
+ entry.addOnly = addOnly;
155
+ }
156
+ const marker = rec.raw('marker');
157
+ if (marker !== undefined)
158
+ entry.marker = parseNonEmptyString(marker, `${label}.marker`);
159
+ if (entry.addOnly === true && entry.marker === undefined && defaultMarker === undefined) {
160
+ throw new ConfigError(`Config field "${label}" sets addOnly but no marker: set "${label}.marker" or "patchPolicy.addOnlyMarker"`);
161
+ }
162
+ return entry;
163
+ }
164
+ function parseScopeRule(raw, label) {
165
+ let rec;
166
+ try {
167
+ rec = parseObject(raw, label);
168
+ }
169
+ catch {
170
+ throw new ConfigError(`Config field "${label}" must be an object`);
171
+ }
172
+ const rule = { files: parseGlobList(rec.raw('files'), `${label}.files`) };
173
+ const category = rec.raw('category');
174
+ if (category !== undefined) {
175
+ rule.category = parsePatchPolicyCategory(category, `${label}.category`);
176
+ }
177
+ const filenamePattern = optionalConfigString(rec, 'filenamePattern', `${label}.filenamePattern`);
178
+ if (filenamePattern !== undefined) {
179
+ try {
180
+ new RegExp(filenamePattern);
181
+ }
182
+ catch (error) {
183
+ throw new ConfigError(`Config field "${label}.filenamePattern" must be a valid regular expression: ${toError(error).message}`);
184
+ }
185
+ rule.filenamePattern = filenamePattern;
186
+ }
187
+ if (rule.category === undefined && rule.filenamePattern === undefined) {
188
+ throw new ConfigError(`Config field "${label}" must set "category" or "filenamePattern" to select the patches it scopes`);
189
+ }
190
+ return rule;
191
+ }
192
+ /**
193
+ * Parses the registration-only policy fields: `upstreamAllowlist`,
194
+ * `addOnlyMarker`, `scopeRules` and `verifyDocumentPaths`. All opt-in.
195
+ */
196
+ function parsePatchPolicyContentRules(rec, out) {
197
+ const addOnlyMarker = rec.raw('addOnlyMarker');
198
+ if (addOnlyMarker !== undefined) {
199
+ out.addOnlyMarker = parseNonEmptyString(addOnlyMarker, 'patchPolicy.addOnlyMarker');
200
+ }
201
+ const allowlist = rec.raw('upstreamAllowlist');
202
+ if (allowlist !== undefined) {
203
+ if (!Array.isArray(allowlist)) {
204
+ throw new ConfigError('Config field "patchPolicy.upstreamAllowlist" must be an array');
205
+ }
206
+ out.upstreamAllowlist = allowlist.map((entry, index) => parseUpstreamAllowEntry(entry, `patchPolicy.upstreamAllowlist[${index}]`, out.addOnlyMarker));
207
+ }
208
+ const scopeRules = rec.raw('scopeRules');
209
+ if (scopeRules !== undefined) {
210
+ if (!Array.isArray(scopeRules)) {
211
+ throw new ConfigError('Config field "patchPolicy.scopeRules" must be an array');
212
+ }
213
+ out.scopeRules = scopeRules.map((entry, index) => parseScopeRule(entry, `patchPolicy.scopeRules[${index}]`));
214
+ }
215
+ const verifyDocumentPaths = rec.raw('verifyDocumentPaths');
216
+ if (verifyDocumentPaths !== undefined) {
217
+ if (typeof verifyDocumentPaths !== 'boolean') {
218
+ throw new ConfigError('Config field "patchPolicy.verifyDocumentPaths" must be a boolean');
219
+ }
220
+ out.verifyDocumentPaths = verifyDocumentPaths;
221
+ }
222
+ }
124
223
  /** Parses and validates the optional patch policy config block. */
125
224
  export function parsePatchPolicyBlock(rec) {
126
225
  const out = { ranges: [] };
@@ -170,5 +269,6 @@ export function parsePatchPolicyBlock(rec) {
170
269
  out.reservedRanges = reservedRangesRaw.map((entry, index) => parsePatchPolicyReservedRange(entry, `patchPolicy.reservedRanges[${index}]`));
171
270
  assertPolicyRangesDoNotOverlap(out.reservedRanges, 'patchPolicy.reservedRanges');
172
271
  }
272
+ parsePatchPolicyContentRules(rec, out);
173
273
  return out;
174
274
  }
@@ -52,6 +52,13 @@ export function parseTestBlock(rec, config) {
52
52
  }
53
53
  out.reapOrphans = reapOrphans;
54
54
  }
55
+ const mochitestPorts = testRec.raw('mochitestPorts');
56
+ if (mochitestPorts !== undefined) {
57
+ if (mochitestPorts !== 'fixed' && mochitestPorts !== 'auto') {
58
+ throw new ConfigError('Config field "test.mochitestPorts" must be "fixed" or "auto"');
59
+ }
60
+ out.mochitestPorts = mochitestPorts;
61
+ }
55
62
  config.test = out;
56
63
  }
57
64
  /** Parses optional project-specific external toolchain doctor probes. */
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Validation for `fireforge.json#patchLint.tokenScales`.
3
+ */
4
+ import type { PatchLintTokenScale } from '../types/config.js';
5
+ /** Parses and validates `patchLint.tokenScales`. */
6
+ export declare function parsePatchLintTokenScales(raw: unknown): PatchLintTokenScale[];
@@ -0,0 +1,50 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Validation for `fireforge.json#patchLint.tokenScales`.
4
+ */
5
+ import { ConfigError } from '../errors/config.js';
6
+ import { parseObject } from '../utils/parse.js';
7
+ function parseNames(raw, label, required) {
8
+ if (raw === undefined && !required)
9
+ return undefined;
10
+ if (!Array.isArray(raw) ||
11
+ (required && raw.length === 0) ||
12
+ raw.some((value) => typeof value !== 'string' || value.trim() === '')) {
13
+ throw new ConfigError(`Config field "${label}" must be ${required ? 'a non-empty' : 'an'} array of non-empty strings`);
14
+ }
15
+ return raw;
16
+ }
17
+ function parseTokenScale(raw, label) {
18
+ let rec;
19
+ try {
20
+ rec = parseObject(raw, label);
21
+ }
22
+ catch {
23
+ throw new ConfigError(`Config field "${label}" must be an object`);
24
+ }
25
+ const properties = parseNames(rec.raw('properties'), `${label}.properties`, true) ?? [];
26
+ const prefix = rec.raw('prefix');
27
+ if (typeof prefix !== 'string' || !/^--[\w-]+$/.test(prefix)) {
28
+ throw new ConfigError(`Config field "${label}.prefix" must be a custom-property prefix such as "--mybrowser-space-"`);
29
+ }
30
+ const scale = { properties, prefix };
31
+ const allowLiterals = parseNames(rec.raw('allowLiterals'), `${label}.allowLiterals`, false);
32
+ if (allowLiterals !== undefined)
33
+ scale.allowLiterals = allowLiterals;
34
+ const allow = parseNames(rec.raw('allow'), `${label}.allow`, false);
35
+ if (allow !== undefined) {
36
+ const malformed = allow.find((name) => !name.startsWith('--'));
37
+ if (malformed !== undefined) {
38
+ throw new ConfigError(`Config field "${label}.allow" entries must be custom-property names starting with "--" (got "${malformed}")`);
39
+ }
40
+ scale.allow = allow;
41
+ }
42
+ return scale;
43
+ }
44
+ /** Parses and validates `patchLint.tokenScales`. */
45
+ export function parsePatchLintTokenScales(raw) {
46
+ if (!Array.isArray(raw)) {
47
+ throw new ConfigError('Config field "patchLint.tokenScales" must be an array');
48
+ }
49
+ return raw.map((entry, index) => parseTokenScale(entry, `patchLint.tokenScales[${index}]`));
50
+ }
@@ -9,9 +9,12 @@ import { isContainedRelativePath, isExplicitAbsolutePath } from '../utils/paths.
9
9
  import { describeProductVersionIncompatibility, FIREFOX_PRODUCTS, isObject, isValidAppId, isValidFirefoxCandidate, isValidFirefoxProduct, isValidFirefoxVersion, isValidProjectLicense, PROJECT_LICENSES, } from '../utils/validation.js';
10
10
  import { SUPPORTED_CONFIG_ROOT_KEYS } from './config-paths.js';
11
11
  import { parseBuildAuditConfig } from './config-validate-build-audit.js';
12
- import { parsePatchLintFileSizeThresholds } from './config-validate-file-size.js';
12
+ import { parseCheckConfig } from './config-validate-check.js';
13
+ import { parsePatchLintSizeFields } from './config-validate-file-size.js';
14
+ import { parseHistoryBlock } from './config-validate-history.js';
13
15
  import { parsePatchPolicyBlock } from './config-validate-patch-policy.js';
14
16
  import { parseExternalToolchainsBlock, parseTestBlock } from './config-validate-test-toolchains.js';
17
+ import { parsePatchLintTokenScales } from './config-validate-token-scales.js';
15
18
  /**
16
19
  * Parses and validates the four required identity fields (`name`,
17
20
  * `vendor`, `appId`, `binaryName`): all non-empty strings, with
@@ -167,6 +170,7 @@ export function validateConfig(data) {
167
170
  config.buildAudit = parseBuildAuditConfig(buildAuditRaw);
168
171
  }
169
172
  parseTestBlock(rec, config);
173
+ parseHistoryBlock(rec, config);
170
174
  parseExternalToolchainsBlock(rec, config);
171
175
  parseWireBlock(rec, config);
172
176
  parseLicenseField(rec, config);
@@ -190,6 +194,11 @@ export function validateConfig(data) {
190
194
  if (typecheckRec) {
191
195
  config.typecheck = parseTypecheckBlock(typecheckRec);
192
196
  }
197
+ // `fireforge check` policy and project steps.
198
+ const checkRaw = rec.raw('check');
199
+ if (checkRaw !== undefined) {
200
+ config.check = parseCheckConfig(checkRaw);
201
+ }
193
202
  // Warn on unknown root keys
194
203
  const knownRootKeys = new Set(SUPPORTED_CONFIG_ROOT_KEYS);
195
204
  for (const key of rec.keys()) {
@@ -372,10 +381,7 @@ function parsePatchLintBlock(rec) {
372
381
  }
373
382
  out.rawColorAllowlist = rawColorAllowlist;
374
383
  }
375
- const fileSizeThresholds = rec.raw('fileSizeThresholds');
376
- if (fileSizeThresholds !== undefined) {
377
- out.fileSizeThresholds = parsePatchLintFileSizeThresholds(fileSizeThresholds);
378
- }
384
+ Object.assign(out, parsePatchLintSizeFields((key) => rec.raw(key)));
379
385
  const forwardRegistration = rec.raw('forwardRegistration');
380
386
  if (forwardRegistration !== undefined) {
381
387
  if (forwardRegistration !== 'support-files' && forwardRegistration !== 'extended') {
@@ -383,6 +389,9 @@ function parsePatchLintBlock(rec) {
383
389
  }
384
390
  out.forwardRegistration = forwardRegistration;
385
391
  }
392
+ const tokenScales = rec.raw('tokenScales');
393
+ if (tokenScales !== undefined)
394
+ out.tokenScales = parsePatchLintTokenScales(tokenScales);
386
395
  for (const key of PATCH_LINT_SEVERITY_GATE_KEYS) {
387
396
  const gate = parseSeverityGate(rec.raw(key), `patchLint.${key}`);
388
397
  if (gate !== undefined)
@@ -54,6 +54,11 @@ export declare function writeConfig(root: string, config: FireForgeConfig): Prom
54
54
  * This is used by CLI `config --force`, where callers may intentionally write
55
55
  * keys or value shapes outside the validated FireForgeConfig schema.
56
56
  *
57
+ * An existing file is edited in place: only the values that differ from the
58
+ * document on disk are rewritten, so a one-key change keeps the file's
59
+ * formatting (compact objects and arrays, indentation, key order) and stays
60
+ * a one-hunk diff. A missing or unparseable file is written in full.
61
+ *
57
62
  * Individual writes are atomic via {@link writeJson} (temp file + rename),
58
63
  * but atomicity alone does not prevent lost updates across concurrent
59
64
  * writers: each writer reads an old copy, mutates its own in-memory view,
@@ -16,6 +16,7 @@ import { isObject } from '../utils/validation.js';
16
16
  import { getProjectPaths } from './config-paths.js';
17
17
  import { validateConfig } from './config-validate.js';
18
18
  import { createSiblingLockPath, withFileLock } from './file-lock.js';
19
+ import { rewriteJsonPreservingFormat } from './json-preserving-edit.js';
19
20
  // ---- re-exports ----
20
21
  export { mutateConfig } from './config-mutate.js';
21
22
  export { FIREFORGE_DIR, getProjectPaths, SUPPORTED_CONFIG_PATHS, SUPPORTED_CONFIG_ROOT_KEYS, } from './config-paths.js';
@@ -109,6 +110,11 @@ export async function writeConfig(root, config) {
109
110
  * This is used by CLI `config --force`, where callers may intentionally write
110
111
  * keys or value shapes outside the validated FireForgeConfig schema.
111
112
  *
113
+ * An existing file is edited in place: only the values that differ from the
114
+ * document on disk are rewritten, so a one-key change keeps the file's
115
+ * formatting (compact objects and arrays, indentation, key order) and stays
116
+ * a one-hunk diff. A missing or unparseable file is written in full.
117
+ *
112
118
  * Individual writes are atomic via {@link writeJson} (temp file + rename),
113
119
  * but atomicity alone does not prevent lost updates across concurrent
114
120
  * writers: each writer reads an old copy, mutates its own in-memory view,
@@ -119,6 +125,16 @@ export async function writeConfig(root, config) {
119
125
  */
120
126
  export async function writeConfigDocument(root, config) {
121
127
  const paths = getProjectPaths(root);
128
+ if (await fsUtils.pathExists(paths.config)) {
129
+ const current = await fsUtils.readText(paths.config);
130
+ // `FireForgeConfig` is plain parsed-config data, so it is a JSON object.
131
+ const edited = rewriteJsonPreservingFormat(current, config);
132
+ if (edited !== undefined) {
133
+ if (edited !== current)
134
+ await fsUtils.writeText(paths.config, edited);
135
+ return;
136
+ }
137
+ }
122
138
  await fsUtils.writeJson(paths.config, config);
123
139
  }
124
140
  /**
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Dispatch-time half of per-path coverage. A scoped build that followed a
3
+ * full build carries the full build's anchor forward instead of collapsing
4
+ * the claim to its own paths. A requested path outside the scoped list is
5
+ * then still covered while its own staging inputs are unchanged against
6
+ * that anchor, and a refusal names the inputs that did change.
7
+ */
8
+ import type { BuildBaseline } from './build-baseline-types.js';
9
+ /** A requested path no recorded claim covers, with the inputs that explain it. */
10
+ export interface UncoveredRequest {
11
+ path: string;
12
+ /** Staging inputs changed since the carried full build. Empty when unknown. */
13
+ changed: string[];
14
+ }
15
+ /**
16
+ * Re-checks the paths the scoped list does not cover against the carried
17
+ * full-build anchor. Returns the paths that stay uncovered.
18
+ *
19
+ * @param engineDir - Path to the engine directory
20
+ * @param baseline - Last successful build's baseline
21
+ * @param uncovered - Request paths outside the scoped list
22
+ * @param fullSuite - The sentinel standing for a path-less (full-suite) request
23
+ */
24
+ export declare function filterByCarriedFullCoverage(engineDir: string, baseline: BuildBaseline | undefined, uncovered: readonly string[], fullSuite: string): Promise<UncoveredRequest[]>;
@@ -0,0 +1,44 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Dispatch-time half of per-path coverage. A scoped build that followed a
4
+ * full build carries the full build's anchor forward instead of collapsing
5
+ * the claim to its own paths. A requested path outside the scoped list is
6
+ * then still covered while its own staging inputs are unchanged against
7
+ * that anchor, and a refusal names the inputs that did change.
8
+ */
9
+ import { toError } from '../utils/errors.js';
10
+ import { verbose } from '../utils/logger.js';
11
+ import { collectStagingInputs, findChangedInputs, hashEngineFile, readEngineContentState, } from './coverage-records.js';
12
+ /**
13
+ * Re-checks the paths the scoped list does not cover against the carried
14
+ * full-build anchor. Returns the paths that stay uncovered.
15
+ *
16
+ * @param engineDir - Path to the engine directory
17
+ * @param baseline - Last successful build's baseline
18
+ * @param uncovered - Request paths outside the scoped list
19
+ * @param fullSuite - The sentinel standing for a path-less (full-suite) request
20
+ */
21
+ export async function filterByCarriedFullCoverage(engineDir, baseline, uncovered, fullSuite) {
22
+ const unresolved = uncovered.map((path) => ({ path, changed: [] }));
23
+ const anchor = baseline?.fullCoverageAnchor;
24
+ if (anchor === undefined || uncovered.length === 0)
25
+ return unresolved;
26
+ if ((await hashEngineFile(engineDir, 'mozconfig')) !== anchor.mozconfigHash)
27
+ return unresolved;
28
+ let state;
29
+ try {
30
+ state = await readEngineContentState(engineDir);
31
+ }
32
+ catch (error) {
33
+ verbose(`Coverage check: engine state unreadable — ${toError(error).message}`);
34
+ return unresolved;
35
+ }
36
+ const remaining = [];
37
+ for (const path of uncovered) {
38
+ const inputs = path === fullSuite ? undefined : await collectStagingInputs(engineDir, path);
39
+ const changed = await findChangedInputs(state, anchor, inputs);
40
+ if (changed === undefined || changed.length > 0)
41
+ remaining.push({ path, changed: changed ?? [] });
42
+ }
43
+ return remaining;
44
+ }