@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
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { Command, Option } from 'commander';
3
3
  import { withEngineSessionLock } from '../../core/engine-session-lock.js';
4
- import { addWaitLockOption, pickDefined, resolveWaitLockSeconds } from '../../utils/options.js';
4
+ import { addWaitLockOption, pickDefined, resolveWaitLockSeconds, } from '../../utils/options.js';
5
5
  import { furnaceApplyCommand } from './apply.js';
6
6
  import { furnaceChromeDocCreateCommand } from './chrome-doc.js';
7
7
  import { furnaceChromeDocRemoveCommand } from './chrome-doc-remove.js';
@@ -19,6 +19,18 @@ import { furnaceScanCommand } from './scan.js';
19
19
  import { furnaceStatusCommand } from './status.js';
20
20
  import { furnaceSyncCommand } from './sync.js';
21
21
  import { furnaceValidateCommand } from './validate.js';
22
+ import { parseMaxWarnings } from './validate-report.js';
23
+ /**
24
+ * Splits the raw `--wait-lock` value off a furnace leaf's parsed options and
25
+ * resolves it into `waitLockSeconds` (flag, else `FIREFORGE_WAIT_LOCK`, else
26
+ * undefined) for the furnace lock `runFurnaceMutation` takes. Used by the
27
+ * leaves that take only Furnace's lock, where the flag used to be accepted
28
+ * and ignored.
29
+ */
30
+ function withFurnaceLockBudget(options) {
31
+ const { waitLock, ...rest } = options;
32
+ return pickDefined({ ...rest, waitLockSeconds: resolveWaitLockSeconds(waitLock) });
33
+ }
22
34
  async function runEngineLockedFurnaceCommand(context, command, operation, waitLockSeconds) {
23
35
  const projectRoot = context.getProjectRoot();
24
36
  await withEngineSessionLock(projectRoot, command, () => operation(projectRoot), {
@@ -51,16 +63,24 @@ function registerFurnaceApplyCommand(furnace, context) {
51
63
  function registerFurnaceDeployCommand(furnace, context) {
52
64
  const { getProjectRoot, withErrorHandling } = context;
53
65
  const deploy = furnace
54
- .command('deploy [name]')
55
- .description('Apply components and validate in one step')
66
+ .command('deploy [names...]')
67
+ .description('Apply components and validate in one step (name several to deploy them under one lock hold)')
56
68
  .option('--dry-run', 'Show what would be changed without writing (reads may overlap concurrent mutations)')
57
69
  .option('--force', 'Proceed despite baseVersion drift (stale overrides)')
58
- .option('--skip-validate', 'Skip the validation step (apply only)');
59
- addWaitLockOption(deploy).action(withErrorHandling(async (name, options) => {
70
+ .option('--skip-validate', 'Skip the validation step (apply only)')
71
+ .addHelpText('after', [
72
+ '',
73
+ 'Deploy registers every .mjs and .css file in a custom component directory in',
74
+ 'toolkit/content/jar.mn, including extra modules beside the main one (e.g.',
75
+ 'components/custom/<tag>/<tag>-rows.mjs). Add the file to the directory and run',
76
+ '`fireforge furnace deploy <tag>` instead of writing the jar.mn line by hand; a line',
77
+ 'that already exists anywhere in jar.mn is kept and never duplicated.',
78
+ ].join('\n'));
79
+ addWaitLockOption(deploy).action(withErrorHandling(async (names, options) => {
60
80
  const { waitLock, ...rest } = options ?? {};
61
81
  const waitLockSeconds = resolveWaitLockSeconds(waitLock);
62
82
  const parsed = pickDefined({ ...rest, waitLockSeconds });
63
- const run = (projectRoot) => furnaceDeployCommand(projectRoot, name, parsed);
83
+ const run = (projectRoot) => furnaceDeployCommand(projectRoot, names, parsed);
64
84
  if (parsed.dryRun === true) {
65
85
  await run(getProjectRoot());
66
86
  return;
@@ -103,15 +123,15 @@ function registerFurnaceInfoCommands(furnace, context) {
103
123
  }));
104
124
  registerFurnaceApplyCommand(furnace, context);
105
125
  registerFurnaceDeployCommand(furnace, context);
106
- furnace
126
+ const scan = furnace
107
127
  .command('scan')
108
128
  .description('Scan engine for available components. Report-only by default: persist the discovered ' +
109
129
  'inventory into the stock section of furnace.json with --track (or the interactive ' +
110
130
  'prompt); deploy/validate consume the inventory from there.')
111
131
  .option('--deep', 'Search additional Firefox directories beyond the default widgets path')
112
- .option('--track', 'Persist every discovered untracked component into the stock section of furnace.json (non-interactive)')
113
- .action(withErrorHandling(async (options) => {
114
- await furnaceScanCommand(getProjectRoot(), pickDefined(options));
132
+ .option('--track', 'Persist every discovered untracked component into the stock section of furnace.json (non-interactive)');
133
+ addWaitLockOption(scan).action(withErrorHandling(async (options) => {
134
+ await furnaceScanCommand(getProjectRoot(), withFurnaceLockBudget(options));
115
135
  }));
116
136
  furnace
117
137
  .command('init')
@@ -122,7 +142,7 @@ function registerFurnaceInfoCommands(furnace, context) {
122
142
  .action(withErrorHandling(async (options) => {
123
143
  await furnaceInitCommand(getProjectRoot(), options);
124
144
  }));
125
- furnace
145
+ const create = furnace
126
146
  .command('create [name]')
127
147
  .description('Create a new custom component')
128
148
  .option('-d, --description <desc>', 'Component description')
@@ -140,9 +160,9 @@ function registerFurnaceInfoCommands(furnace, context) {
140
160
  .option('--compose <tags>', 'Record stock tags composed internally (metadata only, comma-separated)', (val) => val.split(',').map((s) => s.trim()))
141
161
  .option('--shared-ftl <path>', 'Participate in an existing feature-scoped .ftl at this path (e.g. "browser/mybrowser-dock.ftl"); skips the per-component .ftl scaffold (implies --localized)')
142
162
  .option('--dry-run', 'Show the planned file set and furnace.json changes without writing')
143
- .option('--allow-prefix-mismatch', 'Create the component even when its name does not start with the configured `componentPrefix` in furnace.json. Without this flag the command refuses to write anything on a prefix mismatch.')
144
- .action(withErrorHandling(async (name, options) => {
145
- await furnaceCreateCommand(getProjectRoot(), name, options);
163
+ .option('--allow-prefix-mismatch', 'Create the component even when its name does not start with the configured `componentPrefix` in furnace.json. Without this flag the command refuses to write anything on a prefix mismatch.');
164
+ addWaitLockOption(create).action(withErrorHandling(async (name, options) => {
165
+ await furnaceCreateCommand(getProjectRoot(), name, withFurnaceLockBudget(options));
146
166
  }));
147
167
  registerChromeDocCommands(furnace, context);
148
168
  }
@@ -151,23 +171,23 @@ function registerChromeDocCommands(furnace, context) {
151
171
  const chromeDoc = furnace
152
172
  .command('chrome-doc')
153
173
  .description('Scaffold top-level chrome documents (xhtml + js + css + ftl + jar.mn)');
154
- chromeDoc
174
+ const chromeDocCreate = chromeDoc
155
175
  .command('create <name>')
156
176
  .description('Scaffold a new top-level chrome document')
157
177
  .option('--no-titlebar', 'Frameless overlay-style document (omits titlebar-buttonbox)')
158
178
  .option('--browser-window', 'Emit the browser.xhtml-like main-window skeleton (<html id="main-window"> with windowtype/chromehidden/persist root attributes platform C++ reads before scripts run) for the document configured as the fork\'s main browser window; implies the titlebar markup')
159
179
  .option('--with-tests', 'Scaffold an xpcshell packaging-verification test that probes XCurProcD/chrome/browser/... directly (bypasses the xpcshell chrome:// URI limitation).')
160
- .option('--dry-run', 'Show the chrome-doc scaffold plan without writing')
161
- .action(withErrorHandling(async (name, options) => {
162
- await furnaceChromeDocCreateCommand(getProjectRoot(), name, pickDefined(options));
180
+ .option('--dry-run', 'Show the chrome-doc scaffold plan without writing');
181
+ addWaitLockOption(chromeDocCreate).action(withErrorHandling(async (name, options) => {
182
+ await furnaceChromeDocCreateCommand(getProjectRoot(), name, withFurnaceLockBudget(options));
163
183
  }));
164
- chromeDoc
184
+ const chromeDocRemove = chromeDoc
165
185
  .command('remove <name>')
166
186
  .description('Remove a scaffolded top-level chrome document')
167
187
  .option('-y, --yes', 'Skip confirmation')
168
- .option('--dry-run', 'Show the chrome-doc removal plan without writing')
169
- .action(withErrorHandling(async (name, options) => {
170
- await furnaceChromeDocRemoveCommand(getProjectRoot(), name, pickDefined(options));
188
+ .option('--dry-run', 'Show the chrome-doc removal plan without writing');
189
+ addWaitLockOption(chromeDocRemove).action(withErrorHandling(async (name, options) => {
190
+ await furnaceChromeDocRemoveCommand(getProjectRoot(), name, withFurnaceLockBudget(options));
171
191
  }));
172
192
  }
173
193
  /**
@@ -178,12 +198,13 @@ function registerChromeDocCommands(furnace, context) {
178
198
  */
179
199
  function registerFurnaceModifyCommands(furnace, context) {
180
200
  const { getProjectRoot, withErrorHandling } = context;
181
- furnace
201
+ const override = furnace
182
202
  .command('override [names...]')
183
203
  .description('Fork one or more existing components for modification')
184
204
  .addOption(new Option('-t, --type <type>', 'Override type').choices(['css-only', 'full']))
185
- .option('-d, --description <desc>', 'Description')
186
- .action(withErrorHandling(async (names, options) => {
205
+ .option('-d, --description <desc>', 'Description');
206
+ addWaitLockOption(override).action(withErrorHandling(async (names, rawOptions) => {
207
+ const options = withFurnaceLockBudget(rawOptions);
187
208
  if (names.length <= 1) {
188
209
  await furnaceOverrideCommand(getProjectRoot(), names[0], options);
189
210
  }
@@ -198,26 +219,29 @@ function registerFurnaceModifyCommands(furnace, context) {
198
219
  .action(withErrorHandling(async (options) => {
199
220
  await furnaceListCommand(getProjectRoot(), options);
200
221
  }));
201
- furnace
222
+ const remove = furnace
202
223
  .command('remove <name>')
203
224
  .description('Remove a component from the workspace')
204
- .option('-y, --yes', 'Skip confirmation')
205
- .action(withErrorHandling(async (name, options) => {
206
- await furnaceRemoveCommand(getProjectRoot(), name, options);
225
+ .option('-y, --yes', 'Skip confirmation');
226
+ addWaitLockOption(remove).action(withErrorHandling(async (name, options) => {
227
+ await furnaceRemoveCommand(getProjectRoot(), name, withFurnaceLockBudget(options));
207
228
  }));
208
- furnace
229
+ const preview = furnace
209
230
  .command('preview')
210
231
  .description('Start component preview (Storybook)')
211
- .option('--install', 'Force reinstall Storybook dependencies')
212
- .action(withErrorHandling(async (options) => {
213
- await furnacePreviewCommand(getProjectRoot(), options);
232
+ .option('--install', 'Force reinstall Storybook dependencies');
233
+ addWaitLockOption(preview).action(withErrorHandling(async (options) => {
234
+ await furnacePreviewCommand(getProjectRoot(), withFurnaceLockBudget(options));
214
235
  }));
215
236
  furnace
216
237
  .command('validate [name]')
217
238
  .description('Run accessibility and compatibility checks')
218
239
  .option('--fix', 'Auto-fix registration issues (missing jar.mn entries, customElements.js)')
240
+ .option('--json', 'Write the result as one JSON document (findings, excepted findings, stale exceptions, verdict) to stdout')
241
+ .option('--max-warnings <n>', 'Fail when more than <n> warnings remain after furnace.json validationExceptions (0 for a warning-clean gate)')
219
242
  .action(withErrorHandling(async (name, options) => {
220
- await furnaceValidateCommand(getProjectRoot(), name, pickDefined(options ?? {}));
243
+ const { maxWarnings, ...rest } = options ?? {};
244
+ await furnaceValidateCommand(getProjectRoot(), name, pickDefined({ ...rest, maxWarnings: parseMaxWarnings(maxWarnings) }));
221
245
  }));
222
246
  furnace
223
247
  .command('diff [name]')
@@ -225,21 +249,22 @@ function registerFurnaceModifyCommands(furnace, context) {
225
249
  .action(withErrorHandling(async (name) => {
226
250
  await furnaceDiffCommand(getProjectRoot(), name);
227
251
  }));
228
- furnace
252
+ const refresh = furnace
229
253
  .command('refresh [name]')
230
254
  .description('Merge upstream Firefox changes into overrides (three-way merge)')
231
255
  .option('--dry-run', 'Show what would change without modifying files')
232
256
  .option('-a, --all', 'Refresh all overrides in a single batch')
233
257
  .addOption(new Option('-s, --strategy <strategy>', 'Auto-resolve conflicts (ours = keep local, theirs = accept upstream)').choices(['ours', 'theirs']))
234
- .option('--reset-base', 'Reset baseline to current engine HEAD (skips three-way merge, recovers from missing baseCommit)')
235
- .action(withErrorHandling(async (name, options) => {
236
- await furnaceRefreshCommand(getProjectRoot(), name, pickDefined(options));
258
+ .option('--reset-base', 'Reset baseline to current engine HEAD (skips three-way merge, recovers from missing baseCommit)');
259
+ addWaitLockOption(refresh).action(withErrorHandling(async (name, options) => {
260
+ await furnaceRefreshCommand(getProjectRoot(), name, withFurnaceLockBudget(options));
237
261
  }));
238
- furnace
262
+ const rename = furnace
239
263
  .command('rename <old-name> <new-name>')
240
264
  .description('Rename a component (updates files, config, and registrations)')
241
- .action(withErrorHandling(async (oldName, newName) => {
242
- await furnaceRenameCommand(getProjectRoot(), oldName, newName);
265
+ .option('--dry-run', 'Show the planned file moves, furnace.json change and engine registration edits without writing');
266
+ addWaitLockOption(rename).action(withErrorHandling(async (oldName, newName, options) => {
267
+ await furnaceRenameCommand(getProjectRoot(), oldName, newName, withFurnaceLockBudget(options));
243
268
  }));
244
269
  registerFurnaceSyncCommand(furnace, context);
245
270
  }
@@ -2,7 +2,7 @@
2
2
  import { stat } from 'node:fs/promises';
3
3
  import { basename, dirname, isAbsolute, join, posix } from 'node:path';
4
4
  import { text } from '@clack/prompts';
5
- import { getProjectPaths, loadConfig, mutateConfig, writeConfig } from '../../core/config.js';
5
+ import { getProjectPaths, loadConfig, loadRawConfigDocument, mutateConfig, validateConfig, withConfigFileLock, writeConfigDocument, } from '../../core/config.js';
6
6
  import { stdioIsInteractive } from '../../core/destructive.js';
7
7
  import { createDefaultFurnaceConfig, furnaceConfigExists, writeFurnaceConfig, } from '../../core/furnace-config.js';
8
8
  import { DEFAULT_LICENSE } from '../../core/license-headers.js';
@@ -239,14 +239,21 @@ async function scaffoldTokensCss(projectRoot) {
239
239
  // `fireforge lint` after a `token add` fails on raw-color-value issues for
240
240
  // tokens the operator just created. The add is idempotent, so re-running
241
241
  // `furnace init --force` does not duplicate the entry.
242
+ // The edit seeds from the raw document under the config lock, like
243
+ // `fireforge config`: a validated round-trip would drop keys written with
244
+ // `config --force`, and an unlocked one could lose a concurrent write.
242
245
  try {
243
246
  const existingAllowlist = forgeConfig.patchLint?.rawColorAllowlist ?? [];
244
247
  if (!existingAllowlist.includes(tokensCssPath)) {
245
- const updatedConfig = mutateConfig(forgeConfig, 'patchLint.rawColorAllowlist', [
246
- ...existingAllowlist,
247
- tokensCssPath,
248
- ]);
249
- await writeConfig(projectRoot, updatedConfig);
248
+ await withConfigFileLock(projectRoot, async () => {
249
+ const raw = await loadRawConfigDocument(projectRoot);
250
+ const current = validateConfig(raw).patchLint?.rawColorAllowlist ?? [];
251
+ if (current.includes(tokensCssPath))
252
+ return;
253
+ const updated = mutateConfig(raw, 'patchLint.rawColorAllowlist', [...current, tokensCssPath], true);
254
+ validateConfig(updated);
255
+ await writeConfigDocument(projectRoot, updated);
256
+ });
250
257
  info(`Added ${tokensCssPath} to patchLint.rawColorAllowlist`);
251
258
  }
252
259
  }
@@ -6,7 +6,7 @@ import { getProjectPaths, loadConfig, loadState } from '../../core/config.js';
6
6
  import { stdioIsInteractive } from '../../core/destructive.js';
7
7
  import { getFurnacePaths, writeFurnaceConfig } from '../../core/furnace-config.js';
8
8
  import { resolveFtlDir } from '../../core/furnace-constants.js';
9
- import { completeJournalRollback, runFurnaceMutation } from '../../core/furnace-operation.js';
9
+ import { completeJournalRollback, runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
10
10
  import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
11
11
  import { CUSTOM_ELEMENT_TAG_PATTERN, CUSTOM_ELEMENT_TAG_RULES, } from '../../core/furnace-registration-validate.js';
12
12
  import { createRollbackJournal, recordCreatedDir, snapshotFile, } from '../../core/furnace-rollback.js';
@@ -160,7 +160,7 @@ async function performOverrideMutations(args) {
160
160
  subject: `component "${args.componentName}"`,
161
161
  });
162
162
  }
163
- });
163
+ }, waitLockMutationOptions(args.waitLockSeconds));
164
164
  }
165
165
  /**
166
166
  * Throws if `componentName` is already classified as something `override`
@@ -322,6 +322,7 @@ export async function furnaceOverrideCommand(projectRoot, name, options = {}) {
322
322
  ftlDir,
323
323
  firefoxVersion: forgeConfig.firefox.version,
324
324
  ...(state.baseCommit ? { baseCommit: state.baseCommit } : {}),
325
+ waitLockSeconds: options.waitLockSeconds,
325
326
  });
326
327
  // --- Success ---
327
328
  note(`Files copied to components/overrides/${componentName}/:\n` +
@@ -421,6 +422,7 @@ export async function furnaceBatchOverrideCommand(projectRoot, names, options =
421
422
  ftlDir,
422
423
  firefoxVersion: forgeConfig.firefox.version,
423
424
  ...(state.baseCommit ? { baseCommit: state.baseCommit } : {}),
425
+ waitLockSeconds: options.waitLockSeconds,
424
426
  });
425
427
  succeeded.push(componentName);
426
428
  }
@@ -2,7 +2,7 @@
2
2
  import { join } from 'node:path';
3
3
  import { applyAllComponents } from '../../core/furnace-apply.js';
4
4
  import { updateFurnaceState } from '../../core/furnace-config.js';
5
- import { runFurnaceMutation } from '../../core/furnace-operation.js';
5
+ import { runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
6
6
  import { assertFurnaceReady } from '../../core/furnace-precondition.js';
7
7
  import { restoreRollbackJournal } from '../../core/furnace-rollback.js';
8
8
  import { countEntriesWithBlockingStepErrors } from '../../core/furnace-step-errors.js';
@@ -224,7 +224,7 @@ export async function furnacePreviewCommand(projectRoot, options = {}) {
224
224
  // entrypoint's global signal handlers consulting the lifecycle registry.
225
225
  // The body's own try/catch + teardown path handles the normal exit case
226
226
  // (mach storybook returns or throws).
227
- await runFurnaceMutation(projectRoot, 'preview-teardown', async (ctx) => {
227
+ const mutate = async (ctx) => {
228
228
  // Register the stories cleanup as an extra teardown hook so the signal
229
229
  // handler can wipe the staged stories directory in addition to the
230
230
  // journal restore.
@@ -336,7 +336,8 @@ export async function furnacePreviewCommand(projectRoot, options = {}) {
336
336
  // holds and downstream formatters always see a message/stack pair.
337
337
  throw toError(primaryError);
338
338
  }
339
- });
339
+ };
340
+ await runFurnaceMutation(projectRoot, 'preview-teardown', mutate, waitLockMutationOptions(options.waitLockSeconds));
340
341
  if (previewResult &&
341
342
  previewResult.exitCode !== 0 &&
342
343
  previewResult.exitCode !== 130 &&
@@ -6,7 +6,7 @@ import { getOverrideEngineTargetPath } from '../../core/furnace-apply-helpers.js
6
6
  import { getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig, } from '../../core/furnace-config.js';
7
7
  import { resolveFtlDir } from '../../core/furnace-constants.js';
8
8
  import { isComponentSourceFile } from '../../core/furnace-constants.js';
9
- import { completeJournalRollback, runFurnaceMutation } from '../../core/furnace-operation.js';
9
+ import { completeJournalRollback, runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
10
10
  import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
11
11
  import { refreshOverrideFile } from '../../core/furnace-refresh.js';
12
12
  import { createRollbackJournal, restoreRollbackJournal, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
@@ -165,7 +165,7 @@ async function refreshSingleOverride(projectRoot, name, options = {}) {
165
165
  }
166
166
  throw error;
167
167
  }
168
- }, { dryRun });
168
+ }, { dryRun, ...waitLockMutationOptions(options.waitLockSeconds) });
169
169
  return { results, currentVersion };
170
170
  }
171
171
  /**
@@ -6,7 +6,7 @@ import { getProjectPaths, loadConfig } from '../../core/config.js';
6
6
  import { stdioIsInteractive } from '../../core/destructive.js';
7
7
  import { getFurnacePaths, loadFurnaceConfig, loadFurnaceState, updateFurnaceState, writeFurnaceConfig, } from '../../core/furnace-config.js';
8
8
  import { resolveFtlDir, xpcshellTestParentDir } from '../../core/furnace-constants.js';
9
- import { completeJournalRollback, runFurnaceMutation } from '../../core/furnace-operation.js';
9
+ import { completeJournalRollback, runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
10
10
  import { createRollbackJournal, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
11
11
  import { isGitRepository } from '../../core/git.js';
12
12
  import { deregisterTestManifest } from '../../core/moz-manifest-register.js';
@@ -343,7 +343,7 @@ export async function furnaceRemoveCommand(projectRoot, name, options = {}) {
343
343
  // rollback pathway.
344
344
  const paths = getProjectPaths(projectRoot);
345
345
  await requireGitEngineForRemove(type, name, paths.engine);
346
- await runFurnaceMutation(projectRoot, 'remove-rollback', async (ctx) => {
346
+ const mutate = async (ctx) => {
347
347
  const journal = createRollbackJournal();
348
348
  ctx.registerJournal(journal);
349
349
  try {
@@ -408,7 +408,8 @@ export async function furnaceRemoveCommand(projectRoot, name, options = {}) {
408
408
  subject: `component "${name}"`,
409
409
  });
410
410
  }
411
- });
411
+ };
412
+ await runFurnaceMutation(projectRoot, 'remove-rollback', mutate, waitLockMutationOptions(options.waitLockSeconds));
412
413
  info(`Removed "${name}" from furnace.json`);
413
414
  outro('Component removed');
414
415
  }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `furnace rename --dry-run`: the plan a rename would carry out, rendered
3
+ * from reads only. A rename edits three engine registration files besides
4
+ * moving the component, and before this none of those edits was visible
5
+ * until it had run.
6
+ */
7
+ import type { FurnaceConfig } from '../../types/furnace.js';
8
+ /** Inputs for {@link renderRenamePlan}. */
9
+ export interface RenamePlanInput {
10
+ config: FurnaceConfig;
11
+ oldName: string;
12
+ newName: string;
13
+ /** Workspace directory of the component being renamed. */
14
+ oldDir: string;
15
+ /** On-disk directory label: `custom` or `overrides`. */
16
+ componentDirLabel: 'custom' | 'overrides';
17
+ engineDir: string;
18
+ /** Project marker a freshly added registration line would carry. */
19
+ markerComment: string | undefined;
20
+ }
21
+ /**
22
+ * Renders the rename plan: workspace file moves, the furnace.json re-key,
23
+ * and, for a custom component, every engine edit the real run would make
24
+ * (registrations only when the component is registered).
25
+ *
26
+ * @param input - See {@link RenamePlanInput}
27
+ * @returns The plan as display lines joined by newlines
28
+ */
29
+ export declare function renderRenamePlan(input: RenamePlanInput): Promise<string>;
@@ -0,0 +1,127 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * `furnace rename --dry-run`: the plan a rename would carry out, rendered
4
+ * from reads only. A rename edits three engine registration files besides
5
+ * moving the component, and before this none of those edits was visible
6
+ * until it had run.
7
+ */
8
+ import { readdir } from 'node:fs/promises';
9
+ import { join } from 'node:path';
10
+ import { isComponentSourceFile, JAR_MN, resolveFtlChromeSubPath, resolveFtlDir, resolveFtlLocaleJarMnPath, } from '../../core/furnace-constants.js';
11
+ import { hasLocaleFtlJarMnEntry } from '../../core/furnace-registration.js';
12
+ import { isTagAlreadyRegistered } from '../../core/furnace-registration-ast.js';
13
+ import { pathExists, readText } from '../../utils/fs.js';
14
+ import { renameComponentFileName, renameTagInDescription } from './rename-helpers.js';
15
+ /** Plan lines for the workspace move and the furnace.json re-key. */
16
+ async function workspaceLines(input) {
17
+ const { config, oldName, newName, oldDir, componentDirLabel } = input;
18
+ const lines = [
19
+ `Workspace: components/${componentDirLabel}/${oldName}/ → components/${componentDirLabel}/${newName}/`,
20
+ ];
21
+ const entries = (await readdir(oldDir, { withFileTypes: true }))
22
+ .filter((entry) => entry.isFile())
23
+ .map((entry) => entry.name)
24
+ .sort();
25
+ for (const fileName of entries) {
26
+ const renamed = renameComponentFileName(fileName, oldName, newName);
27
+ const rewrite = isComponentSourceFile(fileName) ? ' (tag and class references rewritten)' : '';
28
+ lines.push(` ${fileName}${renamed === fileName ? '' : ` → ${renamed}`}${rewrite}`);
29
+ }
30
+ const entry = config.custom[oldName] ?? config.overrides[oldName];
31
+ lines.push(`furnace.json: "${oldName}" → "${newName}"`);
32
+ if (entry) {
33
+ const description = renameTagInDescription(entry.description, oldName, newName);
34
+ if (description !== entry.description) {
35
+ lines.push(` description: "${entry.description}" → "${description}"`);
36
+ }
37
+ }
38
+ return lines;
39
+ }
40
+ /** Plan line for the customElements.js registration. */
41
+ async function customElementsLine(input) {
42
+ const { engineDir, oldName, newName, markerComment } = input;
43
+ const path = join(engineDir, 'toolkit/content/customElements.js');
44
+ if (!(await pathExists(path)))
45
+ return undefined;
46
+ const content = await readText(path);
47
+ const oldRegistered = isTagAlreadyRegistered(content, oldName);
48
+ if (isTagAlreadyRegistered(content, newName)) {
49
+ return `customElements.js: "${newName}" is already registered${oldRegistered ? `; remove the "${oldName}" entry` : ''}`;
50
+ }
51
+ if (oldRegistered) {
52
+ return `customElements.js: rewrite the "${oldName}" entry in place as "${newName}" (position and marker comments kept)`;
53
+ }
54
+ const marker = markerComment !== undefined ? ` marked // ${markerComment}:` : '';
55
+ return `customElements.js: add a "${newName}" entry${marker}`;
56
+ }
57
+ /** Plan line for the toolkit/content/jar.mn widget lines. */
58
+ async function jarLine(input) {
59
+ const { engineDir, oldName, newName, oldDir } = input;
60
+ const path = join(engineDir, JAR_MN);
61
+ if (!(await pathExists(path)))
62
+ return undefined;
63
+ const oldLines = (await readText(path))
64
+ .split('\n')
65
+ .filter((line) => line.includes(`(widgets/${oldName}/`)).length;
66
+ const newFiles = (await readdir(oldDir, { withFileTypes: true }))
67
+ .filter((entry) => entry.isFile() && /\.(?:mjs|css)$/.test(entry.name))
68
+ .map((entry) => renameComponentFileName(entry.name, oldName, newName))
69
+ .sort();
70
+ return (`${JAR_MN}: replace ${oldLines} "${oldName}" line(s) with ` +
71
+ (newFiles.length > 0 ? newFiles.join(', ') : 'none (no .mjs/.css files)'));
72
+ }
73
+ /** Plan lines for the deployed .ftl and its locale jar.mn line. */
74
+ async function localeLines(input, componentConfig) {
75
+ const { config, engineDir, oldName, newName } = input;
76
+ if (!componentConfig.localized)
77
+ return [];
78
+ const ftlDir = resolveFtlDir(config.ftlBasePath);
79
+ const lines = [];
80
+ if (await pathExists(join(engineDir, ftlDir, `${oldName}.ftl`))) {
81
+ lines.push(`${ftlDir}/${oldName}.ftl → ${newName}.ftl`);
82
+ }
83
+ const chromeSubPath = resolveFtlChromeSubPath(ftlDir);
84
+ const localeJarRel = resolveFtlLocaleJarMnPath(ftlDir);
85
+ if (chromeSubPath === undefined || localeJarRel === undefined)
86
+ return lines;
87
+ const present = await hasLocaleFtlJarMnEntry(engineDir, localeJarRel, oldName, chromeSubPath);
88
+ if (componentConfig.sharedFtl !== undefined) {
89
+ lines.push(`${localeJarRel}: no per-component line (sharedFtl: ${componentConfig.sharedFtl})` +
90
+ (present ? `; remove the stale "${oldName}.ftl" line` : ''));
91
+ }
92
+ else {
93
+ lines.push(present
94
+ ? `${localeJarRel}: re-point the "${oldName}.ftl" line to "${newName}.ftl"`
95
+ : `${localeJarRel}: no "${oldName}.ftl" line to re-point (left unchanged)`);
96
+ }
97
+ return lines;
98
+ }
99
+ /**
100
+ * Renders the rename plan: workspace file moves, the furnace.json re-key,
101
+ * and, for a custom component, every engine edit the real run would make
102
+ * (registrations only when the component is registered).
103
+ *
104
+ * @param input - See {@link RenamePlanInput}
105
+ * @returns The plan as display lines joined by newlines
106
+ */
107
+ export async function renderRenamePlan(input) {
108
+ const lines = await workspaceLines(input);
109
+ const componentConfig = input.config.custom[input.oldName];
110
+ if (!componentConfig || !(await pathExists(input.engineDir))) {
111
+ return lines.join('\n');
112
+ }
113
+ lines.push('Engine:');
114
+ const engineLines = componentConfig.register
115
+ ? [
116
+ await customElementsLine(input),
117
+ await jarLine(input),
118
+ ...(await localeLines(input, componentConfig)),
119
+ ].filter((line) => line !== undefined)
120
+ : ['registrations untouched (register: false)'];
121
+ if (await pathExists(join(input.engineDir, componentConfig.targetPath))) {
122
+ engineLines.push(`remove the stale deployed directory ${componentConfig.targetPath}/`);
123
+ }
124
+ lines.push(...engineLines.map((line) => ` ${line}`));
125
+ lines.push('Test scaffolds named after the old tag are renamed when present.');
126
+ return lines.join('\n');
127
+ }
@@ -3,6 +3,13 @@ import type { FurnaceConfig } from '../../types/furnace.js';
3
3
  * Applies a component rename to scaffold-owned filenames only.
4
4
  */
5
5
  export declare function renameComponentFileName(fileName: string, oldName: string, newName: string): string;
6
+ /**
7
+ * Rewrites whole-tag mentions of the old name in a component description,
8
+ * so a rename does not carry a description that still names the old tag.
9
+ * Boundary-aware like the source rewrite: `moz-panel` inside
10
+ * `moz-panel-group` is left alone.
11
+ */
12
+ export declare function renameTagInDescription(description: string, oldName: string, newName: string): string;
6
13
  /**
7
14
  * Re-keys a custom component config entry and same-config compose references.
8
15
  */
@@ -1,5 +1,6 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { updateFurnaceState } from '../../core/furnace-config.js';
3
+ import { escapeRegex } from '../../utils/regex.js';
3
4
  /**
4
5
  * Applies a component rename to scaffold-owned filenames only.
5
6
  */
@@ -11,6 +12,15 @@ export function renameComponentFileName(fileName, oldName, newName) {
11
12
  }
12
13
  return fileName;
13
14
  }
15
+ /**
16
+ * Rewrites whole-tag mentions of the old name in a component description,
17
+ * so a rename does not carry a description that still names the old tag.
18
+ * Boundary-aware like the source rewrite: `moz-panel` inside
19
+ * `moz-panel-group` is left alone.
20
+ */
21
+ export function renameTagInDescription(description, oldName, newName) {
22
+ return description.replace(new RegExp(`(?<![\\w-])${escapeRegex(oldName)}(?![\\w-])`, 'g'), newName);
23
+ }
14
24
  /**
15
25
  * Re-keys a custom component config entry and same-config compose references.
16
26
  */
@@ -20,6 +30,7 @@ export function updateConfigForCustomRename(config, oldName, newName) {
20
30
  return;
21
31
  config.custom[newName] = {
22
32
  ...oldConfig,
33
+ description: renameTagInDescription(oldConfig.description, oldName, newName),
23
34
  targetPath: oldConfig.targetPath.replace(new RegExp(`(^|/)${oldName}$`), `$1${newName}`),
24
35
  };
25
36
  // eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- idiomatic key removal from config record
@@ -37,7 +48,10 @@ export function updateConfigForOverrideRename(config, oldName, newName) {
37
48
  const oldConfig = config.overrides[oldName];
38
49
  if (!oldConfig)
39
50
  return;
40
- config.overrides[newName] = { ...oldConfig };
51
+ config.overrides[newName] = {
52
+ ...oldConfig,
53
+ description: renameTagInDescription(oldConfig.description, oldName, newName),
54
+ };
41
55
  // eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- idiomatic key removal from config record
42
56
  delete config.overrides[oldName];
43
57
  }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Engine registration edits for `furnace rename`: the customElements.js
3
+ * entry (rewritten in place, keeping its marker comments), the
4
+ * toolkit/content/jar.mn widget lines, the deployed `.ftl`, and the locale
5
+ * jar.mn line (re-pointed only when one existed, never for `sharedFtl`).
6
+ */
7
+ import { type RollbackJournal } from '../../core/furnace-rollback.js';
8
+ import type { CustomComponentConfig } from '../../types/furnace.js';
9
+ /**
10
+ * The project marker (`// <MARKER>:`) a registration line written by the
11
+ * rename carries, resolved exactly as `furnace apply` resolves it. An
12
+ * unreadable fireforge.json degrades to no marker rather than failing the
13
+ * rename.
14
+ */
15
+ export declare function loadMarkerComment(projectRoot: string): Promise<string | undefined>;
16
+ /** Inputs for {@link updateEngineRegistrations}. */
17
+ export interface UpdateEngineRegistrationsOptions {
18
+ /** Absolute path to the engine checkout. */
19
+ engineDir: string;
20
+ /** Tag name being renamed away from. */
21
+ oldName: string;
22
+ /** Tag name being renamed to. */
23
+ newName: string;
24
+ /** Workspace directory holding the renamed component's files. */
25
+ newDir: string;
26
+ /** Engine-relative directory localized components deploy their `.ftl` into. */
27
+ ftlDir: string;
28
+ /** The renamed component's furnace.json entry (`localized`, `sharedFtl`). */
29
+ componentConfig: CustomComponentConfig;
30
+ /** Project marker for a registration line the rename has to add fresh. */
31
+ markerComment: string | undefined;
32
+ /** Rollback journal every write is recorded in. */
33
+ journal: RollbackJournal;
34
+ }
35
+ /**
36
+ * Re-points the engine-side registrations (customElements.js, jar.mn, and
37
+ * the localized `.ftl` entries) from the old tag name to the new one.
38
+ *
39
+ * @param options - See {@link UpdateEngineRegistrationsOptions}
40
+ */
41
+ export declare function updateEngineRegistrations(options: UpdateEngineRegistrationsOptions): Promise<void>;