@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,38 @@
1
+ /**
2
+ * The advice an export placement refusal appends for new files: "register
3
+ * first" when a new system module only lacks its moz.build line, and
4
+ * adopt-then-split for a real moved-code block, but only after checking
5
+ * that the adopt itself would pass.
6
+ *
7
+ * The advice used to be offered on the moved-code heuristic alone. On one
8
+ * queue it told the operator to adopt six new modules into an existing
9
+ * patch when the only error was the missing registration; the adopt then
10
+ * refused one module for importing later patches, and without it breached
11
+ * the source patch's hard size limit. Split out of export-flow.ts for the
12
+ * line budget.
13
+ */
14
+ import { type PatchQueueContext } from '../core/patch-lint.js';
15
+ import type { PatchLintIssue } from '../types/commands/index.js';
16
+ /**
17
+ * Why adopting `diff` into `sourceFilename` would itself be refused: the
18
+ * forward imports the adopted files would give the source patch, and the
19
+ * source patch's hard size limit. Empty when the adopt is clean.
20
+ *
21
+ * @param baseCtx - The current queue
22
+ * @param sourceFilename - The patch the adopt would grow
23
+ * @param diff - The pending export's diff
24
+ */
25
+ export declare function findAdoptBlockers(baseCtx: PatchQueueContext, sourceFilename: string, diff: string): string[];
26
+ /**
27
+ * Detail lines for a placement refusal: register-first and moved-code
28
+ * advice, each only when the projection supports it.
29
+ *
30
+ * @param baseCtx - The current queue
31
+ * @param diff - The pending export's diff
32
+ * @param projectedIssues - Error findings of the placement projection
33
+ * @param plan - The placement's new filename and order
34
+ */
35
+ export declare function describePlacementAdvice(baseCtx: PatchQueueContext, diff: string, projectedIssues: readonly PatchLintIssue[], plan: {
36
+ newFilename: string;
37
+ insertionOrder: number;
38
+ }): string[];
@@ -0,0 +1,98 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * The advice an export placement refusal appends for new files: "register
4
+ * first" when a new system module only lacks its moz.build line, and
5
+ * adopt-then-split for a real moved-code block, but only after checking
6
+ * that the adopt itself would pass.
7
+ *
8
+ * The advice used to be offered on the moved-code heuristic alone. On one
9
+ * queue it told the operator to adopt six new modules into an existing
10
+ * patch when the only error was the missing registration; the adopt then
11
+ * refused one module for importing later patches, and without it breached
12
+ * the source patch's hard size limit. Split out of export-flow.ts for the
13
+ * line budget.
14
+ */
15
+ import { detectMovedCodeOverlaps, formatMovedCodeAdvice } from '../core/export-moved-code-shape.js';
16
+ import { countNonBinaryDiffLines, getPatchSizeThresholds, lintPatchQueue, resolvePatchSizeTier, } from '../core/patch-lint.js';
17
+ import { computeProjectedLintRegressions } from '../core/patch-lint-projection.js';
18
+ import { projectEntryBody } from './patch/entry-projection.js';
19
+ /**
20
+ * Why adopting `diff` into `sourceFilename` would itself be refused: the
21
+ * forward imports the adopted files would give the source patch, and the
22
+ * source patch's hard size limit. Empty when the adopt is clean.
23
+ *
24
+ * @param baseCtx - The current queue
25
+ * @param sourceFilename - The patch the adopt would grow
26
+ * @param diff - The pending export's diff
27
+ */
28
+ export function findAdoptBlockers(baseCtx, sourceFilename, diff) {
29
+ const source = baseCtx.entries.find((entry) => entry.filename === sourceFilename);
30
+ if (source === undefined)
31
+ return [];
32
+ const blockers = [];
33
+ const pending = projectEntryBody(diff);
34
+ const adopted = {
35
+ ...source,
36
+ diff: `${source.diff}\n${diff}`,
37
+ newFiles: new Map([...source.newFiles, ...pending.newFiles]),
38
+ createdFiles: new Set([...source.createdFiles, ...pending.createdFiles]),
39
+ modifiedFileAdditions: new Map([
40
+ ...source.modifiedFileAdditions,
41
+ ...pending.modifiedFileAdditions,
42
+ ]),
43
+ };
44
+ const isError = (issue) => issue.severity === 'error';
45
+ const baseline = lintPatchQueue(baseCtx).filter(isError);
46
+ const projected = lintPatchQueue({
47
+ ...baseCtx,
48
+ entries: baseCtx.entries.map((entry) => (entry.filename === sourceFilename ? adopted : entry)),
49
+ }).filter(isError);
50
+ for (const issue of computeProjectedLintRegressions(baseline, projected)) {
51
+ if (issue.check !== 'forward-import' || !issue.patches?.includes(sourceFilename))
52
+ continue;
53
+ for (const declaration of issue.stagedDeclarations ?? []) {
54
+ blockers.push(`${declaration.file} imports "${declaration.specifier ?? ''}", created by ` +
55
+ `${declaration.owner}, which applies after ${sourceFilename} (forward-import)`);
56
+ }
57
+ }
58
+ if (!(source.metadata?.lintIgnore ?? []).includes('large-patch-lines')) {
59
+ const tier = resolvePatchSizeTier([...(source.metadata?.filesAffected ?? []), ...pending.newFiles.keys()], source.metadata?.tier);
60
+ const limit = getPatchSizeThresholds(tier.tier).lines.error;
61
+ const lines = countNonBinaryDiffLines(source.diff).textLines + countNonBinaryDiffLines(diff).textLines;
62
+ if (lines > limit) {
63
+ blockers.push(`${sourceFilename} would grow to about ${lines} lines, over its ${tier.tier} hard limit ` +
64
+ `of ${limit} (large-patch-lines)`);
65
+ }
66
+ }
67
+ return blockers;
68
+ }
69
+ /**
70
+ * Detail lines for a placement refusal: register-first and moved-code
71
+ * advice, each only when the projection supports it.
72
+ *
73
+ * @param baseCtx - The current queue
74
+ * @param diff - The pending export's diff
75
+ * @param projectedIssues - Error findings of the placement projection
76
+ * @param plan - The placement's new filename and order
77
+ */
78
+ export function describePlacementAdvice(baseCtx, diff, projectedIssues, plan) {
79
+ const newFiles = projectEntryBody(diff).newFiles;
80
+ const exported = projectedIssues.filter((issue) => issue.patches?.includes(plan.newFilename) ?? issue.message.includes(plan.newFilename));
81
+ const unregisteredModules = [
82
+ ...new Set(exported
83
+ .filter((issue) => issue.check === 'unregistered-system-module' && newFiles.has(issue.file))
84
+ .map((issue) => issue.file)),
85
+ ];
86
+ const registrationResolvesAll = unregisteredModules.length > 0 &&
87
+ exported.every((issue) => issue.check === 'unregistered-system-module');
88
+ const overlaps = registrationResolvesAll ? [] : detectMovedCodeOverlaps(diff, baseCtx.entries);
89
+ const strongest = overlaps[0];
90
+ return formatMovedCodeAdvice({
91
+ overlaps,
92
+ newPatchName: plan.newFilename,
93
+ insertionOrder: plan.insertionOrder,
94
+ unregisteredModules,
95
+ registrationResolvesAll,
96
+ adoptBlockers: strongest !== undefined ? findAdoptBlockers(baseCtx, strongest.sourcePatch, diff) : [],
97
+ });
98
+ }
@@ -7,11 +7,11 @@
7
7
  * per-file / per-function line budgets and makes each step individually
8
8
  * testable without dragging the whole command harness along for the ride.
9
9
  */
10
- import { join } from 'node:path';
10
+ import { dirname, join } from 'node:path';
11
11
  import {} from '../core/destructive.js';
12
- import { detectMovedCodeOverlaps, formatAdoptThenSplitRemedy, } from '../core/export-moved-code-shape.js';
13
12
  import { buildPlanExportInput, findAllPatchesForFilesWithDetails, patchNameSlug, planExport, } from '../core/patch-export.js';
14
13
  import { buildPatchQueueContext, lintPatchQueue } from '../core/patch-lint.js';
14
+ import { formatLintIgnoreList } from '../core/patch-lint-ignore.js';
15
15
  import { withPatchDirectoryLock } from '../core/patch-lock.js';
16
16
  import { addPatchToManifest, loadPatchesManifest, loadPatchesManifestForWrite, renumberPatchesInManifest, resolvePatchIdentifier, savePatchesManifest, } from '../core/patch-manifest.js';
17
17
  import { formatPatchOrder, requirePatchOrder } from '../core/patch-parse.js';
@@ -20,9 +20,11 @@ import { GeneralError, InvalidArgumentError } from '../errors/base.js';
20
20
  import { toError } from '../utils/errors.js';
21
21
  import { pathExists, readText, removeFile, writeText } from '../utils/fs.js';
22
22
  import { info, warn } from '../utils/logger.js';
23
+ import { describePlacementAdvice } from './export-adopt-check.js';
23
24
  import { groupProjectedPlacementErrors } from './export-placement-conflicts.js';
24
25
  import { assertPlacementAvoidsReservedRanges } from './export-placement-policy.js';
25
26
  import { findPartialOwnershipOverlap } from './export-shared.js';
27
+ import { acceptTreeRegisteredModules } from './export-staged-registration.js';
26
28
  import { projectEntryBody } from './patch/entry-projection.js';
27
29
  import { getSortedRenameEntries, renameMapsEqual } from './patch-rename-map.js';
28
30
  function buildFilenameForPlacement(category, name, order, width) {
@@ -170,18 +172,31 @@ export async function projectPlacementForLint(patchesDir, plan, diff, config) {
170
172
  // whether they live in a brand-new file or are added to an existing one.
171
173
  // Leaving either map empty lets a patch land before one of its own
172
174
  // dependencies and still pass the gate.
175
+ const pendingBody = projectEntryBody(diff);
173
176
  projectedEntries.push({
174
177
  filename: plan.newFilename,
175
178
  order: plan.insertionOrder,
176
179
  metadata: null,
177
- ...projectEntryBody(diff),
180
+ ...pendingBody,
178
181
  });
179
182
  projectedEntries.sort((a, b) => a.order - b.order || a.filename.localeCompare(b.filename));
180
183
  const policyRest = baseCtx.patchPolicy ? { patchPolicy: baseCtx.patchPolicy } : {};
181
- const projectedIssues = lintPatchQueue({
182
- entries: projectedEntries,
183
- ...policyRest,
184
- }).filter((i) => i.severity === 'error');
184
+ // A new module whose moz.build line is declared (staged registration)
185
+ // and present in the working tree, but not yet exported by its owner, is
186
+ // accepted with a "re-export the owner next" note instead of refused.
187
+ const registration = await acceptTreeRegisteredModules({
188
+ engineDir: join(dirname(patchesDir), 'engine'),
189
+ baseCtx,
190
+ newFilename: plan.newFilename,
191
+ pendingNewFiles: new Set(pendingBody.newFiles.keys()),
192
+ projectedIssues: lintPatchQueue({
193
+ entries: projectedEntries,
194
+ ...policyRest,
195
+ }).filter((i) => i.severity === 'error'),
196
+ });
197
+ for (const note of registration.accepted)
198
+ warn(note);
199
+ const projectedIssues = registration.remaining;
185
200
  if (projectedIssues.length === 0)
186
201
  return null;
187
202
  // Baseline lint over the un-renumbered queue: an error present here is
@@ -192,16 +207,16 @@ export async function projectPlacementForLint(patchesDir, plan, diff, config) {
192
207
  entries: baseCtx.entries,
193
208
  ...policyRest,
194
209
  }).filter((i) => i.severity === 'error');
195
- // New-file + moved-code slice: when the refusal is caused by
196
- // the source patch still carrying lines the new patch would gain, name
197
- // the adopt-then-split sequence instead of leaving the operator to
198
- // derive it from two individually-correct guards that together dead-end.
199
- const movedCode = detectMovedCodeOverlaps(diff, baseCtx.entries);
210
+ // Register-first and new-file + moved-code advice: name the working
211
+ // sequence instead of leaving the operator to derive it from two
212
+ // individually-correct guards that together dead-end, but only offer
213
+ // adopt-then-split for a real moved block whose adopt would itself pass.
200
214
  return {
201
215
  reason: `placement would introduce ${projectedIssues.length} cross-patch lint error(s)`,
202
216
  details: [
203
217
  ...groupProjectedPlacementErrors(projectedIssues, baselineIssues, plan),
204
- ...formatAdoptThenSplitRemedy(movedCode, plan.newFilename, plan.insertionOrder),
218
+ ...registration.advice,
219
+ ...describePlacementAdvice(baseCtx, diff, projectedIssues, plan),
205
220
  ],
206
221
  };
207
222
  }
@@ -380,7 +395,7 @@ export async function renderDryRunPreview(input) {
380
395
  info(` tier: ${plan.metadata.tier}`);
381
396
  }
382
397
  if (plan.metadata.lintIgnore !== undefined && plan.metadata.lintIgnore.length > 0) {
383
- info(` lintIgnore: ${plan.metadata.lintIgnore.join(', ')}`);
398
+ info(` lintIgnore: ${formatLintIgnoreList(plan.metadata.lintIgnore)}`);
384
399
  }
385
400
  if (supersedeDetails.length > 0) {
386
401
  info(`\n[dry-run] Would supersede ${supersedeDetails.length} existing patch(es):`);
@@ -4,7 +4,7 @@
4
4
  * budgets. `gatePlacementPlan` owns every policy/confirmation decision a
5
5
  * placement export makes before the locked commit in `export.ts` runs.
6
6
  */
7
- import type { ExportOptions, PatchMetadata } from '../types/commands/index.js';
7
+ import type { ExportOptions } from '../types/commands/index.js';
8
8
  import type { FireForgeConfig } from '../types/config.js';
9
9
  import type { SpinnerHandle } from '../utils/logger.js';
10
10
  import { type PlacementPlan } from './export-flow.js';
@@ -14,7 +14,11 @@ import { type PlacementPlan } from './export-flow.js';
14
14
  * shares this shape. With exactOptionalPropertyTypes the keys must be
15
15
  * omitted entirely (not set to undefined) when the flags are absent.
16
16
  */
17
- export declare function patchMetadataExtras(options: ExportOptions): Partial<Pick<PatchMetadata, 'tier' | 'lintIgnore'>>;
17
+ export declare function patchMetadataExtras(options: ExportOptions): {
18
+ tier?: 'branding';
19
+ /** CLI flags only ever carry bare check IDs. */
20
+ lintIgnore?: string[];
21
+ };
18
22
  /**
19
23
  * Resolves and gates the placement plan when any placement flag
20
24
  * (`--order`/`--before`/`--after`) was given: rejects the `--supersede`
@@ -8,6 +8,7 @@
8
8
  import { confirmDestructive } from '../core/destructive.js';
9
9
  import { loadPatchesManifest } from '../core/patch-manifest.js';
10
10
  import { applyRenameMapToManifest, buildProjectedManifest, enforcePatchPolicy, } from '../core/patch-policy.js';
11
+ import { reservationAt } from '../core/patch-retired.js';
11
12
  import { buildPatchSourceMetadata } from '../core/patch-source-metadata.js';
12
13
  import { InvalidArgumentError } from '../errors/base.js';
13
14
  import { proceedAfterDecision } from './destructive-decision.js';
@@ -68,6 +69,13 @@ export async function gatePlacementPlan(args) {
68
69
  forceUnsafe: options.forceUnsafe === true,
69
70
  });
70
71
  const summary = placementSummary(placementPlan);
72
+ const reservation = reservationAt(currentManifest, placementPlan.insertionOrder);
73
+ if (reservation !== undefined) {
74
+ const notice = `order ${placementPlan.insertionOrder} is reserved by ${reservation.owner}; ` +
75
+ 'this export fills the reservation';
76
+ summary.push(notice);
77
+ s.message(notice);
78
+ }
71
79
  const renameCount = placementPlan.renameMap.size;
72
80
  // Route through confirmDestructive when the operation is destructive
73
81
  // enough to warrant a prompt (more than one rename) or when the user asked
@@ -1,3 +1,4 @@
1
+ import type { LintIgnoreChecks } from '../core/patch-lint-ignore.js';
1
2
  import type { PatchesManifest, PatchLintIssue } from '../types/commands/index.js';
2
3
  import type { ExportOptions, PatchCategory } from '../types/commands/index.js';
3
4
  import type { FireForgeConfig } from '../types/config.js';
@@ -22,7 +23,7 @@ export interface RunPatchLintOptions {
22
23
  * `large-patch-lines` on a cohesive branding bundle that genuinely cannot
23
24
  * be split.
24
25
  */
25
- ignoreChecks?: ReadonlySet<string> | undefined;
26
+ ignoreChecks?: LintIgnoreChecks | undefined;
26
27
  /**
27
28
  * Optional explicit tier override (threaded from `PatchMetadata.tier`).
28
29
  * Forces the branding-tier thresholds when set, independent of the
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Lets `export --order` land a new system module whose moz.build line is
3
+ * still unexported working-tree drift in the patch that registers it.
4
+ *
5
+ * The two refusals used to deadlock: the module's export failed
6
+ * `unregistered-system-module` because no patch body carried its moz.build
7
+ * line yet, and the registering patch could not be re-exported first
8
+ * because its line named a file no patch created. The way through is
9
+ * declared intent plus evidence: when the registering patch carries a
10
+ * staged registration for the module and the working tree's moz.build has
11
+ * exactly that line, the export is accepted and the operator is told to
12
+ * re-export the registering patch next. Without the declaration the
13
+ * refusal names the steps, with the exact `patch staged-dependency`
14
+ * command, instead of stopping at "register it".
15
+ *
16
+ * Consumed by export-flow.ts.
17
+ */
18
+ import type { PatchQueueContext } from '../core/patch-lint.js';
19
+ import type { PatchLintIssue } from '../types/commands/index.js';
20
+ /** Result of {@link acceptTreeRegisteredModules}. */
21
+ export interface TreeRegistrationOutcome {
22
+ /** The projected errors left after accepted modules are removed. */
23
+ remaining: PatchLintIssue[];
24
+ /** One note per accepted module, naming the re-export to run next. */
25
+ accepted: string[];
26
+ /** Detail lines for modules whose registration is in the tree but undeclared. */
27
+ advice: string[];
28
+ }
29
+ /**
30
+ * Removes `unregistered-system-module` findings for modules the pending
31
+ * export creates when a queue patch declares their registration as a
32
+ * staged dependency and the working tree's moz.build carries that line.
33
+ * For a module whose line is in the tree but undeclared, returns the steps
34
+ * that make it acceptable.
35
+ *
36
+ * @param args - The engine directory (the working tree), the current queue,
37
+ * the filename the pending export would take, the files it creates, and
38
+ * the placement projection's error findings
39
+ */
40
+ export declare function acceptTreeRegisteredModules(args: {
41
+ engineDir: string;
42
+ baseCtx: PatchQueueContext;
43
+ newFilename: string;
44
+ pendingNewFiles: ReadonlySet<string>;
45
+ projectedIssues: readonly PatchLintIssue[];
46
+ }): Promise<TreeRegistrationOutcome>;
@@ -0,0 +1,91 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Lets `export --order` land a new system module whose moz.build line is
4
+ * still unexported working-tree drift in the patch that registers it.
5
+ *
6
+ * The two refusals used to deadlock: the module's export failed
7
+ * `unregistered-system-module` because no patch body carried its moz.build
8
+ * line yet, and the registering patch could not be re-exported first
9
+ * because its line named a file no patch created. The way through is
10
+ * declared intent plus evidence: when the registering patch carries a
11
+ * staged registration for the module and the working tree's moz.build has
12
+ * exactly that line, the export is accepted and the operator is told to
13
+ * re-export the registering patch next. Without the declaration the
14
+ * refusal names the steps, with the exact `patch staged-dependency`
15
+ * command, instead of stopping at "register it".
16
+ *
17
+ * Consumed by export-flow.ts.
18
+ */
19
+ import { basename, dirname, join } from 'node:path';
20
+ import { STAGED_DEPENDENCY_REASON_PLACEHOLDER } from '../core/patch-lint-staged-registration.js';
21
+ import { pathExists, readText } from '../utils/fs.js';
22
+ async function readLines(path) {
23
+ if (!(await pathExists(path)))
24
+ return undefined;
25
+ return (await readText(path)).split('\n').map((line) => line.trim());
26
+ }
27
+ /** The nearest moz.build at or above `modulePath` whose lines name `leaf` quoted. */
28
+ async function findTreeRegistration(engineDir, modulePath) {
29
+ const leaf = basename(modulePath);
30
+ let dir = dirname(modulePath);
31
+ for (;;) {
32
+ const file = dir === '.' ? 'moz.build' : `${dir}/moz.build`;
33
+ const line = (await readLines(join(engineDir, file)))?.find((candidate) => candidate.includes(`"${leaf}"`) || candidate.includes(`'${leaf}'`));
34
+ if (line !== undefined)
35
+ return { file, line };
36
+ if (dir === '.' || dir === '')
37
+ return undefined;
38
+ const parent = dirname(dir);
39
+ dir = parent === dir ? '.' : parent;
40
+ }
41
+ }
42
+ /**
43
+ * Removes `unregistered-system-module` findings for modules the pending
44
+ * export creates when a queue patch declares their registration as a
45
+ * staged dependency and the working tree's moz.build carries that line.
46
+ * For a module whose line is in the tree but undeclared, returns the steps
47
+ * that make it acceptable.
48
+ *
49
+ * @param args - The engine directory (the working tree), the current queue,
50
+ * the filename the pending export would take, the files it creates, and
51
+ * the placement projection's error findings
52
+ */
53
+ export async function acceptTreeRegisteredModules(args) {
54
+ const outcome = { remaining: [], accepted: [], advice: [] };
55
+ for (const issue of args.projectedIssues) {
56
+ if (issue.check !== 'unregistered-system-module' || !args.pendingNewFiles.has(issue.file)) {
57
+ outcome.remaining.push(issue);
58
+ continue;
59
+ }
60
+ const declared = args.baseCtx.entries.flatMap((entry) => (entry.metadata?.stagedDependencies?.registrations ?? [])
61
+ .filter((registration) => registration.creates === issue.file &&
62
+ (registration.owner === undefined || registration.owner === args.newFilename))
63
+ .map((registration) => ({ holder: entry.filename, registration })));
64
+ let accepted = false;
65
+ for (const { holder, registration } of declared) {
66
+ const lines = await readLines(join(args.engineDir, registration.file));
67
+ if (lines?.includes(registration.line.trim()) !== true)
68
+ continue;
69
+ outcome.accepted.push(`${issue.file} is registered by the working tree's ${registration.file} ` +
70
+ `("${registration.line.trim()}"), which ${holder} declares as a staged registration ` +
71
+ `but has not exported yet. Re-export ${holder} right after this export to land the ` +
72
+ `line: fireforge re-export ${holder}`);
73
+ accepted = true;
74
+ break;
75
+ }
76
+ if (accepted)
77
+ continue;
78
+ outcome.remaining.push(issue);
79
+ const tree = await findTreeRegistration(args.engineDir, issue.file);
80
+ if (tree === undefined)
81
+ continue;
82
+ const owner = args.baseCtx.entries.find((entry) => entry.metadata?.filesAffected.includes(tree.file))?.filename;
83
+ if (owner === undefined)
84
+ continue;
85
+ outcome.advice.push('', `${issue.file} is registered in the working tree's ${tree.file}, but ${owner} has not ` +
86
+ 'exported that line. Land both without untangling the tree:', ` 1. fireforge patch staged-dependency ${owner} --add --kind registration --file ${tree.file} ` +
87
+ `--line "${tree.line.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}" --creates ${issue.file} ` +
88
+ `--owner ${args.newFilename} ${STAGED_DEPENDENCY_REASON_PLACEHOLDER}`, ' 2. re-run this export (a declared registration present in the working tree is accepted)', ` 3. fireforge re-export ${owner}`);
89
+ }
90
+ return outcome;
91
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The remedy printed under a forward-import refusal from `re-export`
3
+ * (plain refresh and `--scan` adoption alike): the earliest order the files
4
+ * can take as their own later patch, and the exact `patch staged-dependency
5
+ * --add` commands that declare the edges instead.
6
+ *
7
+ * The refusal used to end with "export them with --order <n> or run
8
+ * patch staged-dependency --add", which left the operator to work out both
9
+ * the number and every flag of the declaration from the finding text.
10
+ */
11
+ import type { PatchesManifest, PatchLintIssue } from '../types/commands/index.js';
12
+ import type { FireForgeConfig } from '../types/config.js';
13
+ /** Inputs to {@link formatForwardImportRemedy}. */
14
+ export interface ForwardImportRemedyInput {
15
+ /** What to do after the fix, e.g. "re-running the scan". */
16
+ reRunHint: string;
17
+ /** The refusal's findings; only `forward-import` and `forward-registration` ones are read. */
18
+ issues: readonly PatchLintIssue[];
19
+ /** Current manifest (null when unavailable: the order is then not named). */
20
+ manifest: PatchesManifest | null;
21
+ /** Project config; only `patchPolicy` is read. */
22
+ config?: Pick<FireForgeConfig, 'patchPolicy'>;
23
+ /** Category the files would be exported under (the refused patch's). */
24
+ category?: string;
25
+ }
26
+ /**
27
+ * Renders the remedy lines. Falls back to the generic sentence when the
28
+ * findings carry no structured declarations.
29
+ *
30
+ * @param input - See {@link ForwardImportRemedyInput}
31
+ */
32
+ export declare function formatForwardImportRemedy(input: ForwardImportRemedyInput): string;
@@ -0,0 +1,63 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * The remedy printed under a forward-import refusal from `re-export`
4
+ * (plain refresh and `--scan` adoption alike): the earliest order the files
5
+ * can take as their own later patch, and the exact `patch staged-dependency
6
+ * --add` commands that declare the edges instead.
7
+ *
8
+ * The refusal used to end with "export them with --order <n> or run
9
+ * patch staged-dependency --add", which left the operator to work out both
10
+ * the number and every flag of the declaration from the finding text.
11
+ */
12
+ import { STAGED_DEPENDENCY_REASON_PLACEHOLDER } from '../core/patch-lint-staged-registration.js';
13
+ import { earliestLegalOrderAfter } from '../core/patch-order-legal.js';
14
+ import { formatPatchOrder } from '../core/patch-parse.js';
15
+ function quoteArg(value) {
16
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
17
+ }
18
+ function declarationCommand(declaration) {
19
+ const anchor = declaration.kind === 'import'
20
+ ? `--specifier ${quoteArg(declaration.specifier ?? '')}`
21
+ : `--kind registration --line ${quoteArg(declaration.line ?? '')}`;
22
+ return (`fireforge patch staged-dependency ${declaration.patch} --add --file ${declaration.file} ` +
23
+ `${anchor} --creates ${declaration.creates} --owner ${declaration.owner} ` +
24
+ STAGED_DEPENDENCY_REASON_PLACEHOLDER);
25
+ }
26
+ /**
27
+ * Renders the remedy lines. Falls back to the generic sentence when the
28
+ * findings carry no structured declarations.
29
+ *
30
+ * @param input - See {@link ForwardImportRemedyInput}
31
+ */
32
+ export function formatForwardImportRemedy(input) {
33
+ const declarations = input.issues
34
+ .filter((issue) => issue.check === 'forward-import' || issue.check === 'forward-registration')
35
+ .flatMap((issue) => issue.stagedDeclarations ?? []);
36
+ const ownerOrders = declarations
37
+ .map((declaration) => input.manifest?.patches.find((patch) => patch.filename === declaration.owner)?.order)
38
+ .filter((order) => order !== undefined);
39
+ const latestOwner = ownerOrders.length > 0 ? Math.max(...ownerOrders) : undefined;
40
+ const legalOrder = latestOwner !== undefined && input.manifest !== null && input.category !== undefined
41
+ ? earliestLegalOrderAfter(input.config ?? {}, input.manifest, input.category, latestOwner)
42
+ : null;
43
+ const orderText = legalOrder !== null
44
+ ? `"fireforge export <files> --order ${formatPatchOrder(legalOrder)}"; ` +
45
+ `${formatPatchOrder(legalOrder)} is the earliest legal order after the latest creator ` +
46
+ `(${formatPatchOrder(latestOwner ?? legalOrder)})`
47
+ : '"fireforge export --order <n>"';
48
+ const lines = [
49
+ `Export those files as their own later patch (${orderText}; or ` +
50
+ '"fireforge patch split --order <n>"), or declare the intentional dependency with ' +
51
+ `"fireforge patch staged-dependency --add" before ${input.reRunHint}` +
52
+ (declarations.length > 0 ? ':' : '.'),
53
+ ];
54
+ const seen = new Set();
55
+ for (const declaration of declarations) {
56
+ const command = declarationCommand(declaration);
57
+ if (seen.has(command))
58
+ continue;
59
+ seen.add(command);
60
+ lines.push(` ${command}`);
61
+ }
62
+ return lines.join('\n');
63
+ }
@@ -2,7 +2,7 @@
2
2
  import { watch as fsWatch } from 'node:fs';
3
3
  import { loadConfig } from '../../core/config.js';
4
4
  import { applyAllComponents, computeComponentChecksums } from '../../core/furnace-apply.js';
5
- import { logApplyResult } from '../../core/furnace-apply-output.js';
5
+ import { formatSkipBreakdown, logApplyResult } from '../../core/furnace-apply-output.js';
6
6
  import { getFurnacePaths } from '../../core/furnace-config.js';
7
7
  import { isComponentSourceFile } from '../../core/furnace-constants.js';
8
8
  import { runFurnaceMutation, waitLockMutationOptions } from '../../core/furnace-operation.js';
@@ -108,7 +108,7 @@ async function runWatchLoop(projectRoot) {
108
108
  }
109
109
  const applied = result.applied.length;
110
110
  const skipped = result.skipped.length;
111
- info(`Re-applied: ${applied} applied, ${skipped} skipped`);
111
+ info(`Re-applied: ${applied} applied, ${skipped} skipped${formatSkipBreakdown(result.skipped)}`);
112
112
  retryNeeded = false;
113
113
  lastChecksums = await snapshotWatchedChecksums(watchDirs);
114
114
  }
@@ -318,7 +318,7 @@ export async function furnaceApplyCommand(projectRoot, name, options = {}) {
318
318
  outro(`Dry run complete — would apply ${appliedCount}, skip ${skippedCount}`);
319
319
  }
320
320
  else {
321
- outro(`Applied ${appliedCount}, skipped ${skippedCount}`);
321
+ outro(`Applied ${appliedCount}, skipped ${skippedCount}${formatSkipBreakdown(result.skipped)}`);
322
322
  }
323
323
  // Watch mode: re-apply on file changes in component directories
324
324
  if (options.watch && !dryRun) {
@@ -2,8 +2,9 @@
2
2
  * `fireforge furnace chrome-doc remove <name>` removes the files and
3
3
  * registrations created by `furnace chrome-doc create`.
4
4
  */
5
+ import type { FurnaceLockWaitable } from '../../types/commands/index.js';
5
6
  /** Options for `furnace chrome-doc remove`. */
6
- export interface FurnaceChromeDocRemoveOptions {
7
+ export interface FurnaceChromeDocRemoveOptions extends FurnaceLockWaitable {
7
8
  /** Skip confirmation. Required for real non-interactive removal. */
8
9
  yes?: boolean;
9
10
  /** Print the removal plan without writing files. */
@@ -8,7 +8,7 @@ import { join } from 'node:path';
8
8
  import { confirm } from '@clack/prompts';
9
9
  import { loadConfig } from '../../core/config.js';
10
10
  import { stdioIsInteractive } from '../../core/destructive.js';
11
- import { completeJournalRollback, runFurnaceMutation, } from '../../core/furnace-operation.js';
11
+ import { completeJournalRollback, runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
12
12
  import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
13
13
  import { createRollbackJournal, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
14
14
  import { FurnaceError } from '../../errors/furnace.js';
@@ -138,7 +138,7 @@ export async function furnaceChromeDocRemoveCommand(projectRoot, name, options =
138
138
  plan,
139
139
  binaryName: forgeConfig.binaryName,
140
140
  operationContext: ctx,
141
- }));
141
+ }), waitLockMutationOptions(options.waitLockSeconds));
142
142
  info(`Removed ${result.removedFiles} source file${result.removedFiles === 1 ? '' : 's'} and ` +
143
143
  `${result.removedJarEntries} jar registration${result.removedJarEntries === 1 ? '' : 's'} for "${name}".`);
144
144
  if (result.removedTestDir) {
@@ -15,8 +15,9 @@
15
15
  * mid-scaffold restores every touched file. A successful run leaves the tree
16
16
  * ready for `fireforge build`.
17
17
  */
18
+ import type { FurnaceLockWaitable } from '../../types/commands/index.js';
18
19
  /** Options for `furnace chrome-doc create`. */
19
- export interface FurnaceChromeDocCreateOptions {
20
+ export interface FurnaceChromeDocCreateOptions extends FurnaceLockWaitable {
20
21
  /**
21
22
  * Emit the titlebar-buttonbox markup and leave platform window controls
22
23
  * visible. Defaults to `true` because the common case is a full chrome
@@ -19,7 +19,7 @@
19
19
  import { join } from 'node:path';
20
20
  import { loadConfig } from '../../core/config.js';
21
21
  import { furnaceConfigExists, loadFurnaceConfig } from '../../core/furnace-config.js';
22
- import { completeJournalRollback, runFurnaceMutation, } from '../../core/furnace-operation.js';
22
+ import { completeJournalRollback, runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
23
23
  import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
24
24
  import { createRollbackJournal, recordCreatedDir, snapshotFile, } from '../../core/furnace-rollback.js';
25
25
  import { DEFAULT_LICENSE, getLicenseHeader } from '../../core/license-headers.js';
@@ -343,7 +343,7 @@ export async function furnaceChromeDocCreateCommand(projectRoot, name, options =
343
343
  browserWindow,
344
344
  binaryName: forgeConfig.binaryName,
345
345
  operationContext: ctx,
346
- }));
346
+ }), waitLockMutationOptions(options.waitLockSeconds));
347
347
  const nextSteps = [
348
348
  ` 1. Edit engine/browser/base/content/${name}.xhtml and fill in the body.`,
349
349
  ` 2. Localize strings in engine/browser/locales/en-US/browser/${name}.ftl.`,
@@ -5,7 +5,7 @@ import { getProjectPaths, loadConfig } from '../../core/config.js';
5
5
  import { stdioIsInteractive } from '../../core/destructive.js';
6
6
  import { getFurnacePaths, writeFurnaceConfig } from '../../core/furnace-config.js';
7
7
  import { resolveFtlChromeSubPath, tagNameToClassName, WIDGETS_DIR, } from '../../core/furnace-constants.js';
8
- import { completeJournalRollback, runFurnaceMutation, } from '../../core/furnace-operation.js';
8
+ import { completeJournalRollback, runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
9
9
  import { CUSTOM_ELEMENT_TAG_PATTERN, CUSTOM_ELEMENT_TAG_RULES, describeTagNameProblem, } from '../../core/furnace-registration-validate.js';
10
10
  import { createRollbackJournal, recordCreatedDir, snapshotFile, } from '../../core/furnace-rollback.js';
11
11
  import { isComponentInEngine, scanWidgetsDirectory } from '../../core/furnace-scanner.js';
@@ -394,7 +394,7 @@ export async function furnaceCreateCommand(projectRoot, name, options = {}) {
394
394
  testStyle,
395
395
  testDir,
396
396
  operationContext: ctx,
397
- }));
397
+ }), waitLockMutationOptions(options.waitLockSeconds));
398
398
  note(formatSuccessNote({
399
399
  componentName,
400
400
  files,