@hominis/fireforge 0.44.4 → 0.46.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 (378) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/dist/bin/fireforge.js +6 -0
  3. package/dist/build-info.json +4 -4
  4. package/dist/src/cli-usage-error.d.ts +46 -0
  5. package/dist/src/cli-usage-error.js +81 -0
  6. package/dist/src/cli.d.ts +5 -1
  7. package/dist/src/cli.js +20 -21
  8. package/dist/src/commands/bootstrap.js +1 -2
  9. package/dist/src/commands/build.js +15 -6
  10. package/dist/src/commands/destructive-decision.d.ts +19 -0
  11. package/dist/src/commands/destructive-decision.js +31 -0
  12. package/dist/src/commands/doctor-check-core.d.ts +0 -6
  13. package/dist/src/commands/doctor-check-core.js +0 -8
  14. package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
  15. package/dist/src/commands/doctor-orphaned-harness.js +4 -12
  16. package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
  17. package/dist/src/commands/doctor-source-pin.d.ts +29 -0
  18. package/dist/src/commands/doctor-source-pin.js +81 -0
  19. package/dist/src/commands/doctor.d.ts +2 -0
  20. package/dist/src/commands/doctor.js +9 -8
  21. package/dist/src/commands/download.js +35 -24
  22. package/dist/src/commands/export-all.js +21 -4
  23. package/dist/src/commands/export-flow.js +15 -60
  24. package/dist/src/commands/export-placement-conflicts.js +3 -3
  25. package/dist/src/commands/export-placement-gate.js +2 -8
  26. package/dist/src/commands/export-placement-policy.js +3 -5
  27. package/dist/src/commands/export-shared.d.ts +42 -23
  28. package/dist/src/commands/export-shared.js +4 -19
  29. package/dist/src/commands/export.js +10 -1
  30. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  31. package/dist/src/commands/furnace/authoring-config.js +19 -0
  32. package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
  33. package/dist/src/commands/furnace/create-browser-test.js +3 -8
  34. package/dist/src/commands/furnace/create-dry-run.js +2 -5
  35. package/dist/src/commands/furnace/create-templates.d.ts +17 -1
  36. package/dist/src/commands/furnace/create-templates.js +2 -1
  37. package/dist/src/commands/furnace/create.js +25 -19
  38. package/dist/src/commands/furnace/deploy.js +11 -2
  39. package/dist/src/commands/furnace/init.js +2 -1
  40. package/dist/src/commands/furnace/override.js +28 -19
  41. package/dist/src/commands/furnace/refresh.js +9 -1
  42. package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
  43. package/dist/src/commands/furnace/remove-mutations.js +0 -1
  44. package/dist/src/commands/furnace/remove.js +2 -6
  45. package/dist/src/commands/furnace/rename-browser-test.js +1 -7
  46. package/dist/src/commands/furnace/rename.js +29 -29
  47. package/dist/src/commands/furnace/status.js +12 -9
  48. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  49. package/dist/src/commands/furnace/test-file-name.js +39 -0
  50. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  51. package/dist/src/commands/furnace/validation-output.js +3 -8
  52. package/dist/src/commands/import.js +2 -9
  53. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  54. package/dist/src/commands/lint-per-patch.js +19 -4
  55. package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
  56. package/dist/src/commands/lint.d.ts +2 -1
  57. package/dist/src/commands/lint.js +1 -1
  58. package/dist/src/commands/manifest.js +1 -1
  59. package/dist/src/commands/package.js +2 -4
  60. package/dist/src/commands/patch/compact.js +14 -15
  61. package/dist/src/commands/patch/delete.js +15 -25
  62. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  63. package/dist/src/commands/patch/entry-projection.js +24 -0
  64. package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
  65. package/dist/src/commands/patch/lint-ignore.js +19 -29
  66. package/dist/src/commands/patch/move-files-create.js +24 -10
  67. package/dist/src/commands/patch/move-files-into.js +38 -37
  68. package/dist/src/commands/patch/move-files.js +4 -13
  69. package/dist/src/commands/patch/rename.js +27 -41
  70. package/dist/src/commands/patch/reorder.js +25 -38
  71. package/dist/src/commands/patch/split-plan.d.ts +4 -5
  72. package/dist/src/commands/patch/split-plan.js +4 -11
  73. package/dist/src/commands/patch/split.js +45 -43
  74. package/dist/src/commands/patch/staged-dependency.js +16 -22
  75. package/dist/src/commands/patch/tier.js +15 -25
  76. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  77. package/dist/src/commands/patch-rename-map.js +36 -0
  78. package/dist/src/commands/pending-resolution.d.ts +15 -0
  79. package/dist/src/commands/pending-resolution.js +25 -0
  80. package/dist/src/commands/re-export-adjacent.d.ts +32 -2
  81. package/dist/src/commands/re-export-adjacent.js +51 -10
  82. package/dist/src/commands/re-export-bulk-scan.js +6 -10
  83. package/dist/src/commands/re-export-drift.d.ts +8 -0
  84. package/dist/src/commands/re-export-drift.js +65 -4
  85. package/dist/src/commands/re-export-files.d.ts +2 -3
  86. package/dist/src/commands/re-export-files.js +38 -32
  87. package/dist/src/commands/re-export-lint.d.ts +3 -4
  88. package/dist/src/commands/re-export-lint.js +9 -2
  89. package/dist/src/commands/re-export-options.js +19 -5
  90. package/dist/src/commands/re-export-register.js +5 -1
  91. package/dist/src/commands/re-export-scan.js +8 -8
  92. package/dist/src/commands/re-export-single.d.ts +22 -3
  93. package/dist/src/commands/re-export-single.js +19 -11
  94. package/dist/src/commands/re-export.js +44 -27
  95. package/dist/src/commands/rebase/abort.js +3 -8
  96. package/dist/src/commands/rebase/continue.js +12 -12
  97. package/dist/src/commands/rebase/index.js +2 -2
  98. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  99. package/dist/src/commands/rebase/patch-loop.js +2 -7
  100. package/dist/src/commands/resolve.js +18 -10
  101. package/dist/src/commands/run.d.ts +0 -13
  102. package/dist/src/commands/run.js +12 -25
  103. package/dist/src/commands/setup-support.d.ts +16 -4
  104. package/dist/src/commands/setup-support.js +16 -16
  105. package/dist/src/commands/setup.js +3 -9
  106. package/dist/src/commands/source.js +8 -1
  107. package/dist/src/commands/status-check.js +2 -2
  108. package/dist/src/commands/status-output.js +2 -2
  109. package/dist/src/commands/status.js +3 -4
  110. package/dist/src/commands/test-build-phase.d.ts +2 -2
  111. package/dist/src/commands/test-build-phase.js +33 -8
  112. package/dist/src/commands/test-diagnose.js +14 -5
  113. package/dist/src/commands/test-modes.d.ts +2 -2
  114. package/dist/src/commands/test-modes.js +0 -1
  115. package/dist/src/commands/test-register.js +6 -3
  116. package/dist/src/commands/test-run.js +29 -21
  117. package/dist/src/commands/test-stale-gate.js +8 -5
  118. package/dist/src/commands/test-verdict.d.ts +16 -2
  119. package/dist/src/commands/test-verdict.js +18 -3
  120. package/dist/src/commands/test.js +51 -7
  121. package/dist/src/commands/token-list.d.ts +26 -0
  122. package/dist/src/commands/token-list.js +153 -0
  123. package/dist/src/commands/token.js +17 -1
  124. package/dist/src/commands/tree.js +7 -2
  125. package/dist/src/commands/typecheck.js +9 -9
  126. package/dist/src/commands/wire.js +4 -3
  127. package/dist/src/core/branding.d.ts +0 -7
  128. package/dist/src/core/branding.js +3 -2
  129. package/dist/src/core/browser-wire.js +6 -3
  130. package/dist/src/core/build-audit-platform.d.ts +29 -0
  131. package/dist/src/core/build-audit-platform.js +140 -0
  132. package/dist/src/core/build-audit-registration.js +3 -2
  133. package/dist/src/core/build-audit-skip.d.ts +57 -0
  134. package/dist/src/core/build-audit-skip.js +112 -0
  135. package/dist/src/core/build-audit.d.ts +15 -2
  136. package/dist/src/core/build-audit.js +154 -12
  137. package/dist/src/core/build-baseline.d.ts +45 -28
  138. package/dist/src/core/build-baseline.js +6 -29
  139. package/dist/src/core/build-jar-escalation.d.ts +64 -0
  140. package/dist/src/core/build-jar-escalation.js +128 -0
  141. package/dist/src/core/build-overwrite-guard.js +1 -1
  142. package/dist/src/core/build-prepare.d.ts +5 -2
  143. package/dist/src/core/build-prepare.js +34 -6
  144. package/dist/src/core/config-paths.d.ts +2 -6
  145. package/dist/src/core/config-paths.js +4 -2
  146. package/dist/src/core/config-state.d.ts +0 -6
  147. package/dist/src/core/config-state.js +1 -19
  148. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  149. package/dist/src/core/config-validate-build-audit.js +80 -0
  150. package/dist/src/core/config-validate-file-size.d.ts +12 -0
  151. package/dist/src/core/config-validate-file-size.js +65 -0
  152. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  153. package/dist/src/core/config-validate-patch-policy.js +3 -3
  154. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  155. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  156. package/dist/src/core/config-validate.js +20 -3
  157. package/dist/src/core/config.d.ts +2 -2
  158. package/dist/src/core/config.js +11 -15
  159. package/dist/src/core/coverage-extend.js +4 -4
  160. package/dist/src/core/discard-baseline.js +7 -7
  161. package/dist/src/core/engine-precondition.d.ts +1 -9
  162. package/dist/src/core/engine-precondition.js +3 -8
  163. package/dist/src/core/engine-session-lock.js +13 -17
  164. package/dist/src/core/export-moved-code-shape.js +2 -2
  165. package/dist/src/core/file-lock-owner.d.ts +103 -0
  166. package/dist/src/core/file-lock-owner.js +259 -0
  167. package/dist/src/core/file-lock.d.ts +2 -7
  168. package/dist/src/core/file-lock.js +40 -91
  169. package/dist/src/core/firefox-cache.d.ts +13 -1
  170. package/dist/src/core/firefox-cache.js +59 -34
  171. package/dist/src/core/firefox-download.d.ts +15 -0
  172. package/dist/src/core/firefox-download.js +37 -21
  173. package/dist/src/core/firefox-extract.d.ts +6 -5
  174. package/dist/src/core/firefox-extract.js +36 -18
  175. package/dist/src/core/firefox.d.ts +30 -32
  176. package/dist/src/core/firefox.js +5 -36
  177. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  178. package/dist/src/core/furnace-apply-dry-run.js +1 -7
  179. package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
  180. package/dist/src/core/furnace-apply-helpers.js +8 -19
  181. package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
  182. package/dist/src/core/furnace-apply.d.ts +1 -1
  183. package/dist/src/core/furnace-apply.js +30 -10
  184. package/dist/src/core/furnace-config.d.ts +1 -1
  185. package/dist/src/core/furnace-config.js +23 -36
  186. package/dist/src/core/furnace-constants.js +3 -2
  187. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  188. package/dist/src/core/furnace-dir-entry.js +21 -0
  189. package/dist/src/core/furnace-operation.d.ts +2 -0
  190. package/dist/src/core/furnace-operation.js +3 -1
  191. package/dist/src/core/furnace-refresh.d.ts +18 -6
  192. package/dist/src/core/furnace-refresh.js +3 -6
  193. package/dist/src/core/furnace-registration-ast.d.ts +0 -1
  194. package/dist/src/core/furnace-registration-ast.js +0 -3
  195. package/dist/src/core/furnace-registration.d.ts +2 -1
  196. package/dist/src/core/furnace-registration.js +22 -29
  197. package/dist/src/core/furnace-scanner.js +3 -6
  198. package/dist/src/core/furnace-state-persist.d.ts +2 -2
  199. package/dist/src/core/furnace-validate-registration.js +5 -7
  200. package/dist/src/core/git-base.d.ts +3 -1
  201. package/dist/src/core/git-base.js +25 -3
  202. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  203. package/dist/src/core/git-diff-new-file.js +119 -0
  204. package/dist/src/core/git-diff.d.ts +9 -9
  205. package/dist/src/core/git-diff.js +88 -157
  206. package/dist/src/core/git-file-ops.d.ts +0 -7
  207. package/dist/src/core/git-file-ops.js +4 -28
  208. package/dist/src/core/git-readonly-index.d.ts +0 -2
  209. package/dist/src/core/git-readonly-index.js +0 -4
  210. package/dist/src/core/git-status.d.ts +7 -0
  211. package/dist/src/core/git-status.js +21 -9
  212. package/dist/src/core/git.d.ts +0 -6
  213. package/dist/src/core/git.js +14 -43
  214. package/dist/src/core/harness-orphans.d.ts +73 -0
  215. package/dist/src/core/harness-orphans.js +187 -0
  216. package/dist/src/core/history-log.d.ts +19 -0
  217. package/dist/src/core/history-log.js +29 -0
  218. package/dist/src/core/mach-error-hints.d.ts +0 -5
  219. package/dist/src/core/mach-error-hints.js +1 -1
  220. package/dist/src/core/mach-known-noise-filter.d.ts +25 -3
  221. package/dist/src/core/mach-known-noise-filter.js +31 -10
  222. package/dist/src/core/mach-mozconfig.js +2 -1
  223. package/dist/src/core/mach-python.d.ts +2 -0
  224. package/dist/src/core/mach-python.js +12 -7
  225. package/dist/src/core/mach-resource-shim.d.ts +13 -1
  226. package/dist/src/core/mach-resource-shim.js +112 -9
  227. package/dist/src/core/mach.d.ts +46 -53
  228. package/dist/src/core/mach.js +31 -83
  229. package/dist/src/core/marionette-port.d.ts +0 -17
  230. package/dist/src/core/marionette-port.js +12 -58
  231. package/dist/src/core/marionette-preflight.js +9 -27
  232. package/dist/src/core/mochitest-server-port.js +3 -3
  233. package/dist/src/core/moz-manifest-rules.js +37 -33
  234. package/dist/src/core/patch-apply-fuzz.js +4 -4
  235. package/dist/src/core/patch-apply.d.ts +4 -5
  236. package/dist/src/core/patch-apply.js +55 -32
  237. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  238. package/dist/src/core/patch-export-coverage.js +1 -30
  239. package/dist/src/core/patch-export-update.d.ts +18 -1
  240. package/dist/src/core/patch-export-update.js +4 -3
  241. package/dist/src/core/patch-export.d.ts +11 -21
  242. package/dist/src/core/patch-export.js +44 -66
  243. package/dist/src/core/patch-files.d.ts +0 -4
  244. package/dist/src/core/patch-files.js +0 -11
  245. package/dist/src/core/patch-lint-binary.d.ts +6 -12
  246. package/dist/src/core/patch-lint-cache.d.ts +17 -1
  247. package/dist/src/core/patch-lint-cache.js +3 -6
  248. package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
  249. package/dist/src/core/patch-lint-checkjs.js +43 -25
  250. package/dist/src/core/patch-lint-creators.d.ts +2 -6
  251. package/dist/src/core/patch-lint-cross.d.ts +16 -8
  252. package/dist/src/core/patch-lint-cross.js +49 -92
  253. package/dist/src/core/patch-lint-css.js +10 -6
  254. package/dist/src/core/patch-lint-file-size.d.ts +52 -0
  255. package/dist/src/core/patch-lint-file-size.js +53 -0
  256. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  257. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  258. package/dist/src/core/patch-lint-jsdoc.js +36 -36
  259. package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
  260. package/dist/src/core/patch-lint-prettier.d.ts +36 -0
  261. package/dist/src/core/patch-lint-prettier.js +126 -0
  262. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  263. package/dist/src/core/patch-lint-queue-types.js +12 -0
  264. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  265. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  266. package/dist/src/core/patch-lint-unmanaged-companion.d.ts +106 -0
  267. package/dist/src/core/patch-lint-unmanaged-companion.js +214 -0
  268. package/dist/src/core/patch-lint.d.ts +14 -9
  269. package/dist/src/core/patch-lint.js +27 -27
  270. package/dist/src/core/patch-lock.js +3 -3
  271. package/dist/src/core/patch-manifest-io.js +8 -7
  272. package/dist/src/core/patch-parse.d.ts +19 -8
  273. package/dist/src/core/patch-parse.js +25 -0
  274. package/dist/src/core/patch-policy.js +15 -27
  275. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  276. package/dist/src/core/patch-source-metadata.js +0 -4
  277. package/dist/src/core/patch-transform.d.ts +21 -8
  278. package/dist/src/core/patch-transform.js +38 -10
  279. package/dist/src/core/process-cpu-time.js +1 -1
  280. package/dist/src/core/rebase-session.d.ts +2 -8
  281. package/dist/src/core/rebase-session.js +5 -17
  282. package/dist/src/core/register-browser-content.js +3 -2
  283. package/dist/src/core/register-browser-test.js +2 -1
  284. package/dist/src/core/register-module.js +2 -1
  285. package/dist/src/core/register-scaffold.js +5 -4
  286. package/dist/src/core/register-shared-css.js +2 -1
  287. package/dist/src/core/run-log-redact.d.ts +38 -0
  288. package/dist/src/core/run-log-redact.js +69 -0
  289. package/dist/src/core/run-log.d.ts +13 -2
  290. package/dist/src/core/run-log.js +67 -18
  291. package/dist/src/core/signal-critical.js +2 -4
  292. package/dist/src/core/smoke-patterns.d.ts +0 -1
  293. package/dist/src/core/smoke-patterns.js +18 -11
  294. package/dist/src/core/state-file.d.ts +12 -1
  295. package/dist/src/core/state-file.js +14 -1
  296. package/dist/src/core/status-classify.js +2 -1
  297. package/dist/src/core/test-changed-prefs.js +26 -11
  298. package/dist/src/core/test-harness-crash.js +61 -48
  299. package/dist/src/core/test-harness-verdict-notes.d.ts +55 -0
  300. package/dist/src/core/test-harness-verdict-notes.js +48 -0
  301. package/dist/src/core/test-stale-check.d.ts +23 -8
  302. package/dist/src/core/test-stale-check.js +77 -4
  303. package/dist/src/core/test-stall-triage.js +37 -19
  304. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  305. package/dist/src/core/test-xpcshell-retry.js +14 -11
  306. package/dist/src/core/token-category.d.ts +33 -0
  307. package/dist/src/core/token-category.js +56 -48
  308. package/dist/src/core/token-dark-mode.js +2 -14
  309. package/dist/src/core/token-inventory.d.ts +56 -0
  310. package/dist/src/core/token-inventory.js +109 -0
  311. package/dist/src/core/token-manager.d.ts +1 -1
  312. package/dist/src/core/token-manager.js +1 -1
  313. package/dist/src/core/token-variant.js +21 -22
  314. package/dist/src/core/toolchain-preflight.js +1 -1
  315. package/dist/src/core/tree-cow.d.ts +0 -2
  316. package/dist/src/core/tree-cow.js +2 -2
  317. package/dist/src/core/tree-store.js +4 -5
  318. package/dist/src/core/typecheck.js +2 -2
  319. package/dist/src/core/wire-dom-fragment.js +6 -17
  320. package/dist/src/core/wire-utils.js +5 -17
  321. package/dist/src/core/xpcshell-appdir.js +2 -4
  322. package/dist/src/errors/base.d.ts +53 -1
  323. package/dist/src/errors/base.js +79 -11
  324. package/dist/src/errors/build.d.ts +28 -0
  325. package/dist/src/errors/build.js +51 -10
  326. package/dist/src/errors/config.d.ts +0 -14
  327. package/dist/src/errors/config.js +13 -34
  328. package/dist/src/errors/download.d.ts +13 -0
  329. package/dist/src/errors/download.js +53 -24
  330. package/dist/src/errors/furnace.js +8 -9
  331. package/dist/src/errors/git.d.ts +8 -8
  332. package/dist/src/errors/git.js +20 -30
  333. package/dist/src/errors/patch.js +8 -10
  334. package/dist/src/errors/rebase.js +7 -7
  335. package/dist/src/types/commands/options.d.ts +96 -123
  336. package/dist/src/types/commands/patches.d.ts +17 -6
  337. package/dist/src/types/config.d.ts +75 -2
  338. package/dist/src/types/index.d.ts +0 -1
  339. package/dist/src/utils/assert.d.ts +3 -15
  340. package/dist/src/utils/assert.js +1 -18
  341. package/dist/src/utils/build-info.d.ts +1 -1
  342. package/dist/src/utils/child-env.d.ts +2 -1
  343. package/dist/src/utils/elapsed.js +10 -1
  344. package/dist/src/utils/fs.d.ts +4 -2
  345. package/dist/src/utils/fs.js +11 -14
  346. package/dist/src/utils/hash.d.ts +12 -0
  347. package/dist/src/utils/hash.js +16 -0
  348. package/dist/src/utils/line-dispatch.d.ts +15 -0
  349. package/dist/src/utils/line-dispatch.js +48 -0
  350. package/dist/src/utils/options.d.ts +1 -7
  351. package/dist/src/utils/options.js +6 -7
  352. package/dist/src/utils/package-root.js +3 -12
  353. package/dist/src/utils/platform.d.ts +0 -4
  354. package/dist/src/utils/process-group.d.ts +2 -1
  355. package/dist/src/utils/process-group.js +20 -19
  356. package/dist/src/utils/process.d.ts +40 -26
  357. package/dist/src/utils/process.js +115 -166
  358. package/dist/src/utils/ps-duration.js +3 -3
  359. package/dist/src/utils/sleep.d.ts +26 -0
  360. package/dist/src/utils/sleep.js +31 -0
  361. package/dist/src/utils/validation.d.ts +2 -38
  362. package/dist/src/utils/validation.js +7 -16
  363. package/docs/configuration.md +136 -1
  364. package/docs/exit-codes.md +34 -20
  365. package/docs/lifecycle-invariants.md +18 -12
  366. package/docs/machine-output.md +31 -8
  367. package/docs/patch-workflow.md +2 -1
  368. package/docs/run-logs.md +44 -4
  369. package/docs/testing.md +70 -11
  370. package/package.json +8 -7
  371. package/dist/src/commands/rebase.d.ts +0 -4
  372. package/dist/src/commands/rebase.js +0 -5
  373. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  374. package/dist/src/core/patch-artifact-normalize.js +0 -24
  375. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  376. package/dist/src/core/patch-lint-reexports.js +0 -8
  377. package/dist/src/core/wire-targets.d.ts +0 -7
  378. package/dist/src/core/wire-targets.js +0 -8
@@ -1,10 +1,10 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
- import { createHash } from 'node:crypto';
3
2
  import { readdir } from 'node:fs/promises';
4
3
  import { join, relative } from 'node:path';
5
4
  import { FurnaceError } from '../errors/furnace.js';
6
5
  import { toError } from '../utils/errors.js';
7
6
  import { copyFile, ensureDir, pathExists, readText, removeFile } from '../utils/fs.js';
7
+ import { sha256Hex } from '../utils/hash.js';
8
8
  import { verbose } from '../utils/logger.js';
9
9
  import { normalizePathSlashes } from '../utils/paths.js';
10
10
  import { buildCustomDryRunActions } from './furnace-apply-dry-run.js';
@@ -12,26 +12,12 @@ import { applyCustomFtlFile, applySharedFtlPrune, removeCustomFtlJarMnEntry, } f
12
12
  import { normalizeForChecksum } from './furnace-checksum-utils.js';
13
13
  import { CUSTOM_ELEMENTS_JS, JAR_MN } from './furnace-constants.js';
14
14
  import { deployFileWithFragments, SHARED_FRAGMENTS_DIR } from './furnace-css-fragments.js';
15
+ import { isRegularFile } from './furnace-dir-entry.js';
15
16
  import { addCustomElementRegistration, addJarMnEntries } from './furnace-registration.js';
16
17
  import { recordCreatedDir, snapshotFile } from './furnace-rollback.js';
17
18
  import { checkRegistrationConsistency } from './furnace-validate-registration.js';
18
19
  import { isGitRepository } from './git.js';
19
20
  import { fileExistsInHead, restoreTrackedPath } from './git-file-ops.js';
20
- /**
21
- * True for a plain-file directory entry — symlinks and directories are
22
- * never copy candidates. Exported for the patch-owned overwrite probe,
23
- * which walks the same override copy-candidate set as apply.
24
- */
25
- export function isRegularFile(entry) {
26
- if (!entry.isFile())
27
- return false;
28
- if (typeof entry.isSymbolicLink === 'function' && entry.isSymbolicLink())
29
- return false;
30
- return true;
31
- }
32
- function isChecksummedComponentFile(name) {
33
- return name.endsWith('.mjs') || name.endsWith('.css') || name.endsWith('.ftl');
34
- }
35
21
  /**
36
22
  * Filter deciding which files in an override workspace directory are candidates
37
23
  * for copying into the engine. Exported so `furnace remove` can invert apply
@@ -102,10 +88,13 @@ export async function computeComponentChecksums(componentDir) {
102
88
  continue;
103
89
  if (entry.name === 'override.json')
104
90
  continue;
105
- if (!isChecksummedComponentFile(entry.name))
91
+ // The three extensions a component can deploy; anything else in the
92
+ // workspace directory is not part of the component's checksum identity.
93
+ const { name: entryName } = entry;
94
+ if (!entryName.endsWith('.mjs') && !entryName.endsWith('.css') && !entryName.endsWith('.ftl'))
106
95
  continue;
107
96
  const content = await readText(join(componentDir, entry.name));
108
- const hash = createHash('sha256').update(normalizeForChecksum(content)).digest('hex');
97
+ const hash = sha256Hex(normalizeForChecksum(content));
109
98
  checksums[entry.name] = hash;
110
99
  }
111
100
  return checksums;
@@ -233,7 +222,7 @@ export async function hasOverrideEngineDrift(ctx, config, cachedEngineChecksums)
233
222
  const engineContent = normalizeForChecksum(await readText(enginePath));
234
223
  // Fast path: compare engine content hash against cached value from last apply
235
224
  if (cachedEngineChecksums) {
236
- const engineHash = createHash('sha256').update(engineContent).digest('hex');
225
+ const engineHash = sha256Hex(engineContent);
237
226
  const cachedHash = cachedEngineChecksums[entry.name];
238
227
  if (cachedHash && engineHash !== cachedHash) {
239
228
  return true;
@@ -21,9 +21,11 @@ import { join, relative } from 'node:path';
21
21
  import { toError } from '../utils/errors.js';
22
22
  import { pathExists, readText } from '../utils/fs.js';
23
23
  import { verbose } from '../utils/logger.js';
24
+ import { normalizePathSlashes } from '../utils/paths.js';
24
25
  import { getProjectPaths } from './config.js';
25
- import { getOverrideEngineTargetPath, isOverrideCopyCandidate, isRegularFile, } from './furnace-apply-helpers.js';
26
+ import { getOverrideEngineTargetPath, isOverrideCopyCandidate } from './furnace-apply-helpers.js';
26
27
  import { FTL_DIR } from './furnace-constants.js';
28
+ import { isRegularFile } from './furnace-dir-entry.js';
27
29
  import { checkRegistrationConsistency } from './furnace-validate-registration.js';
28
30
  import { loadPatchesManifest } from './patch-manifest.js';
29
31
  import { buildPatchClaims } from './status-classify.js';
@@ -86,7 +88,7 @@ async function findOverrideOverwrites(args) {
86
88
  // A missing target is a fresh deploy, not a lost engine edit.
87
89
  if (!(await pathExists(enginePath)))
88
90
  continue;
89
- const engineRel = relative(engineDir, enginePath).replace(/\\/g, '/');
91
+ const engineRel = normalizePathSlashes(relative(engineDir, enginePath));
90
92
  const owners = patchClaims.get(engineRel);
91
93
  if (owners === undefined || owners.length === 0)
92
94
  continue;
@@ -1,7 +1,7 @@
1
1
  import type { ApplyResult, DryRunAction } from '../types/furnace.js';
2
2
  import { type FurnaceOperationContext } from './furnace-operation.js';
3
3
  import { type RollbackJournal } from './furnace-rollback.js';
4
- export { computeComponentChecksums, hasComponentChanged, prefixChecksums, } from './furnace-apply-helpers.js';
4
+ export { computeComponentChecksums, prefixChecksums } from './furnace-apply-helpers.js';
5
5
  /**
6
6
  * Result of {@link applyAllComponents}.
7
7
  *
@@ -19,7 +19,18 @@ import { addJarMnEntries, removeCustomElementRegistration, removeJarMnEntries, }
19
19
  import { createRollbackJournal, restoreRollbackJournalOrThrow, snapshotFile, } from './furnace-rollback.js';
20
20
  import { blockingStepErrors, hasBlockingStepErrors } from './furnace-step-errors.js';
21
21
  import { runPostApplyConsistencyChecks } from './furnace-validate-registration.js';
22
- export { computeComponentChecksums, hasComponentChanged, prefixChecksums, } from './furnace-apply-helpers.js';
22
+ export { computeComponentChecksums, prefixChecksums } from './furnace-apply-helpers.js';
23
+ /**
24
+ * Records one component's checksums into the run-wide accumulator, under the
25
+ * flattened `type/name/file` keys the state file stores. A Map keeps the
26
+ * accumulation explicit — the previous `Object.assign` merges read as if they
27
+ * replaced the accumulator rather than adding to it.
28
+ */
29
+ function recordComponentChecksums(accumulator, checksums, type, name) {
30
+ for (const [key, value] of Object.entries(prefixChecksums(checksums, type, name))) {
31
+ accumulator.set(key, value);
32
+ }
33
+ }
23
34
  function addMissingComponentError(result, name, directoryPath) {
24
35
  result.errors.push({
25
36
  name,
@@ -54,7 +65,7 @@ async function applyOverrideBatch(ctx) {
54
65
  const drifted = await hasOverrideEngineDrift({ engineDir, componentDir, ftlDir }, overrideConfig, cachedEngine);
55
66
  if (!drifted) {
56
67
  result.skipped.push({ name, reason: 'No changes since last apply' });
57
- Object.assign(newChecksums, prefixChecksums(previous, 'override', name));
68
+ recordComponentChecksums(newChecksums, previous, 'override', name);
58
69
  continue;
59
70
  }
60
71
  }
@@ -95,7 +106,7 @@ async function applyOverrideBatch(ctx) {
95
106
  filesAffectedTotal.push(...filesAffected);
96
107
  result.applied.push({ name, type: 'override', filesAffected: filesAffectedTotal });
97
108
  if (!dryRun) {
98
- Object.assign(newChecksums, prefixChecksums(currentChecksums, 'override', name));
109
+ recordComponentChecksums(newChecksums, currentChecksums, 'override', name);
99
110
  }
100
111
  }
101
112
  catch (error) {
@@ -114,7 +125,8 @@ async function applyOverrideBatch(ctx) {
114
125
  * the customElements.js registration. Snapshots are taken under the same
115
126
  * journal so the undo path is symmetric with apply.
116
127
  */
117
- async function reconcileCustomRegistrationAfterUndeploy(engineDir, name, config, deletedFiles, currentChecksums, rollbackJournal, filesAffected) {
128
+ async function reconcileCustomRegistrationAfterUndeploy(args) {
129
+ const { engineDir, name, config, deletedFiles, currentChecksums, rollbackJournal, filesAffected, } = args;
118
130
  if (!config.register || deletedFiles.length === 0)
119
131
  return;
120
132
  const deletedRegistrationFiles = deletedFiles.filter((f) => f.endsWith('.mjs') || f.endsWith('.css'));
@@ -212,7 +224,7 @@ async function applyCustomBatch(ctx, root, markerComment) {
212
224
  const drifted = await hasCustomEngineDrift({ root, name, componentDir, ftlDir }, customConfig);
213
225
  if (!drifted) {
214
226
  result.skipped.push({ name, reason: 'No changes since last apply' });
215
- Object.assign(newChecksums, prefixChecksums(previous, 'custom', name));
227
+ recordComponentChecksums(newChecksums, previous, 'custom', name);
216
228
  continue;
217
229
  }
218
230
  }
@@ -250,7 +262,15 @@ async function applyCustomBatch(ctx, root, markerComment) {
250
262
  allActions.push(...actions);
251
263
  }
252
264
  if (!dryRun && deletedFiles.length > 0 && blockingStepErrors(stepErrors).length === 0) {
253
- await reconcileCustomRegistrationAfterUndeploy(engineDir, name, customConfig, deletedFiles, currentChecksums, rollbackJournal, filesAffectedTotal);
265
+ await reconcileCustomRegistrationAfterUndeploy({
266
+ engineDir,
267
+ name,
268
+ config: customConfig,
269
+ deletedFiles,
270
+ currentChecksums,
271
+ rollbackJournal,
272
+ filesAffected: filesAffectedTotal,
273
+ });
254
274
  }
255
275
  filesAffectedTotal.push(...filesAffected);
256
276
  result.applied.push({
@@ -264,7 +284,7 @@ async function applyCustomBatch(ctx, root, markerComment) {
264
284
  // the next run. Advisory errors (FTL degradation) still persist —
265
285
  // re-applying cannot fix a fork that has no locale jar.mn.
266
286
  if (!dryRun && blockingStepErrors(stepErrors).length === 0) {
267
- Object.assign(newChecksums, prefixChecksums(currentChecksums, 'custom', name));
287
+ recordComponentChecksums(newChecksums, currentChecksums, 'custom', name);
268
288
  }
269
289
  }
270
290
  catch (error) {
@@ -330,7 +350,7 @@ export async function applyAllComponents(root, dryRun = false, options) {
330
350
  errors: [],
331
351
  };
332
352
  const allActions = [];
333
- const newChecksums = {};
353
+ const newChecksums = new Map();
334
354
  const componentName = options?.componentName;
335
355
  // When a single component is requested, validate it exists before running
336
356
  // the batch functions (which would silently skip an unknown name).
@@ -406,8 +426,8 @@ export async function applyAllComponents(root, dryRun = false, options) {
406
426
  // instead of byte-comparing against workspace sources.
407
427
  await updateFurnaceState(root, {
408
428
  lastApply: new Date().toISOString(),
409
- appliedChecksums: newChecksums,
410
- engineChecksums: { ...newChecksums },
429
+ appliedChecksums: Object.fromEntries(newChecksums),
430
+ engineChecksums: Object.fromEntries(newChecksums),
411
431
  });
412
432
  }
413
433
  else if (rollbackJournal) {
@@ -6,7 +6,7 @@ export declare const SHARED_FRAGMENTS_DIR = "shared";
6
6
  /**
7
7
  * Paths for furnace-related files and directories.
8
8
  */
9
- interface FurnacePaths {
9
+ export interface FurnacePaths {
10
10
  /** Path to furnace.json */
11
11
  furnaceConfig: string;
12
12
  /** Path to components directory */
@@ -53,25 +53,18 @@ export async function furnaceConfigExists(root) {
53
53
  const paths = getFurnacePaths(root);
54
54
  return pathExists(paths.furnaceConfig);
55
55
  }
56
+ /** True when a recovered record carries at least one entry. */
57
+ function hasEntries(record) {
58
+ return Object.keys(record).length > 0;
59
+ }
56
60
  /** The current (and only) config schema version. */
57
61
  const CURRENT_CONFIG_VERSION = 1;
58
62
  /**
59
- * Migrates a furnace config from an older schema version to the current one.
60
- * Returns the data unchanged if it is already at the current version.
61
- *
62
- * When a future version 2 is introduced, add a `case 1:` that transforms
63
- * v1 data into v2 shape and falls through to validation. The pattern is:
64
- *
65
- * ```
66
- * case 1:
67
- * data = migrateV1ToV2(data);
68
- * // fallthrough
69
- * case 2:
70
- * break;
71
- * ```
63
+ * Rejects a config whose `version` field is not the single schema version that
64
+ * exists. A non-integer or sub-1 value is a malformed document; a value above
65
+ * the current version was written by a newer FireForge and cannot be read here.
72
66
  */
73
- function migrateFurnaceConfig(data) {
74
- const version = data['version'];
67
+ function validateConfigVersion(version) {
75
68
  if (typeof version !== 'number' || !Number.isInteger(version) || version < 1) {
76
69
  throw new FurnaceError(`Furnace config: "version" must be a positive integer (got ${JSON.stringify(version)}). ` +
77
70
  `Current schema version is ${CURRENT_CONFIG_VERSION}.`);
@@ -80,9 +73,6 @@ function migrateFurnaceConfig(data) {
80
73
  throw new FurnaceError(`Furnace config: version ${version} is newer than what this version of FireForge supports (${CURRENT_CONFIG_VERSION}). ` +
81
74
  'Upgrade FireForge to read this config.');
82
75
  }
83
- // Today only version 1 exists, so no migration is needed. When future
84
- // versions are added, migration steps will be chained here.
85
- return data;
86
76
  }
87
77
  /**
88
78
  * Validates a raw config object and returns a typed FurnaceConfig.
@@ -97,31 +87,28 @@ export function validateFurnaceConfig(data) {
97
87
  // Furnace config documents arrive from JSON.parse (loadFurnaceConfig /
98
88
  // writeFurnaceConfig round-trips), which can only produce JSON values,
99
89
  // so the object check above is the only invariant left to establish.
100
- // Run migration before validation so older configs are transparently upgraded.
101
- const migrated = migrateFurnaceConfig(data);
102
- if (migrated['version'] !== CURRENT_CONFIG_VERSION) {
103
- throw new FurnaceError(`Furnace config: "version" must be ${CURRENT_CONFIG_VERSION} after migration`);
104
- }
105
- if (!isString(migrated['componentPrefix'])) {
90
+ const raw = data;
91
+ validateConfigVersion(raw['version']);
92
+ if (!isString(raw['componentPrefix'])) {
106
93
  throw new FurnaceError('Furnace config: "componentPrefix" must be a string');
107
94
  }
108
95
  // Validate optional tokenPrefix
109
- if (migrated['tokenPrefix'] !== undefined && !isString(migrated['tokenPrefix'])) {
96
+ if (raw['tokenPrefix'] !== undefined && !isString(raw['tokenPrefix'])) {
110
97
  throw new FurnaceError('Furnace config: "tokenPrefix" must be a string if provided');
111
98
  }
112
99
  // Validate optional tokenAllowlist
113
- if (migrated['tokenAllowlist'] !== undefined) {
114
- parseStringArray(migrated['tokenAllowlist'], 'tokenAllowlist');
100
+ if (raw['tokenAllowlist'] !== undefined) {
101
+ parseStringArray(raw['tokenAllowlist'], 'tokenAllowlist');
115
102
  }
116
103
  // Validate optional runtimeVariables — CSS runtime state channels
117
104
  // (e.g. `--cam-x`) that are exempt from `token-prefix-violation`.
118
- validateRuntimeVariables(migrated['runtimeVariables']);
105
+ validateRuntimeVariables(raw['runtimeVariables']);
119
106
  // Validate optional tokenHostDocuments — list of chrome XHTMLs that the
120
107
  // `missing-token-link` validator scans for the tokens CSS link.
121
- validateTokenHostDocuments(migrated['tokenHostDocuments']);
122
- const stock = parseStockList(migrated['stock']);
123
- const overrides = parseNamedComponentMap(migrated['overrides'], 'override', 'overrides', parseOverrideConfig);
124
- const custom = parseNamedComponentMap(migrated['custom'], 'custom', 'custom', parseCustomConfig);
108
+ validateTokenHostDocuments(raw['tokenHostDocuments']);
109
+ const stock = parseStockList(raw['stock']);
110
+ const overrides = parseNamedComponentMap(raw['overrides'], 'override', 'overrides', parseOverrideConfig);
111
+ const custom = parseNamedComponentMap(raw['custom'], 'custom', 'custom', parseCustomConfig);
125
112
  // Detect circular composes references among custom components.
126
113
  detectComposesCycles(custom);
127
114
  // Validate that every composes reference points to a known component.
@@ -146,12 +133,12 @@ export function validateFurnaceConfig(data) {
146
133
  }
147
134
  const config = {
148
135
  version: CURRENT_CONFIG_VERSION,
149
- componentPrefix: migrated['componentPrefix'],
136
+ componentPrefix: raw['componentPrefix'],
150
137
  stock,
151
138
  overrides,
152
139
  custom,
153
140
  };
154
- applyOptionalFurnaceFields(migrated, config);
141
+ applyOptionalFurnaceFields(raw, config);
155
142
  return config;
156
143
  }
157
144
  /**
@@ -228,7 +215,7 @@ function sanitizeFurnaceState(data) {
228
215
  }
229
216
  appliedChecksums[filePath] = checksum;
230
217
  }
231
- if (Object.keys(appliedChecksums).length > 0 || !hasInvalidChecksum) {
218
+ if (hasEntries(appliedChecksums) || !hasInvalidChecksum) {
232
219
  state.appliedChecksums = appliedChecksums;
233
220
  recoveredFields.push('appliedChecksums');
234
221
  }
@@ -245,7 +232,7 @@ function sanitizeFurnaceState(data) {
245
232
  engineChecksums[filePath] = checksum;
246
233
  }
247
234
  }
248
- if (Object.keys(engineChecksums).length > 0) {
235
+ if (hasEntries(engineChecksums)) {
249
236
  state.engineChecksums = engineChecksums;
250
237
  recoveredFields.push('engineChecksums');
251
238
  }
@@ -1,6 +1,7 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /** Path to customElements.js within the engine source tree */
3
3
  export const CUSTOM_ELEMENTS_JS = 'toolkit/content/customElements.js';
4
+ import { normalizePathSlashes } from '../utils/paths.js';
4
5
  /** Path to jar.mn within the engine source tree (toolkit global) */
5
6
  export const JAR_MN = 'toolkit/content/jar.mn';
6
7
  /**
@@ -78,7 +79,7 @@ export function resolveFtlDir(configuredPath) {
78
79
  * degrade gracefully rather than inventing a path.
79
80
  */
80
81
  export function resolveFtlChromeSubPath(ftlBasePath) {
81
- const path = (ftlBasePath ?? FTL_DIR).replace(/\\/g, '/');
82
+ const path = normalizePathSlashes(ftlBasePath ?? FTL_DIR);
82
83
  const match = /^(.*?)\/locales\/[^/]+\/(.+?)\/?$/.exec(path);
83
84
  if (!match?.[2])
84
85
  return undefined;
@@ -93,7 +94,7 @@ export function resolveFtlChromeSubPath(ftlBasePath) {
93
94
  * callers must treat that as "cannot locate" and degrade gracefully.
94
95
  */
95
96
  export function resolveFtlLocaleJarMnPath(ftlBasePath) {
96
- const path = (ftlBasePath ?? FTL_DIR).replace(/\\/g, '/');
97
+ const path = normalizePathSlashes(ftlBasePath ?? FTL_DIR);
97
98
  const match = /^(.*?)\/locales\/[^/]+\//.exec(path);
98
99
  if (!match?.[1])
99
100
  return undefined;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The minimal `readdir(..., { withFileTypes: true })` entry shape the furnace
3
+ * apply path needs, plus the single predicate that decides whether an entry is
4
+ * a copy candidate. Kept in its own leaf module so both `furnace-apply-helpers`
5
+ * and its dry-run mirror can share one definition without importing each other.
6
+ */
7
+ /** Structural subset of `Dirent` used by the furnace apply/dry-run walkers. */
8
+ export interface DirectoryEntry {
9
+ isFile(): boolean;
10
+ isSymbolicLink?(): boolean;
11
+ name: string;
12
+ }
13
+ /**
14
+ * True for a plain-file directory entry — symlinks and directories are
15
+ * never copy candidates.
16
+ *
17
+ * @param entry - Directory entry to classify
18
+ * @returns True when the entry is a regular file
19
+ */
20
+ export declare function isRegularFile(entry: DirectoryEntry): boolean;
@@ -0,0 +1,21 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * The minimal `readdir(..., { withFileTypes: true })` entry shape the furnace
4
+ * apply path needs, plus the single predicate that decides whether an entry is
5
+ * a copy candidate. Kept in its own leaf module so both `furnace-apply-helpers`
6
+ * and its dry-run mirror can share one definition without importing each other.
7
+ */
8
+ /**
9
+ * True for a plain-file directory entry — symlinks and directories are
10
+ * never copy candidates.
11
+ *
12
+ * @param entry - Directory entry to classify
13
+ * @returns True when the entry is a regular file
14
+ */
15
+ export function isRegularFile(entry) {
16
+ if (!entry.isFile())
17
+ return false;
18
+ if (typeof entry.isSymbolicLink === 'function' && entry.isSymbolicLink())
19
+ return false;
20
+ return true;
21
+ }
@@ -188,5 +188,7 @@ export declare function recordFurnaceRollbackFailure(root: string, operation: Fu
188
188
  * reuse the module across tests, so the test suite must call this between
189
189
  * cases that exercise the signal pathway. Not exported from the package
190
190
  * entry point.
191
+ *
192
+ * @internal Exported only so tests can reach it; not part of the public surface.
191
193
  */
192
194
  export declare function __resetFurnaceOperationStateForTests(): void;
@@ -381,7 +381,7 @@ export async function runFurnaceMutation(root, kind, body, options = {}) {
381
381
  const lockStatus = await readLockStatus(lockPath);
382
382
  assert(lockStatus.held, 'furnace lock held before the mutation body runs');
383
383
  assert(lockStatus.holder === undefined || lockStatus.holder.pid === process.pid, () => `furnace lock is owned by this process ` +
384
- `(held by PID ${String(lockStatus.holder?.pid)}, we are ${String(process.pid)})`);
384
+ `(held by PID ${lockStatus.holder?.pid}, we are ${process.pid})`);
385
385
  activeOperations.set(token, operation);
386
386
  try {
387
387
  return await body({
@@ -432,6 +432,8 @@ export async function recordFurnaceRollbackFailure(root, operation, reason) {
432
432
  * reuse the module across tests, so the test suite must call this between
433
433
  * cases that exercise the signal pathway. Not exported from the package
434
434
  * entry point.
435
+ *
436
+ * @internal Exported only so tests can reach it; not part of the public surface.
435
437
  */
436
438
  export function __resetFurnaceOperationStateForTests() {
437
439
  activeOperations.clear();
@@ -3,14 +3,26 @@ export interface RefreshFileResult {
3
3
  status: 'merged' | 'conflict' | 'unchanged' | 'new-file';
4
4
  conflictMarkers?: number;
5
5
  }
6
+ export interface RefreshOverrideFileOptions {
7
+ /** Path to the engine git repository. */
8
+ engineDir: string;
9
+ /** Path to the current override file in the workspace. */
10
+ overridePath: string;
11
+ /** Engine-relative path for git show. */
12
+ engineRelPath: string;
13
+ /** The git ref at which the override was originally created. */
14
+ baseCommit: string;
15
+ /** Display name for the file. */
16
+ fileName: string;
17
+ /** When true, compute the merge but write nothing. */
18
+ dryRun?: boolean | undefined;
19
+ /** Conflict resolution preference handed to `git merge-file`. */
20
+ strategy?: 'ours' | 'theirs' | undefined;
21
+ }
6
22
  /**
7
23
  * Refreshes a single override file against the current engine HEAD.
8
24
  *
9
- * @param engineDir - Path to the engine git repository
10
- * @param overridePath - Path to the current override file in the workspace
11
- * @param engineRelPath - Engine-relative path for git show
12
- * @param baseCommit - The git ref at which the override was originally created
13
- * @param fileName - Display name for the file
25
+ * @param options - See {@link RefreshOverrideFileOptions}
14
26
  * @returns Merge result with the updated content written to the override file
15
27
  */
16
- export declare function refreshOverrideFile(engineDir: string, overridePath: string, engineRelPath: string, baseCommit: string, fileName: string, dryRun?: boolean, strategy?: 'ours' | 'theirs'): Promise<RefreshFileResult>;
28
+ export declare function refreshOverrideFile(options: RefreshOverrideFileOptions): Promise<RefreshFileResult>;
@@ -81,14 +81,11 @@ async function threeWayMergeFile(base, ours, theirs, label, strategy) {
81
81
  /**
82
82
  * Refreshes a single override file against the current engine HEAD.
83
83
  *
84
- * @param engineDir - Path to the engine git repository
85
- * @param overridePath - Path to the current override file in the workspace
86
- * @param engineRelPath - Engine-relative path for git show
87
- * @param baseCommit - The git ref at which the override was originally created
88
- * @param fileName - Display name for the file
84
+ * @param options - See {@link RefreshOverrideFileOptions}
89
85
  * @returns Merge result with the updated content written to the override file
90
86
  */
91
- export async function refreshOverrideFile(engineDir, overridePath, engineRelPath, baseCommit, fileName, dryRun, strategy) {
87
+ export async function refreshOverrideFile(options) {
88
+ const { engineDir, overridePath, engineRelPath, baseCommit, fileName, dryRun, strategy } = options;
92
89
  // Read the three versions
93
90
  const oursContent = await readText(overridePath);
94
91
  const baseContent = await getFileContentAtRef(engineDir, engineRelPath, baseCommit);
@@ -28,7 +28,6 @@ export interface RegistrationWriteOptions {
28
28
  */
29
29
  markerComment?: string;
30
30
  }
31
- export { removeCustomElementRegistration } from './furnace-registration-remove.js';
32
31
  /**
33
32
  * Adds a custom element registration entry to customElements.js.
34
33
  *
@@ -46,9 +46,6 @@ function formatMarkerSuffix(markerComment) {
46
46
  return '';
47
47
  return ` // ${markerComment}:`;
48
48
  }
49
- // Re-export from split modules so existing import sites continue working
50
- export { removeCustomElementRegistration } from './furnace-registration-remove.js';
51
- // Re-export constants so existing import sites continue working
52
49
  // ---------------------------------------------------------------------------
53
50
  // Helpers
54
51
  // ---------------------------------------------------------------------------
@@ -1,4 +1,5 @@
1
- export { addCustomElementRegistration, removeCustomElementRegistration, validateCustomElementRegistration, } from './furnace-registration-ast.js';
1
+ export { addCustomElementRegistration, validateCustomElementRegistration, } from './furnace-registration-ast.js';
2
+ export { removeCustomElementRegistration } from './furnace-registration-remove.js';
2
3
  /**
3
4
  * Adds jar.mn entries that map chrome:// URIs to on-disk paths for a
4
5
  * component's files.
@@ -2,22 +2,31 @@
2
2
  import { join } from 'node:path';
3
3
  import { FurnaceError } from '../errors/furnace.js';
4
4
  import { pathExists, readText, writeText } from '../utils/fs.js';
5
+ /**
6
+ * Returns the leading whitespace of the first line matching `pattern`, whose
7
+ * first capture group must be that indentation. `undefined` when no line
8
+ * matches, so callers can chain progressively looser patterns before falling
9
+ * back to a convention default.
10
+ */
11
+ function detectIndent(lines, pattern) {
12
+ for (const line of lines) {
13
+ const match = pattern.exec(line);
14
+ if (match?.[1])
15
+ return match[1];
16
+ }
17
+ return undefined;
18
+ }
5
19
  /**
6
20
  * Detects the indentation used by existing `content/global/elements/` lines
7
21
  * in jar.mn. Falls back to 3 spaces (the historical Firefox convention) when
8
22
  * no reference line is found.
9
23
  */
10
24
  function detectJarMnIndent(lines) {
11
- for (const line of lines) {
12
- const match = /^(\s+)content\/global\/elements\//.exec(line);
13
- if (match?.[1]) {
14
- return match[1];
15
- }
16
- }
17
- return ' ';
25
+ return detectIndent(lines, /^(\s+)content\/global\/elements\//) ?? ' ';
18
26
  }
19
27
  // Re-export everything from the AST module so existing imports keep working
20
- export { addCustomElementRegistration, removeCustomElementRegistration, validateCustomElementRegistration, } from './furnace-registration-ast.js';
28
+ export { addCustomElementRegistration, validateCustomElementRegistration, } from './furnace-registration-ast.js';
29
+ export { removeCustomElementRegistration } from './furnace-registration-remove.js';
21
30
  import { escapeRegex } from '../utils/regex.js';
22
31
  import { JAR_MN } from './furnace-constants.js';
23
32
  /**
@@ -72,10 +81,7 @@ export async function addJarMnEntries(engineDir, tagName, files) {
72
81
  // Find insertion point among existing content/global/elements/ lines
73
82
  const elementLinePattern = /^\s+content\/global\/elements\/([^.]+)\./;
74
83
  let insertIndex = -1;
75
- for (let i = 0; i < lines.length; i++) {
76
- const line = lines[i];
77
- if (line === undefined)
78
- continue;
84
+ for (const [i, line] of lines.entries()) {
79
85
  const match = elementLinePattern.exec(line);
80
86
  if (match) {
81
87
  const existingTag = match[1] ?? '';
@@ -148,10 +154,7 @@ export async function addLocaleFtlJarMnEntry(engineDir, jarMnRelPath, tagName, c
148
154
  const newEntry = `${indent}locale/@AB_CD@/${chromeSubPath}/${ftlFile} (%${chromeSubPath}/${ftlFile})`;
149
155
  const sectionPattern = new RegExp(`^(\\s+)locale\\/(?:@AB_CD@|[a-zA-Z-]+)\\/${escapedChrome}\\/([^.\\s]+)\\.ftl`);
150
156
  let insertIndex = -1;
151
- for (let i = 0; i < lines.length; i++) {
152
- const line = lines[i];
153
- if (line === undefined)
154
- continue;
157
+ for (const [i, line] of lines.entries()) {
155
158
  const match = sectionPattern.exec(line);
156
159
  if (match) {
157
160
  const existingTag = match[2] ?? '';
@@ -174,19 +177,9 @@ export async function addLocaleFtlJarMnEntry(engineDir, jarMnRelPath, tagName, c
174
177
  /** Detects locale jar.mn indentation by sampling an existing matching entry. */
175
178
  function detectLocaleJarMnIndent(lines, chromeSubPath) {
176
179
  const escapedChrome = escapeRegex(chromeSubPath);
177
- const pattern = new RegExp(`^(\\s+)locale\\/(?:@AB_CD@|[a-zA-Z-]+)\\/${escapedChrome}\\/`);
178
- for (const line of lines) {
179
- const match = pattern.exec(line);
180
- if (match?.[1])
181
- return match[1];
182
- }
183
- // Fall back to detecting any existing `locale/...` indent before giving up.
184
- for (const line of lines) {
185
- const match = /^(\s+)locale\//.exec(line);
186
- if (match?.[1])
187
- return match[1];
188
- }
189
- return ' ';
180
+ const exact = new RegExp(`^(\\s+)locale\\/(?:@AB_CD@|[a-zA-Z-]+)\\/${escapedChrome}\\/`);
181
+ // Fall back to any existing `locale/...` indent before giving up.
182
+ return detectIndent(lines, exact) ?? detectIndent(lines, /^(\s+)locale\//) ?? ' ';
190
183
  }
191
184
  /**
192
185
  * Removes a locale jar.mn entry previously written by `addLocaleFtlJarMnEntry`.
@@ -1,9 +1,9 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
- import { createHash } from 'node:crypto';
3
2
  import { readdir } from 'node:fs/promises';
4
3
  import { join } from 'node:path';
5
4
  import { toError } from '../utils/errors.js';
6
5
  import { pathExists, readText } from '../utils/fs.js';
6
+ import { sha256Hex } from '../utils/hash.js';
7
7
  import { verbose, warn } from '../utils/logger.js';
8
8
  import { asEstree, getNodeSource, parseScript, walkAST } from './ast-utils.js';
9
9
  import { CUSTOM_ELEMENTS_JS, FTL_DIR, WIDGETS_DIR } from './furnace-constants.js';
@@ -48,7 +48,7 @@ export async function scanCustomElementsRegistrations(engineDir) {
48
48
  return registrations;
49
49
  }
50
50
  const content = await readText(filePath);
51
- const contentHash = createHash('sha256').update(content).digest('hex');
51
+ const contentHash = sha256Hex(content);
52
52
  // Return cached result if file hasn't changed since last parse.
53
53
  if (registrationCache && registrationCache.hash === contentHash) {
54
54
  return new Map(registrationCache.registrations);
@@ -121,10 +121,7 @@ export async function scanCustomElementsRegistrations(engineDir) {
121
121
  'Results may be incomplete — run "fireforge furnace validate" to verify registration consistency.');
122
122
  verbose(`Scanner regex fallback activated due to parse error: ${reason}`);
123
123
  const lines = content.split('\n');
124
- for (let i = 0; i < lines.length; i++) {
125
- const line = lines[i];
126
- if (line === undefined)
127
- continue;
124
+ for (const [i, line] of lines.entries()) {
128
125
  const callbackMatch = /setElementCreationCallback\(\s*"([^"]+)"/.exec(line);
129
126
  if (!callbackMatch?.[1])
130
127
  continue;
@@ -20,7 +20,7 @@
20
20
  * `furnace validate`.
21
21
  */
22
22
  import { type ApplyAllComponentsResult } from './furnace-apply.js';
23
- import { type getFurnacePaths } from './furnace-config.js';
23
+ import { type FurnacePaths } from './furnace-config.js';
24
24
  /**
25
25
  * Counts applied entries that carry at least one BLOCKING step error
26
26
  * (advisory step errors are warnings and never fail a run).
@@ -65,4 +65,4 @@ export declare function getPersistableAppliedEntry(commandLabel: string, name: s
65
65
  export declare function persistSingleComponentState(projectRoot: string, appliedEntry: {
66
66
  name: string;
67
67
  type: 'override' | 'custom';
68
- }, furnacePaths: ReturnType<typeof getFurnacePaths>): Promise<void>;
68
+ }, furnacePaths: FurnacePaths): Promise<void>;