@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,98 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Engine registration edits for `furnace rename`: the customElements.js
4
+ * entry (rewritten in place, keeping its marker comments), the
5
+ * toolkit/content/jar.mn widget lines, the deployed `.ftl`, and the locale
6
+ * jar.mn line (re-pointed only when one existed, never for `sharedFtl`).
7
+ */
8
+ import { readdir } from 'node:fs/promises';
9
+ import { join } from 'node:path';
10
+ import { loadConfig } from '../../core/config.js';
11
+ import { resolveFtlChromeSubPath, resolveFtlLocaleJarMnPath, } from '../../core/furnace-constants.js';
12
+ import { resolveFurnaceMarkerComment } from '../../core/furnace-marker.js';
13
+ import { addCustomElementRegistration, addJarMnEntries, addLocaleFtlJarMnEntry, removeCustomElementRegistration, removeLocaleFtlJarMnEntry, renameCustomElementRegistration, } from '../../core/furnace-registration.js';
14
+ import { renameJarMnEntriesInPlace } from '../../core/furnace-registration-jar-inplace.js';
15
+ import { snapshotFile } from '../../core/furnace-rollback.js';
16
+ import { pathExists, readText, removeFile, writeText } from '../../utils/fs.js';
17
+ import { renameComponentFileName } from './rename-helpers.js';
18
+ /**
19
+ * The project marker (`// <MARKER>:`) a registration line written by the
20
+ * rename carries, resolved exactly as `furnace apply` resolves it. An
21
+ * unreadable fireforge.json degrades to no marker rather than failing the
22
+ * rename.
23
+ */
24
+ export async function loadMarkerComment(projectRoot) {
25
+ try {
26
+ return resolveFurnaceMarkerComment(await loadConfig(projectRoot));
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ }
32
+ /**
33
+ * Re-points the engine-side registrations (customElements.js, jar.mn, and
34
+ * the localized `.ftl` entries) from the old tag name to the new one.
35
+ *
36
+ * @param options - See {@link UpdateEngineRegistrationsOptions}
37
+ */
38
+ export async function updateEngineRegistrations(options) {
39
+ const { engineDir, oldName, newName, newDir, ftlDir, componentConfig, markerComment, journal } = options;
40
+ const customElementsPath = join(engineDir, 'toolkit/content/customElements.js');
41
+ const jarMnPath = join(engineDir, 'toolkit/content/jar.mn');
42
+ if (await pathExists(customElementsPath)) {
43
+ await snapshotFile(journal, customElementsPath);
44
+ // Rewrite the existing entry in place: it keeps its position, layout and
45
+ // trailing `// <MARKER>:` comments. Only when there is no entry to
46
+ // rewrite is a fresh one added, carrying the project marker like every
47
+ // line `furnace apply` writes.
48
+ if (!(await renameCustomElementRegistration(engineDir, oldName, newName))) {
49
+ await removeCustomElementRegistration(engineDir, oldName);
50
+ await addCustomElementRegistration(engineDir, newName, `chrome://global/content/elements/${newName}.mjs`, markerComment !== undefined ? { markerComment } : {});
51
+ }
52
+ }
53
+ if (await pathExists(jarMnPath)) {
54
+ await snapshotFile(journal, jarMnPath);
55
+ const files = (await readdir(newDir, { withFileTypes: true }))
56
+ .filter((e) => e.isFile() && (e.name.endsWith('.mjs') || e.name.endsWith('.css')))
57
+ .map((e) => e.name);
58
+ // Rewrite the old lines where they stand, like the customElements.js
59
+ // entry above, so the rename moves no other line of the block; only a
60
+ // file with no line yet is inserted.
61
+ await renameJarMnEntriesInPlace(engineDir, oldName, newName, files, (file) => renameComponentFileName(file, oldName, newName));
62
+ if (files.length > 0) {
63
+ await addJarMnEntries(engineDir, newName, files);
64
+ }
65
+ }
66
+ // Rename FTL localization files in the engine locale directory
67
+ const ftlDirPath = join(engineDir, ftlDir);
68
+ const oldFtlPath = join(ftlDirPath, `${oldName}.ftl`);
69
+ const newFtlPath = join(ftlDirPath, `${newName}.ftl`);
70
+ if (await pathExists(oldFtlPath)) {
71
+ await snapshotFile(journal, oldFtlPath);
72
+ await snapshotFile(journal, newFtlPath);
73
+ const ftlContent = await readText(oldFtlPath);
74
+ await writeText(newFtlPath, ftlContent);
75
+ await removeFile(oldFtlPath);
76
+ }
77
+ // Re-point the locale jar.mn chrome registration when the component is
78
+ // localized. Renaming the .ftl file on disk alone leaves the locale
79
+ // jar.mn pointing at `locale/.../${oldName}.ftl`, so `furnace validate`
80
+ // passes while the engine still carries a stale registration for the
81
+ // now-missing file. Only an existing line is re-pointed: a `sharedFtl`
82
+ // component has no per-component .ftl, and adding a line for one names a
83
+ // file that does not exist (an unmanaged edit `mach build` trips over).
84
+ if (componentConfig.localized) {
85
+ const chromeSubPath = resolveFtlChromeSubPath(ftlDir);
86
+ const localeJarRel = resolveFtlLocaleJarMnPath(ftlDir);
87
+ if (chromeSubPath !== undefined && localeJarRel !== undefined) {
88
+ const localeJarAbs = join(engineDir, localeJarRel);
89
+ if (await pathExists(localeJarAbs)) {
90
+ await snapshotFile(journal, localeJarAbs);
91
+ const removed = await removeLocaleFtlJarMnEntry(engineDir, localeJarRel, oldName, chromeSubPath);
92
+ if (removed && componentConfig.sharedFtl === undefined) {
93
+ await addLocaleFtlJarMnEntry(engineDir, localeJarRel, newName, chromeSubPath);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
@@ -1,5 +1,6 @@
1
+ import type { FurnaceRenameOptions } from '../../types/commands/index.js';
1
2
  /**
2
3
  * Renames a custom or override component atomically: updates directory name,
3
4
  * file names, file contents, furnace.json, and engine registrations.
4
5
  */
5
- export declare function furnaceRenameCommand(projectRoot: string, oldName: string, newName: string): Promise<void>;
6
+ export declare function furnaceRenameCommand(projectRoot: string, oldName: string, newName: string, options?: FurnaceRenameOptions): Promise<void>;
@@ -3,9 +3,8 @@ import { readdir } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { getProjectPaths, loadConfig } from '../../core/config.js';
5
5
  import { getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig, } from '../../core/furnace-config.js';
6
- import { isComponentSourceFile, resolveFtlChromeSubPath, resolveFtlDir, resolveFtlLocaleJarMnPath, tagNameToClassName, } from '../../core/furnace-constants.js';
7
- import { completeJournalRollback, runFurnaceMutation } from '../../core/furnace-operation.js';
8
- import { addCustomElementRegistration, addJarMnEntries, addLocaleFtlJarMnEntry, removeCustomElementRegistration, removeJarMnEntries, removeLocaleFtlJarMnEntry, } from '../../core/furnace-registration.js';
6
+ import { isComponentSourceFile, resolveFtlDir, tagNameToClassName, } from '../../core/furnace-constants.js';
7
+ import { completeJournalRollback, runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
9
8
  import { CUSTOM_ELEMENT_TAG_PATTERN, CUSTOM_ELEMENT_TAG_RULES, } from '../../core/furnace-registration-validate.js';
10
9
  import { createRollbackJournal, recordCreatedDir, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
11
10
  import { getStoriesDir } from '../../core/furnace-stories.js';
@@ -16,7 +15,9 @@ import { copyFile, ensureDir, pathExists, readText, removeDir, removeFile, write
16
15
  import { info, intro, note, outro, warn } from '../../utils/logger.js';
17
16
  import { escapeRegex } from '../../utils/regex.js';
18
17
  import { updateBrowserChromeTestContent } from './rename-browser-test.js';
18
+ import { renderRenamePlan } from './rename-dry-run.js';
19
19
  import { rekeyStateChecksums, renameComponentFileName, updateConfigForCustomRename, updateConfigForOverrideRename, } from './rename-helpers.js';
20
+ import { loadMarkerComment, updateEngineRegistrations } from './rename-registrations.js';
20
21
  import { renameXpcshellTestFiles } from './rename-xpcshell.js';
21
22
  import { browserTestFileName } from './test-file-name.js';
22
23
  /**
@@ -165,7 +166,7 @@ async function performRenameMutations(args) {
165
166
  const { projectRoot, oldName, newName } = args;
166
167
  const oldClassName = tagNameToClassName(oldName);
167
168
  const newClassName = tagNameToClassName(newName);
168
- await runFurnaceMutation(projectRoot, 'rename-rollback', async (ctx) => {
169
+ const mutate = async (ctx) => {
169
170
  const journal = createRollbackJournal();
170
171
  ctx.registerJournal(journal);
171
172
  let newDir = args.newDir;
@@ -245,16 +246,16 @@ async function performRenameMutations(args) {
245
246
  }
246
247
  await writeFurnaceConfig(projectRoot, config);
247
248
  // 3. Update engine registrations (custom components only)
248
- if (isCustom && config.custom[newName]?.register && (await pathExists(args.engineDir))) {
249
- const ftlDir = resolveFtlDir(config.ftlBasePath);
250
- const isLocalized = config.custom[newName].localized;
249
+ const newConfig = config.custom[newName];
250
+ if (isCustom && newConfig?.register && (await pathExists(args.engineDir))) {
251
251
  await updateEngineRegistrations({
252
252
  engineDir: args.engineDir,
253
253
  oldName,
254
254
  newName,
255
255
  newDir,
256
- ftlDir,
257
- isLocalized,
256
+ ftlDir: resolveFtlDir(config.ftlBasePath),
257
+ componentConfig: newConfig,
258
+ markerComment: await loadMarkerComment(projectRoot),
258
259
  journal,
259
260
  });
260
261
  }
@@ -318,68 +319,15 @@ async function performRenameMutations(args) {
318
319
  subject: `rename "${oldName}" → "${newName}"`,
319
320
  });
320
321
  }
321
- });
322
- }
323
- /**
324
- * Re-points the engine-side registrations (customElements.js, jar.mn, and
325
- * the localized `.ftl` entries) from the old tag name to the new one.
326
- *
327
- * @param options - See {@link UpdateEngineRegistrationsOptions}
328
- */
329
- async function updateEngineRegistrations(options) {
330
- const { engineDir, oldName, newName, newDir, ftlDir, isLocalized, journal } = options;
331
- const customElementsPath = join(engineDir, 'toolkit/content/customElements.js');
332
- const jarMnPath = join(engineDir, 'toolkit/content/jar.mn');
333
- if (await pathExists(customElementsPath)) {
334
- await snapshotFile(journal, customElementsPath);
335
- await removeCustomElementRegistration(engineDir, oldName);
336
- await addCustomElementRegistration(engineDir, newName, `chrome://global/content/elements/${newName}.mjs`);
337
- }
338
- if (await pathExists(jarMnPath)) {
339
- await snapshotFile(journal, jarMnPath);
340
- await removeJarMnEntries(engineDir, oldName);
341
- const files = (await readdir(newDir, { withFileTypes: true }))
342
- .filter((e) => e.isFile() && (e.name.endsWith('.mjs') || e.name.endsWith('.css')))
343
- .map((e) => e.name);
344
- if (files.length > 0) {
345
- await addJarMnEntries(engineDir, newName, files);
346
- }
347
- }
348
- // Rename FTL localization files in the engine locale directory
349
- const ftlDirPath = join(engineDir, ftlDir);
350
- const oldFtlPath = join(ftlDirPath, `${oldName}.ftl`);
351
- const newFtlPath = join(ftlDirPath, `${newName}.ftl`);
352
- if (await pathExists(oldFtlPath)) {
353
- await snapshotFile(journal, oldFtlPath);
354
- await snapshotFile(journal, newFtlPath);
355
- const ftlContent = await readText(oldFtlPath);
356
- await writeText(newFtlPath, ftlContent);
357
- await removeFile(oldFtlPath);
358
- }
359
- // Re-wire the locale jar.mn chrome registration when the component is
360
- // localized. `updateEngineRegistrations` renames the .ftl file on disk but
361
- // leaves the locale jar.mn pointing at `locale/.../${oldName}.ftl`, so
362
- // `furnace validate` passes while the engine still carries a stale
363
- // registration for the now-missing file.
364
- if (isLocalized) {
365
- const chromeSubPath = resolveFtlChromeSubPath(ftlDir);
366
- const localeJarRel = resolveFtlLocaleJarMnPath(ftlDir);
367
- if (chromeSubPath !== undefined && localeJarRel !== undefined) {
368
- const localeJarAbs = join(engineDir, localeJarRel);
369
- if (await pathExists(localeJarAbs)) {
370
- await snapshotFile(journal, localeJarAbs);
371
- await removeLocaleFtlJarMnEntry(engineDir, localeJarRel, oldName, chromeSubPath);
372
- await addLocaleFtlJarMnEntry(engineDir, localeJarRel, newName, chromeSubPath);
373
- }
374
- }
375
- }
322
+ };
323
+ await runFurnaceMutation(projectRoot, 'rename-rollback', mutate, waitLockMutationOptions(args.waitLockSeconds));
376
324
  }
377
325
  /**
378
326
  * Renames a custom or override component atomically: updates directory name,
379
327
  * file names, file contents, furnace.json, and engine registrations.
380
328
  */
381
- export async function furnaceRenameCommand(projectRoot, oldName, newName) {
382
- intro('Furnace Rename');
329
+ export async function furnaceRenameCommand(projectRoot, oldName, newName, options = {}) {
330
+ intro(options.dryRun ? 'Furnace Rename (dry run)' : 'Furnace Rename');
383
331
  if (!CUSTOM_ELEMENT_TAG_PATTERN.test(oldName)) {
384
332
  throw new InvalidArgumentError(`Invalid source name "${oldName}": ${CUSTOM_ELEMENT_TAG_RULES}`, 'old-name');
385
333
  }
@@ -419,6 +367,19 @@ export async function furnaceRenameCommand(projectRoot, oldName, newName) {
419
367
  if (await pathExists(newDir)) {
420
368
  throw new FurnaceError(`Target directory already exists: components/${componentDirLabel}/${newName}`, newName);
421
369
  }
370
+ if (options.dryRun) {
371
+ note(await renderRenamePlan({
372
+ config,
373
+ oldName,
374
+ newName,
375
+ oldDir,
376
+ componentDirLabel,
377
+ engineDir: paths.engine,
378
+ markerComment: await loadMarkerComment(projectRoot),
379
+ }), `${oldName} → ${newName}`);
380
+ outro('Dry run complete (no files modified)');
381
+ return;
382
+ }
422
383
  await performRenameMutations({
423
384
  projectRoot,
424
385
  oldName,
@@ -427,6 +388,7 @@ export async function furnaceRenameCommand(projectRoot, oldName, newName) {
427
388
  furnaceConfigPath: furnacePaths.furnaceConfig,
428
389
  furnacePaths,
429
390
  engineDir: paths.engine,
391
+ waitLockSeconds: options.waitLockSeconds,
430
392
  });
431
393
  note(`Component renamed: ${oldName} → ${newName}\n\n` +
432
394
  `Directory: components/${componentDirLabel}/${newName}/\n\n` +
@@ -1,9 +1,14 @@
1
+ import type { FurnaceLockWaitable } from '../../types/commands/index.js';
2
+ /** Options for `furnace scan`. */
3
+ export interface FurnaceScanOptions extends FurnaceLockWaitable {
4
+ /** Search additional Firefox directories beyond the default widgets path. */
5
+ deep?: boolean;
6
+ /** Persist every untracked component into furnace.json's stock section. */
7
+ track?: boolean;
8
+ }
1
9
  /**
2
10
  * Runs the furnace scan command to discover MozLitElement components.
3
11
  * @param projectRoot - Root directory of the project
4
12
  * @param options - Scan options
5
13
  */
6
- export declare function furnaceScanCommand(projectRoot: string, options?: {
7
- deep?: boolean;
8
- track?: boolean;
9
- }): Promise<void>;
14
+ export declare function furnaceScanCommand(projectRoot: string, options?: FurnaceScanOptions): Promise<void>;
@@ -2,7 +2,7 @@
2
2
  import { confirm, multiselect, select } from '@clack/prompts';
3
3
  import { stdioIsInteractive } from '../../core/destructive.js';
4
4
  import { ensureFurnaceConfig, furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig, } from '../../core/furnace-config.js';
5
- import { completeJournalRollback, runFurnaceMutation } from '../../core/furnace-operation.js';
5
+ import { completeJournalRollback, runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
6
6
  import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
7
7
  import { createRollbackJournal, snapshotFile } from '../../core/furnace-rollback.js';
8
8
  import { DEEP_SCAN_PATHS, scanWidgetsDirectory } from '../../core/furnace-scanner.js';
@@ -13,8 +13,9 @@ import { furnaceOverrideCommand } from './override.js';
13
13
  * @param components - Components discovered in the engine scan
14
14
  * @param tracked - Existing Furnace tracking map keyed by tag name
15
15
  * @param projectRoot - Root directory of the project
16
+ * @param waitLockSeconds - Resolved `--wait-lock` budget for the furnace lock
16
17
  */
17
- async function promptAddComponents(components, tracked, projectRoot) {
18
+ async function promptAddComponents(components, tracked, projectRoot, waitLockSeconds) {
18
19
  const untrackedComponents = components.filter((c) => !tracked.has(c.tagName));
19
20
  const shouldAdd = await confirm({ message: 'Add components to furnace.json?' });
20
21
  if (isCancel(shouldAdd) || !shouldAdd) {
@@ -36,7 +37,7 @@ async function promptAddComponents(components, tracked, projectRoot) {
36
37
  outro('Scan complete');
37
38
  return;
38
39
  }
39
- await persistStockComponents(projectRoot, selected);
40
+ await persistStockComponents(projectRoot, selected, waitLockSeconds);
40
41
  const addedNames = selected;
41
42
  success(`Added ${addedNames.length} component${addedNames.length === 1 ? '' : 's'} to furnace.json`);
42
43
  // Offer to immediately override one of the just-added stock components.
@@ -54,7 +55,7 @@ async function promptAddComponents(components, tracked, projectRoot) {
54
55
  cancel('Cancelled');
55
56
  return;
56
57
  }
57
- await furnaceOverrideCommand(projectRoot, overrideTarget);
58
+ await furnaceOverrideCommand(projectRoot, overrideTarget, waitLockSeconds !== undefined ? { waitLockSeconds } : {});
58
59
  }
59
60
  /**
60
61
  * Persists discovered component tag names into the `stock` section of
@@ -69,8 +70,8 @@ async function promptAddComponents(components, tracked, projectRoot) {
69
70
  * auto-created cleans up after itself instead of leaving an unwanted default
70
71
  * config behind.
71
72
  */
72
- async function persistStockComponents(projectRoot, names) {
73
- await runFurnaceMutation(projectRoot, 'scan-rollback', async (ctx) => {
73
+ async function persistStockComponents(projectRoot, names, waitLockSeconds) {
74
+ const mutate = async (ctx) => {
74
75
  const journal = createRollbackJournal();
75
76
  ctx.registerJournal(journal);
76
77
  const furnacePaths = getFurnacePaths(projectRoot);
@@ -96,7 +97,8 @@ async function persistStockComponents(projectRoot, names) {
96
97
  subject: `furnace.json update during scan`,
97
98
  });
98
99
  }
99
- });
100
+ };
101
+ await runFurnaceMutation(projectRoot, 'scan-rollback', mutate, waitLockMutationOptions(waitLockSeconds));
100
102
  }
101
103
  /**
102
104
  * Renders a component's discovered capabilities as a display suffix. Shared
@@ -172,14 +174,14 @@ export async function furnaceScanCommand(projectRoot, options = {}) {
172
174
  return;
173
175
  }
174
176
  const untrackedNames = untrackedComponentList.map((c) => c.tagName);
175
- await persistStockComponents(projectRoot, untrackedNames);
177
+ await persistStockComponents(projectRoot, untrackedNames, options.waitLockSeconds);
176
178
  success(`Tracked ${untrackedNames.length} component${untrackedNames.length === 1 ? '' : 's'} in the stock section of furnace.json`);
177
179
  outro('Scan complete');
178
180
  return;
179
181
  }
180
182
  const isInteractive = stdioIsInteractive();
181
183
  if (isInteractive && untrackedCount > 0) {
182
- await promptAddComponents(components, tracked, projectRoot);
184
+ await promptAddComponents(components, tracked, projectRoot, options.waitLockSeconds);
183
185
  return;
184
186
  }
185
187
  if (untrackedCount > 0) {
@@ -3,7 +3,7 @@ import { readdir } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { loadConfig } from '../../core/config.js';
5
5
  import { applyAllComponents } from '../../core/furnace-apply.js';
6
- import { logApplyResult } from '../../core/furnace-apply-output.js';
6
+ import { formatSkipBreakdown, logApplyResult } from '../../core/furnace-apply-output.js';
7
7
  import { getFurnacePaths, loadFurnaceConfig } from '../../core/furnace-config.js';
8
8
  import { reportJsconfigPathsSync } from '../../core/furnace-jsconfig.js';
9
9
  import { runFurnaceMutation, waitLockMutationOptions } from '../../core/furnace-operation.js';
@@ -136,7 +136,8 @@ export async function furnaceSyncCommand(projectRoot, options = {}) {
136
136
  throw new FurnaceError(`${totalFailures} component${totalFailures === 1 ? '' : 's'} failed to apply cleanly`);
137
137
  }
138
138
  await reportJsconfigPathsSync(projectRoot, config, false);
139
- outro(`Sync complete — ${result.applied.length} applied, ${result.skipped.length} skipped`);
139
+ outro(`Sync complete — ${result.applied.length} applied, ${result.skipped.length} skipped` +
140
+ formatSkipBreakdown(result.skipped));
140
141
  }
141
142
  else {
142
143
  outro('Dry run complete');
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `furnace validate` result rendering: the human per-component lines and
3
+ * the `--json` document (schemaVersion 1, docs/machine-output.md), plus
4
+ * the verdict both share, so the exit code and the `passed` key can never
5
+ * disagree.
6
+ */
7
+ import { type ValidationExceptionOutcome } from '../../core/furnace-validate-exceptions.js';
8
+ import type { ValidationIssue } from '../../types/furnace.js';
9
+ /** One finished `furnace validate` run. */
10
+ export interface FurnaceValidateReport {
11
+ /** Components the run judged, in validation order. */
12
+ components: string[];
13
+ /** Findings after `validationExceptions` were applied. */
14
+ outcome: ValidationExceptionOutcome;
15
+ /** `--max-warnings`, when given. */
16
+ maxWarnings?: number | undefined;
17
+ }
18
+ /** The verdict: counts, and whether errors or the warning budget fail it. */
19
+ export interface FurnaceValidateVerdict {
20
+ errors: number;
21
+ warnings: number;
22
+ /** True when `--max-warnings` was given and the warnings exceed it. */
23
+ warningBudgetExceeded: boolean;
24
+ passed: boolean;
25
+ }
26
+ /** Computes the verdict both renderings report. */
27
+ export declare function judgeValidateReport(report: FurnaceValidateReport): FurnaceValidateVerdict;
28
+ /**
29
+ * Prints one line per clean component and every finding of the rest.
30
+ * Findings on names outside `components` (a stale exception for a removed
31
+ * component, an orphaned scaffold) print after them.
32
+ */
33
+ export declare function renderValidateHuman(components: readonly string[], issues: readonly ValidationIssue[]): void;
34
+ /**
35
+ * Writes the `--json` document and seals stdout. Every finding is listed
36
+ * once in `issues` (stale-exception warnings included); `excepted` and
37
+ * `staleExceptions` restate the exception bookkeeping for a consumer that
38
+ * audits the list itself.
39
+ */
40
+ export declare function renderValidateJson(report: FurnaceValidateReport): void;
41
+ /**
42
+ * Parses `--max-warnings <n>`: a non-negative integer, or undefined when
43
+ * the flag was not given.
44
+ */
45
+ export declare function parseMaxWarnings(raw: string | undefined): number | undefined;
@@ -0,0 +1,91 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * `furnace validate` result rendering: the human per-component lines and
4
+ * the `--json` document (schemaVersion 1, docs/machine-output.md), plus
5
+ * the verdict both share, so the exit code and the `passed` key can never
6
+ * disagree.
7
+ */
8
+ import { countValidationSeverities, } from '../../core/furnace-validate-exceptions.js';
9
+ import { InvalidArgumentError } from '../../errors/base.js';
10
+ import { setStdoutSealed, success } from '../../utils/logger.js';
11
+ import { displayValidationIssues } from './validation-output.js';
12
+ /** Computes the verdict both renderings report. */
13
+ export function judgeValidateReport(report) {
14
+ const { errors, warnings } = countValidationSeverities(report.outcome.issues);
15
+ const warningBudgetExceeded = report.maxWarnings !== undefined && warnings > report.maxWarnings;
16
+ return {
17
+ errors,
18
+ warnings,
19
+ warningBudgetExceeded,
20
+ passed: errors === 0 && !warningBudgetExceeded,
21
+ };
22
+ }
23
+ /** Groups findings by component, keeping the report's component order first. */
24
+ function groupByComponent(components, issues) {
25
+ const grouped = new Map(components.map((name) => [name, []]));
26
+ for (const issue of issues) {
27
+ const list = grouped.get(issue.component) ?? [];
28
+ list.push(issue);
29
+ grouped.set(issue.component, list);
30
+ }
31
+ return grouped;
32
+ }
33
+ /**
34
+ * Prints one line per clean component and every finding of the rest.
35
+ * Findings on names outside `components` (a stale exception for a removed
36
+ * component, an orphaned scaffold) print after them.
37
+ */
38
+ export function renderValidateHuman(components, issues) {
39
+ for (const [name, componentIssues] of groupByComponent(components, issues)) {
40
+ if (componentIssues.length === 0) {
41
+ success(`${name} — all checks passed`);
42
+ }
43
+ else {
44
+ displayValidationIssues(componentIssues);
45
+ }
46
+ }
47
+ }
48
+ /**
49
+ * Writes the `--json` document and seals stdout. Every finding is listed
50
+ * once in `issues` (stale-exception warnings included); `excepted` and
51
+ * `staleExceptions` restate the exception bookkeeping for a consumer that
52
+ * audits the list itself.
53
+ */
54
+ export function renderValidateJson(report) {
55
+ const verdict = judgeValidateReport(report);
56
+ const grouped = groupByComponent(report.components, report.outcome.issues);
57
+ const payload = {
58
+ schemaVersion: 1,
59
+ passed: verdict.passed,
60
+ summary: {
61
+ components: report.components.length,
62
+ errors: verdict.errors,
63
+ warnings: verdict.warnings,
64
+ excepted: report.outcome.excepted.length,
65
+ staleExceptions: report.outcome.stale.length,
66
+ },
67
+ ...(report.maxWarnings !== undefined ? { maxWarnings: report.maxWarnings } : {}),
68
+ components: report.components.map((name) => ({
69
+ name,
70
+ ...countValidationSeverities(grouped.get(name) ?? []),
71
+ })),
72
+ issues: report.outcome.issues,
73
+ excepted: report.outcome.excepted,
74
+ staleExceptions: report.outcome.stale,
75
+ };
76
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
77
+ setStdoutSealed(true);
78
+ }
79
+ /**
80
+ * Parses `--max-warnings <n>`: a non-negative integer, or undefined when
81
+ * the flag was not given.
82
+ */
83
+ export function parseMaxWarnings(raw) {
84
+ if (raw === undefined)
85
+ return undefined;
86
+ const value = Number(raw);
87
+ if (!Number.isInteger(value) || value < 0) {
88
+ throw new InvalidArgumentError('--max-warnings must be a non-negative integer.', '--max-warnings');
89
+ }
90
+ return value;
91
+ }
@@ -3,6 +3,6 @@ import type { FurnaceValidateOptions } from '../../types/commands/index.js';
3
3
  * Runs the furnace validate command to perform static analysis on components.
4
4
  * @param projectRoot - Root directory of the project
5
5
  * @param name - Optional component name to validate (validates all if omitted)
6
- * @param options - Optional command options (e.g. --fix)
6
+ * @param options - Optional command options (`--fix`, `--json`, `--max-warnings`)
7
7
  */
8
8
  export declare function furnaceValidateCommand(projectRoot: string, name?: string, options?: FurnaceValidateOptions): Promise<void>;