@hominis/fireforge 0.35.0 → 0.37.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 (369) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +34 -9
  3. package/dist/bin/fireforge.js +21 -6
  4. package/dist/src/cli.js +22 -5
  5. package/dist/src/commands/bootstrap-checks.js +0 -1
  6. package/dist/src/commands/bootstrap.d.ts +2 -1
  7. package/dist/src/commands/bootstrap.js +16 -7
  8. package/dist/src/commands/build.js +5 -4
  9. package/dist/src/commands/config.js +30 -10
  10. package/dist/src/commands/discard.js +18 -3
  11. package/dist/src/commands/doctor/post-rebase-audit.js +0 -1
  12. package/dist/src/commands/doctor-check-core.js +0 -1
  13. package/dist/src/commands/doctor-external-toolchains.d.ts +3 -0
  14. package/dist/src/commands/doctor-external-toolchains.js +54 -0
  15. package/dist/src/commands/doctor-furnace-jar.js +0 -1
  16. package/dist/src/commands/doctor-furnace-manifest-sync.js +0 -1
  17. package/dist/src/commands/doctor-furnace.js +2 -2
  18. package/dist/src/commands/doctor-orphaned-harness.d.ts +52 -0
  19. package/dist/src/commands/doctor-orphaned-harness.js +132 -0
  20. package/dist/src/commands/doctor-working-tree.js +0 -1
  21. package/dist/src/commands/doctor.js +6 -1
  22. package/dist/src/commands/download.js +28 -13
  23. package/dist/src/commands/export-all.js +5 -3
  24. package/dist/src/commands/export-flow.js +12 -7
  25. package/dist/src/commands/export-placement-gate.js +0 -1
  26. package/dist/src/commands/export-placement-policy.js +0 -1
  27. package/dist/src/commands/export-shared.d.ts +5 -1
  28. package/dist/src/commands/export-shared.js +18 -6
  29. package/dist/src/commands/export.js +22 -10
  30. package/dist/src/commands/furnace/apply.js +22 -6
  31. package/dist/src/commands/furnace/chrome-doc-remove.js +0 -1
  32. package/dist/src/commands/furnace/chrome-doc-templates.js +0 -1
  33. package/dist/src/commands/furnace/chrome-doc-tests.js +0 -1
  34. package/dist/src/commands/furnace/chrome-doc.js +0 -1
  35. package/dist/src/commands/furnace/create-browser-test.js +0 -1
  36. package/dist/src/commands/furnace/create-dry-run.js +0 -1
  37. package/dist/src/commands/furnace/create-features.js +0 -1
  38. package/dist/src/commands/furnace/create-mochikit.js +0 -1
  39. package/dist/src/commands/furnace/create-readback.js +0 -1
  40. package/dist/src/commands/furnace/create-templates.js +0 -1
  41. package/dist/src/commands/furnace/create-validation.js +0 -1
  42. package/dist/src/commands/furnace/create-xpcshell.js +0 -1
  43. package/dist/src/commands/furnace/create.js +0 -1
  44. package/dist/src/commands/furnace/deploy.js +7 -83
  45. package/dist/src/commands/furnace/diff.js +0 -1
  46. package/dist/src/commands/furnace/index.js +59 -25
  47. package/dist/src/commands/furnace/init.js +0 -1
  48. package/dist/src/commands/furnace/list.js +0 -1
  49. package/dist/src/commands/furnace/override.js +0 -1
  50. package/dist/src/commands/furnace/preview.js +3 -3
  51. package/dist/src/commands/furnace/refresh.js +0 -1
  52. package/dist/src/commands/furnace/remove-state.d.ts +15 -0
  53. package/dist/src/commands/furnace/remove-state.js +47 -1
  54. package/dist/src/commands/furnace/remove.js +3 -9
  55. package/dist/src/commands/furnace/rename-browser-test.js +1 -5
  56. package/dist/src/commands/furnace/rename-helpers.d.ts +6 -0
  57. package/dist/src/commands/furnace/rename-helpers.js +30 -1
  58. package/dist/src/commands/furnace/rename-xpcshell.js +7 -6
  59. package/dist/src/commands/furnace/rename.js +15 -36
  60. package/dist/src/commands/furnace/scan.js +0 -1
  61. package/dist/src/commands/furnace/status.js +0 -1
  62. package/dist/src/commands/furnace/sync.js +76 -4
  63. package/dist/src/commands/furnace/validate.js +0 -1
  64. package/dist/src/commands/furnace/validation-output.js +0 -1
  65. package/dist/src/commands/import.js +35 -9
  66. package/dist/src/commands/lint-per-patch.js +19 -7
  67. package/dist/src/commands/lint.js +13 -3
  68. package/dist/src/commands/manifest.js +1 -2
  69. package/dist/src/commands/package.js +2 -1
  70. package/dist/src/commands/patch/compact.js +1 -1
  71. package/dist/src/commands/patch/delete.js +1 -1
  72. package/dist/src/commands/patch/index.js +1 -1
  73. package/dist/src/commands/patch/lint-ignore.js +21 -6
  74. package/dist/src/commands/patch/move-files.js +1 -1
  75. package/dist/src/commands/patch/patch-context.js +0 -1
  76. package/dist/src/commands/patch/rename.js +4 -4
  77. package/dist/src/commands/patch/reorder.js +3 -4
  78. package/dist/src/commands/patch/split-plan.js +2 -1
  79. package/dist/src/commands/patch/split.js +4 -4
  80. package/dist/src/commands/patch/staged-dependency.d.ts +1 -1
  81. package/dist/src/commands/patch/staged-dependency.js +114 -52
  82. package/dist/src/commands/patch/tier.js +18 -5
  83. package/dist/src/commands/re-export-bulk-scan.js +0 -1
  84. package/dist/src/commands/re-export-files.js +5 -1
  85. package/dist/src/commands/re-export-options.js +0 -1
  86. package/dist/src/commands/re-export-scan.js +0 -1
  87. package/dist/src/commands/re-export.js +14 -4
  88. package/dist/src/commands/rebase/abort.js +2 -9
  89. package/dist/src/commands/rebase/confirm.js +0 -1
  90. package/dist/src/commands/rebase/conflict-summary.js +0 -1
  91. package/dist/src/commands/rebase/continue.js +0 -1
  92. package/dist/src/commands/rebase/index.d.ts +1 -1
  93. package/dist/src/commands/rebase/index.js +10 -15
  94. package/dist/src/commands/rebase/patch-loop.js +38 -7
  95. package/dist/src/commands/rebase/summary.js +2 -3
  96. package/dist/src/commands/rebase.js +0 -1
  97. package/dist/src/commands/register.js +1 -1
  98. package/dist/src/commands/reset.js +16 -9
  99. package/dist/src/commands/resolve.js +2 -2
  100. package/dist/src/commands/run.js +4 -4
  101. package/dist/src/commands/setup-support.js +0 -1
  102. package/dist/src/commands/setup.js +1 -2
  103. package/dist/src/commands/source.js +1 -2
  104. package/dist/src/commands/status-output.js +1 -12
  105. package/dist/src/commands/status.js +147 -133
  106. package/dist/src/commands/test-appdir.js +0 -1
  107. package/dist/src/commands/test-diagnose.js +38 -3
  108. package/dist/src/commands/test-modes.d.ts +15 -0
  109. package/dist/src/commands/test-modes.js +61 -0
  110. package/dist/src/commands/test-register.d.ts +4 -0
  111. package/dist/src/commands/test-register.js +64 -0
  112. package/dist/src/commands/test-run.d.ts +23 -7
  113. package/dist/src/commands/test-run.js +15 -14
  114. package/dist/src/commands/test.d.ts +0 -4
  115. package/dist/src/commands/test.js +147 -96
  116. package/dist/src/commands/token-coverage.js +0 -1
  117. package/dist/src/commands/token.js +1 -2
  118. package/dist/src/commands/typecheck.js +1 -1
  119. package/dist/src/commands/verify.js +3 -9
  120. package/dist/src/commands/watch.js +5 -1
  121. package/dist/src/commands/wire.js +1 -1
  122. package/dist/src/core/ast-utils.js +0 -1
  123. package/dist/src/core/brand-validation.js +0 -1
  124. package/dist/src/core/branding.js +0 -1
  125. package/dist/src/core/browser-wire.js +0 -1
  126. package/dist/src/core/build-audit-platform.js +0 -1
  127. package/dist/src/core/build-audit-registration.js +0 -1
  128. package/dist/src/core/build-audit-resolve.js +0 -1
  129. package/dist/src/core/build-audit-transforms.js +0 -1
  130. package/dist/src/core/build-audit.js +0 -1
  131. package/dist/src/core/build-baseline-types.d.ts +24 -0
  132. package/dist/src/core/build-baseline-types.js +0 -1
  133. package/dist/src/core/build-baseline.d.ts +5 -2
  134. package/dist/src/core/build-baseline.js +5 -2
  135. package/dist/src/core/build-prepare.js +2 -2
  136. package/dist/src/core/config-mutate.js +0 -1
  137. package/dist/src/core/config-paths.d.ts +2 -2
  138. package/dist/src/core/config-paths.js +6 -1
  139. package/dist/src/core/config-state.js +0 -1
  140. package/dist/src/core/config-validate-patch-policy.js +0 -1
  141. package/dist/src/core/config-validate-test-toolchains.d.ts +8 -0
  142. package/dist/src/core/config-validate-test-toolchains.js +105 -0
  143. package/dist/src/core/config-validate.js +3 -1
  144. package/dist/src/core/config.js +0 -1
  145. package/dist/src/core/destructive.js +0 -1
  146. package/dist/src/core/diff-hunks.js +13 -7
  147. package/dist/src/core/engine-changes.js +0 -1
  148. package/dist/src/core/engine-session-lock.d.ts +12 -0
  149. package/dist/src/core/engine-session-lock.js +45 -0
  150. package/dist/src/core/file-lock.d.ts +8 -0
  151. package/dist/src/core/file-lock.js +157 -48
  152. package/dist/src/core/firefox-archive.d.ts +4 -0
  153. package/dist/src/core/firefox-archive.js +6 -1
  154. package/dist/src/core/firefox-cache.js +60 -4
  155. package/dist/src/core/firefox-download.js +0 -1
  156. package/dist/src/core/firefox-extract.js +75 -16
  157. package/dist/src/core/firefox-ignorefile.js +0 -1
  158. package/dist/src/core/firefox.d.ts +12 -0
  159. package/dist/src/core/firefox.js +64 -2
  160. package/dist/src/core/furnace-apply-dry-run.js +0 -1
  161. package/dist/src/core/furnace-apply-ftl.d.ts +7 -3
  162. package/dist/src/core/furnace-apply-ftl.js +10 -5
  163. package/dist/src/core/furnace-apply-helpers.js +0 -1
  164. package/dist/src/core/furnace-apply-output.js +2 -2
  165. package/dist/src/core/furnace-apply.d.ts +7 -1
  166. package/dist/src/core/furnace-apply.js +47 -13
  167. package/dist/src/core/furnace-checksum-utils.js +0 -1
  168. package/dist/src/core/furnace-config-array-utils.js +0 -1
  169. package/dist/src/core/furnace-config-custom.js +10 -1
  170. package/dist/src/core/furnace-config-order.js +0 -1
  171. package/dist/src/core/furnace-config-tokens.js +0 -1
  172. package/dist/src/core/furnace-config-validate.js +0 -1
  173. package/dist/src/core/furnace-config.d.ts +11 -0
  174. package/dist/src/core/furnace-config.js +37 -1
  175. package/dist/src/core/furnace-constants.js +0 -1
  176. package/dist/src/core/furnace-css-fragments.js +0 -1
  177. package/dist/src/core/furnace-graph-utils.js +0 -1
  178. package/dist/src/core/furnace-jsconfig.d.ts +2 -3
  179. package/dist/src/core/furnace-jsconfig.js +25 -18
  180. package/dist/src/core/furnace-marker.js +0 -1
  181. package/dist/src/core/furnace-operation.js +0 -1
  182. package/dist/src/core/furnace-refresh.js +0 -1
  183. package/dist/src/core/furnace-registration-ast.d.ts +14 -0
  184. package/dist/src/core/furnace-registration-ast.js +1 -2
  185. package/dist/src/core/furnace-registration-remove.js +0 -1
  186. package/dist/src/core/furnace-registration-validate.js +1 -5
  187. package/dist/src/core/furnace-registration.js +0 -1
  188. package/dist/src/core/furnace-rollback.js +0 -1
  189. package/dist/src/core/furnace-scanner.js +0 -1
  190. package/dist/src/core/furnace-stale-export.d.ts +59 -0
  191. package/dist/src/core/furnace-stale-export.js +123 -0
  192. package/dist/src/core/furnace-staleness.js +0 -1
  193. package/dist/src/core/furnace-state-persist.d.ts +72 -0
  194. package/dist/src/core/furnace-state-persist.js +118 -0
  195. package/dist/src/core/furnace-step-errors.d.ts +23 -0
  196. package/dist/src/core/furnace-step-errors.js +25 -0
  197. package/dist/src/core/furnace-stories.js +0 -1
  198. package/dist/src/core/furnace-validate-accessibility.js +0 -1
  199. package/dist/src/core/furnace-validate-checks.js +0 -1
  200. package/dist/src/core/furnace-validate-compatibility.js +9 -3
  201. package/dist/src/core/furnace-validate-helpers.js +0 -1
  202. package/dist/src/core/furnace-validate-registration.js +7 -7
  203. package/dist/src/core/furnace-validate-structure.d.ts +8 -0
  204. package/dist/src/core/furnace-validate-structure.js +14 -4
  205. package/dist/src/core/furnace-validate.js +0 -1
  206. package/dist/src/core/furnace-version-drift.d.ts +6 -3
  207. package/dist/src/core/furnace-version-drift.js +6 -4
  208. package/dist/src/core/git-base.js +0 -1
  209. package/dist/src/core/git-diff.js +0 -1
  210. package/dist/src/core/git-file-ops.js +0 -1
  211. package/dist/src/core/git-status.d.ts +13 -0
  212. package/dist/src/core/git-status.js +32 -2
  213. package/dist/src/core/git.d.ts +10 -1
  214. package/dist/src/core/git.js +63 -6
  215. package/dist/src/core/license-headers.js +0 -1
  216. package/dist/src/core/lint-cache.js +0 -1
  217. package/dist/src/core/lint-projection.js +0 -1
  218. package/dist/src/core/mach-build-artifacts.js +0 -1
  219. package/dist/src/core/mach-error-hints.js +0 -1
  220. package/dist/src/core/mach-known-noise-filter.d.ts +60 -0
  221. package/dist/src/core/mach-known-noise-filter.js +193 -0
  222. package/dist/src/core/mach-mozconfig.js +0 -1
  223. package/dist/src/core/mach-python.js +0 -1
  224. package/dist/src/core/mach-resource-shim.js +0 -1
  225. package/dist/src/core/mach.d.ts +8 -0
  226. package/dist/src/core/mach.js +36 -4
  227. package/dist/src/core/manifest-helpers.js +0 -1
  228. package/dist/src/core/manifest-register.js +0 -1
  229. package/dist/src/core/manifest-rules.js +0 -1
  230. package/dist/src/core/manifest-tokenizers.js +0 -1
  231. package/dist/src/core/marionette-port.d.ts +8 -0
  232. package/dist/src/core/marionette-port.js +34 -3
  233. package/dist/src/core/marionette-preflight.js +0 -1
  234. package/dist/src/core/markdown-table.js +0 -1
  235. package/dist/src/core/ownership-table.js +2 -9
  236. package/dist/src/core/parser-fallback.js +0 -1
  237. package/dist/src/core/patch-apply-fuzz.d.ts +27 -9
  238. package/dist/src/core/patch-apply-fuzz.js +71 -34
  239. package/dist/src/core/patch-apply.d.ts +30 -0
  240. package/dist/src/core/patch-apply.js +101 -18
  241. package/dist/src/core/patch-artifact-normalize.d.ts +18 -5
  242. package/dist/src/core/patch-artifact-normalize.js +19 -15
  243. package/dist/src/core/patch-export-coverage.js +0 -1
  244. package/dist/src/core/patch-export-metadata.js +0 -1
  245. package/dist/src/core/patch-export-update.js +2 -3
  246. package/dist/src/core/patch-export.d.ts +2 -6
  247. package/dist/src/core/patch-export.js +24 -48
  248. package/dist/src/core/patch-files.js +0 -1
  249. package/dist/src/core/patch-identifier-suggest.js +0 -1
  250. package/dist/src/core/patch-lint-checkjs.js +0 -1
  251. package/dist/src/core/patch-lint-chrome-jsdoc.js +0 -1
  252. package/dist/src/core/patch-lint-cross.js +80 -3
  253. package/dist/src/core/patch-lint-css.js +0 -1
  254. package/dist/src/core/patch-lint-diff-tag.js +0 -1
  255. package/dist/src/core/patch-lint-diff.d.ts +2 -1
  256. package/dist/src/core/patch-lint-diff.js +17 -32
  257. package/dist/src/core/patch-lint-imports.js +0 -1
  258. package/dist/src/core/patch-lint-jsdoc.js +0 -1
  259. package/dist/src/core/patch-lint-observer.js +0 -1
  260. package/dist/src/core/patch-lint-ownership.js +0 -1
  261. package/dist/src/core/patch-lint-reexports.js +0 -1
  262. package/dist/src/core/patch-lint.d.ts +37 -2
  263. package/dist/src/core/patch-lint.js +47 -34
  264. package/dist/src/core/patch-lock.js +0 -1
  265. package/dist/src/core/patch-manifest-consistency.js +9 -1
  266. package/dist/src/core/patch-manifest-io.d.ts +19 -2
  267. package/dist/src/core/patch-manifest-io.js +57 -21
  268. package/dist/src/core/patch-manifest-query.js +18 -10
  269. package/dist/src/core/patch-manifest-resolve.js +0 -1
  270. package/dist/src/core/patch-manifest-validate.js +24 -1
  271. package/dist/src/core/patch-manifest.d.ts +1 -1
  272. package/dist/src/core/patch-manifest.js +1 -2
  273. package/dist/src/core/patch-parse.d.ts +59 -0
  274. package/dist/src/core/patch-parse.js +209 -92
  275. package/dist/src/core/patch-policy.js +0 -1
  276. package/dist/src/core/patch-registration-refs.js +16 -26
  277. package/dist/src/core/patch-source-metadata.js +0 -1
  278. package/dist/src/core/patch-transform.d.ts +4 -0
  279. package/dist/src/core/patch-transform.js +30 -38
  280. package/dist/src/core/rebase-session.js +0 -1
  281. package/dist/src/core/register-browser-content.js +0 -1
  282. package/dist/src/core/register-module.js +0 -1
  283. package/dist/src/core/register-result.js +0 -1
  284. package/dist/src/core/register-scaffold.js +0 -1
  285. package/dist/src/core/register-shared-css.js +0 -1
  286. package/dist/src/core/register-test-manifest.js +0 -1
  287. package/dist/src/core/register-xpcshell-test.js +0 -1
  288. package/dist/src/core/shared-ftl.js +0 -1
  289. package/dist/src/core/signal-critical.js +0 -1
  290. package/dist/src/core/smoke-patterns.js +0 -1
  291. package/dist/src/core/state-file.js +0 -1
  292. package/dist/src/core/status-classify.d.ts +16 -0
  293. package/dist/src/core/status-classify.js +24 -16
  294. package/dist/src/core/test-harness-crash.d.ts +80 -7
  295. package/dist/src/core/test-harness-crash.js +251 -15
  296. package/dist/src/core/test-harness-output.js +0 -1
  297. package/dist/src/core/test-path-scope.d.ts +21 -13
  298. package/dist/src/core/test-path-scope.js +43 -28
  299. package/dist/src/core/test-stale-check.d.ts +29 -1
  300. package/dist/src/core/test-stale-check.js +59 -1
  301. package/dist/src/core/test-stale-symlink.js +0 -1
  302. package/dist/src/core/test-xpcshell-retry.js +0 -1
  303. package/dist/src/core/token-coverage.js +0 -1
  304. package/dist/src/core/token-dark-mode.js +0 -1
  305. package/dist/src/core/token-docs.js +0 -1
  306. package/dist/src/core/token-manager.js +0 -1
  307. package/dist/src/core/token-scaffold.js +0 -1
  308. package/dist/src/core/token-variant.js +0 -1
  309. package/dist/src/core/toolchain-preflight.d.ts +31 -10
  310. package/dist/src/core/toolchain-preflight.js +97 -36
  311. package/dist/src/core/typecheck-shim.js +13 -1
  312. package/dist/src/core/typecheck.js +0 -1
  313. package/dist/src/core/wire-destroy.js +0 -1
  314. package/dist/src/core/wire-dom-fragment.js +0 -1
  315. package/dist/src/core/wire-init.js +0 -1
  316. package/dist/src/core/wire-subscript.js +0 -1
  317. package/dist/src/core/wire-targets.js +0 -1
  318. package/dist/src/core/wire-utils.js +0 -1
  319. package/dist/src/core/xpcshell-appdir.js +1 -4
  320. package/dist/src/errors/base.d.ts +29 -0
  321. package/dist/src/errors/base.js +35 -1
  322. package/dist/src/errors/build.js +0 -1
  323. package/dist/src/errors/codes.d.ts +6 -0
  324. package/dist/src/errors/codes.js +6 -1
  325. package/dist/src/errors/config.js +0 -1
  326. package/dist/src/errors/download.js +0 -1
  327. package/dist/src/errors/furnace.d.ts +11 -1
  328. package/dist/src/errors/furnace.js +6 -5
  329. package/dist/src/errors/git.js +0 -1
  330. package/dist/src/errors/patch.d.ts +17 -0
  331. package/dist/src/errors/patch.js +29 -1
  332. package/dist/src/errors/rebase.js +0 -1
  333. package/dist/src/errors/run.js +1 -1
  334. package/dist/src/index.js +0 -1
  335. package/dist/src/types/cli.js +0 -1
  336. package/dist/src/types/commands/index.d.ts +1 -1
  337. package/dist/src/types/commands/index.js +0 -1
  338. package/dist/src/types/commands/options.d.ts +40 -9
  339. package/dist/src/types/commands/options.js +0 -1
  340. package/dist/src/types/commands/patches.d.ts +37 -0
  341. package/dist/src/types/commands/patches.js +0 -1
  342. package/dist/src/types/commands/project.d.ts +11 -2
  343. package/dist/src/types/commands/project.js +0 -1
  344. package/dist/src/types/config.d.ts +32 -0
  345. package/dist/src/types/config.js +0 -1
  346. package/dist/src/types/furnace.d.ts +23 -0
  347. package/dist/src/types/furnace.js +0 -1
  348. package/dist/src/types/index.js +0 -1
  349. package/dist/src/types/typecheck.js +0 -1
  350. package/dist/src/utils/elapsed.js +0 -1
  351. package/dist/src/utils/errors.js +0 -1
  352. package/dist/src/utils/fs.js +37 -5
  353. package/dist/src/utils/logger.d.ts +7 -0
  354. package/dist/src/utils/logger.js +85 -3
  355. package/dist/src/utils/options.d.ts +15 -0
  356. package/dist/src/utils/options.js +25 -1
  357. package/dist/src/utils/package-root.js +11 -4
  358. package/dist/src/utils/parse.js +0 -1
  359. package/dist/src/utils/paths.js +0 -1
  360. package/dist/src/utils/platform.js +0 -1
  361. package/dist/src/utils/process-group.d.ts +33 -0
  362. package/dist/src/utils/process-group.js +161 -0
  363. package/dist/src/utils/process.d.ts +48 -4
  364. package/dist/src/utils/process.js +258 -59
  365. package/dist/src/utils/regex.d.ts +13 -3
  366. package/dist/src/utils/regex.js +53 -4
  367. package/dist/src/utils/validation.d.ts +0 -12
  368. package/dist/src/utils/validation.js +13 -26
  369. package/package.json +9 -8
@@ -15,7 +15,7 @@
15
15
  * on successful build completion; a failed build does not update it, so a
16
16
  * subsequent run still audits against the last known-good tree.
17
17
  */
18
- import type { BuildBaseline } from './build-baseline-types.js';
18
+ import type { BuildBaseline, TestPackagingCoverage } from './build-baseline-types.js';
19
19
  /** Name of the last-build marker file under `.fireforge/`. */
20
20
  export declare const BUILD_BASELINE_FILENAME = "last-build.json";
21
21
  /**
@@ -39,5 +39,8 @@ export declare function readBuildBaseline(projectRoot: string): Promise<BuildBas
39
39
  * @param projectRoot - Root directory of the project
40
40
  * @param engineDir - Path to the engine directory
41
41
  * @param binaryName - Current `binaryName` from fireforge.json
42
+ * @param testPackagingCoverage - Coverage claim of the packaged test
43
+ * runtime this build produced (`'full'`, or the scoped request paths of
44
+ * a `test --build` invocation). Omitted → field left off the marker.
42
45
  */
43
- export declare function writeBuildBaseline(projectRoot: string, engineDir: string, binaryName: string): Promise<void>;
46
+ export declare function writeBuildBaseline(projectRoot: string, engineDir: string, binaryName: string, testPackagingCoverage?: TestPackagingCoverage): Promise<void>;
@@ -65,8 +65,11 @@ export async function readBuildBaseline(projectRoot) {
65
65
  * @param projectRoot - Root directory of the project
66
66
  * @param engineDir - Path to the engine directory
67
67
  * @param binaryName - Current `binaryName` from fireforge.json
68
+ * @param testPackagingCoverage - Coverage claim of the packaged test
69
+ * runtime this build produced (`'full'`, or the scoped request paths of
70
+ * a `test --build` invocation). Omitted → field left off the marker.
68
71
  */
69
- export async function writeBuildBaseline(projectRoot, engineDir, binaryName) {
72
+ export async function writeBuildBaseline(projectRoot, engineDir, binaryName, testPackagingCoverage) {
70
73
  let engineHeadSha = '';
71
74
  try {
72
75
  engineHeadSha = await getHead(engineDir);
@@ -86,6 +89,7 @@ export async function writeBuildBaseline(projectRoot, engineDir, binaryName) {
86
89
  builtAt: new Date().toISOString(),
87
90
  binaryName,
88
91
  ...(packageableFingerprints !== undefined ? { packageableFingerprints } : {}),
92
+ ...(testPackagingCoverage !== undefined ? { testPackagingCoverage } : {}),
89
93
  };
90
94
  await writeJson(getBuildBaselinePath(projectRoot), baseline);
91
95
  }
@@ -140,4 +144,3 @@ async function collectPackageableFingerprints(engineDir) {
140
144
  return undefined;
141
145
  }
142
146
  }
143
- //# sourceMappingURL=build-baseline.js.map
@@ -13,6 +13,7 @@ import { collectChangedEnginePaths } from './engine-changes.js';
13
13
  import { applyAllComponents } from './furnace-apply.js';
14
14
  import { furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, loadFurnaceState, } from './furnace-config.js';
15
15
  import { runFurnaceMutation } from './furnace-operation.js';
16
+ import { countEntriesWithBlockingStepErrors } from './furnace-step-errors.js';
16
17
  import { cleanStories } from './furnace-stories.js';
17
18
  import { generateMozconfig, runMachCapture } from './mach.js';
18
19
  import { explainMachError } from './mach-error-hints.js';
@@ -169,7 +170,7 @@ export async function prepareBuildEnvironment(projectRoot, paths, config, option
169
170
  // These are distinct from `result.errors`, which captures
170
171
  // components that failed before reaching the applied list at all.
171
172
  // The sum of the two is the total count of failed components.
172
- const appliedWithStepErrorsCount = result.applied.filter((entry) => (entry.stepErrors?.length ?? 0) > 0).length;
173
+ const appliedWithStepErrorsCount = countEntriesWithBlockingStepErrors(result.applied);
173
174
  const totalApplyFailures = result.errors.length + appliedWithStepErrorsCount;
174
175
  if (totalApplyFailures > 0) {
175
176
  furnaceSpinner.error('Failed to apply Furnace components');
@@ -210,4 +211,3 @@ export async function prepareBuildEnvironment(projectRoot, paths, config, option
210
211
  }
211
212
  return { furnaceApplied, reconfigured };
212
213
  }
213
- //# sourceMappingURL=build-prepare.js.map
@@ -70,4 +70,3 @@ export function mutateConfig(config, key, value, skipValidation = false) {
70
70
  }
71
71
  return raw;
72
72
  }
73
- //# sourceMappingURL=config-mutate.js.map
@@ -9,9 +9,9 @@ export declare const FIREFORGE_DIR = ".fireforge";
9
9
  /** Name of the state file */
10
10
  export declare const STATE_FILENAME = "state.json";
11
11
  /** Supported top-level fireforge.json keys backed by the current schema. */
12
- export declare const SUPPORTED_CONFIG_ROOT_KEYS: readonly ["name", "vendor", "appId", "binaryName", "firefox", "build", "license", "wire", "patchLint", "patchPolicy", "typecheck", "markerComment"];
12
+ export declare const SUPPORTED_CONFIG_ROOT_KEYS: readonly ["name", "vendor", "appId", "binaryName", "firefox", "build", "test", "externalToolchains", "license", "wire", "patchLint", "patchPolicy", "typecheck", "markerComment"];
13
13
  /** Supported config paths that can be read or set without --force. */
14
- export declare const SUPPORTED_CONFIG_PATHS: readonly ["name", "vendor", "appId", "binaryName", "license", "firefox", "firefox.version", "firefox.product", "firefox.sha256", "build", "build.jobs", "wire", "wire.subscriptDir", "patchLint", "patchLint.checkJs", "patchLint.checkJsStrict", "patchLint.checkJsCompilerOptions", "patchLint.checkJsExtraShim", "patchLint.rawColorAllowlist", "patchLint.jsdocClassMethods", "patchLint.testAssertionFloor", "patchLint.chromeScriptJsDoc", "patchPolicy", "typecheck", "typecheck.projects", "typecheck.extraShim", "markerComment"];
14
+ export declare const SUPPORTED_CONFIG_PATHS: readonly ["name", "vendor", "appId", "binaryName", "license", "firefox", "firefox.version", "firefox.product", "firefox.sha256", "build", "build.jobs", "test", "test.canaryPath", "test.canaryTimeoutSeconds", "externalToolchains", "wire", "wire.subscriptDir", "patchLint", "patchLint.checkJs", "patchLint.checkJsStrict", "patchLint.checkJsCompilerOptions", "patchLint.checkJsExtraShim", "patchLint.rawColorAllowlist", "patchLint.jsdocClassMethods", "patchLint.testAssertionFloor", "patchLint.chromeScriptJsDoc", "patchPolicy", "typecheck", "typecheck.projects", "typecheck.extraShim", "markerComment"];
15
15
  /**
16
16
  * Gets all project paths based on a root directory.
17
17
  * @param root - Root directory of the project
@@ -25,6 +25,8 @@ export const SUPPORTED_CONFIG_ROOT_KEYS = [
25
25
  'binaryName',
26
26
  'firefox',
27
27
  'build',
28
+ 'test',
29
+ 'externalToolchains',
28
30
  'license',
29
31
  'wire',
30
32
  'patchLint',
@@ -45,6 +47,10 @@ export const SUPPORTED_CONFIG_PATHS = [
45
47
  'firefox.sha256',
46
48
  'build',
47
49
  'build.jobs',
50
+ 'test',
51
+ 'test.canaryPath',
52
+ 'test.canaryTimeoutSeconds',
53
+ 'externalToolchains',
48
54
  'wire',
49
55
  'wire.subscriptDir',
50
56
  'patchLint',
@@ -81,4 +87,3 @@ export function getProjectPaths(root) {
81
87
  componentsDir: join(root, 'components'),
82
88
  };
83
89
  }
84
- //# sourceMappingURL=config-paths.js.map
@@ -149,4 +149,3 @@ export async function updateState(root, updates) {
149
149
  await writeJson(paths.state, validateFireForgeState(nextState));
150
150
  });
151
151
  }
152
- //# sourceMappingURL=config-state.js.map
@@ -172,4 +172,3 @@ export function parsePatchPolicyBlock(rec) {
172
172
  }
173
173
  return out;
174
174
  }
175
- //# sourceMappingURL=config-validate-patch-policy.js.map
@@ -0,0 +1,8 @@
1
+ import type { FireForgeConfig } from '../types/config.js';
2
+ import { parseObject } from '../utils/parse.js';
3
+ type ConfigRecord = ReturnType<typeof parseObject>;
4
+ /** Parses the optional `test` block. */
5
+ export declare function parseTestBlock(rec: ConfigRecord, config: FireForgeConfig): void;
6
+ /** Parses optional project-specific external toolchain doctor probes. */
7
+ export declare function parseExternalToolchainsBlock(rec: ConfigRecord, config: FireForgeConfig): void;
8
+ export {};
@@ -0,0 +1,105 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { ConfigError } from '../errors/config.js';
3
+ import { parseObject } from '../utils/parse.js';
4
+ import { isContainedRelativePath } from '../utils/paths.js';
5
+ function optionalConfigString(rec, key, label) {
6
+ const value = rec.raw(key);
7
+ if (value === undefined)
8
+ return undefined;
9
+ if (typeof value !== 'string') {
10
+ throw new ConfigError(`Config field "${label}" must be a string`);
11
+ }
12
+ return value;
13
+ }
14
+ function optionalConfigObject(rec, key) {
15
+ const value = rec.raw(key);
16
+ if (value === undefined)
17
+ return undefined;
18
+ try {
19
+ return rec.object(key);
20
+ }
21
+ catch {
22
+ throw new ConfigError(`Config field "${key}" must be an object`);
23
+ }
24
+ }
25
+ /** Parses the optional `test` block. */
26
+ export function parseTestBlock(rec, config) {
27
+ const testRec = optionalConfigObject(rec, 'test');
28
+ if (!testRec)
29
+ return;
30
+ const out = {};
31
+ const canaryPath = optionalConfigString(testRec, 'canaryPath', 'test.canaryPath');
32
+ if (canaryPath !== undefined) {
33
+ if (canaryPath.trim() === '' || !isContainedRelativePath(canaryPath)) {
34
+ throw new ConfigError('Config field "test.canaryPath" must be a project-relative path');
35
+ }
36
+ out.canaryPath = canaryPath;
37
+ }
38
+ const canaryTimeout = testRec.raw('canaryTimeoutSeconds');
39
+ if (canaryTimeout !== undefined) {
40
+ if (typeof canaryTimeout !== 'number' ||
41
+ !Number.isInteger(canaryTimeout) ||
42
+ canaryTimeout < 1 ||
43
+ canaryTimeout > 600) {
44
+ throw new ConfigError('Config field "test.canaryTimeoutSeconds" must be an integer in 1..600');
45
+ }
46
+ out.canaryTimeoutSeconds = canaryTimeout;
47
+ }
48
+ config.test = out;
49
+ }
50
+ /** Parses optional project-specific external toolchain doctor probes. */
51
+ export function parseExternalToolchainsBlock(rec, config) {
52
+ const raw = rec.raw('externalToolchains');
53
+ if (raw === undefined)
54
+ return;
55
+ if (!Array.isArray(raw)) {
56
+ throw new ConfigError('Config field "externalToolchains" must be an array');
57
+ }
58
+ config.externalToolchains = raw.map((entry, index) => {
59
+ if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
60
+ throw new ConfigError(`Config field "externalToolchains[${String(index)}]" must be an object`);
61
+ }
62
+ const item = entry;
63
+ const name = item['name'];
64
+ if (typeof name !== 'string' || name.trim() === '') {
65
+ throw new ConfigError(`Config field "externalToolchains[${String(index)}].name" must be a non-empty string`);
66
+ }
67
+ const tools = item['tools'];
68
+ if (!Array.isArray(tools) || tools.length === 0) {
69
+ throw new ConfigError(`Config field "externalToolchains[${String(index)}].tools" must be a non-empty array`);
70
+ }
71
+ return {
72
+ name,
73
+ tools: tools.map((tool, toolIndex) => parseExternalToolRequirement(tool, index, toolIndex)),
74
+ };
75
+ });
76
+ }
77
+ function parseExternalToolRequirement(tool, index, toolIndex) {
78
+ const label = `externalToolchains[${String(index)}].tools[${String(toolIndex)}]`;
79
+ if (typeof tool !== 'object' || tool === null || Array.isArray(tool)) {
80
+ throw new ConfigError(`Config field "${label}" must be an object`);
81
+ }
82
+ const toolRec = tool;
83
+ const toolName = toolRec['name'];
84
+ if (typeof toolName !== 'string' || toolName.trim() === '') {
85
+ throw new ConfigError(`Config field "${label}.name" must be a non-empty string`);
86
+ }
87
+ const toolPath = toolRec['path'];
88
+ if (toolPath !== undefined && (typeof toolPath !== 'string' || toolPath.trim() === '')) {
89
+ throw new ConfigError(`Config field "${label}.path" must be a non-empty string`);
90
+ }
91
+ const xcrun = toolRec['xcrun'];
92
+ if (xcrun !== undefined && typeof xcrun !== 'boolean') {
93
+ throw new ConfigError(`Config field "${label}.xcrun" must be a boolean`);
94
+ }
95
+ const required = toolRec['required'];
96
+ if (required !== undefined && typeof required !== 'boolean') {
97
+ throw new ConfigError(`Config field "${label}.required" must be a boolean`);
98
+ }
99
+ return {
100
+ name: toolName,
101
+ ...(toolPath !== undefined ? { path: toolPath } : {}),
102
+ ...(xcrun !== undefined ? { xcrun } : {}),
103
+ ...(required !== undefined ? { required } : {}),
104
+ };
105
+ }
@@ -9,6 +9,7 @@ import { isContainedRelativePath, isExplicitAbsolutePath } from '../utils/paths.
9
9
  import { isValidAppId, isValidFirefoxVersion, isValidProjectLicense, PROJECT_LICENSES, validateFirefoxProductVersionCompatibility, } from '../utils/validation.js';
10
10
  import { SUPPORTED_CONFIG_ROOT_KEYS } from './config-paths.js';
11
11
  import { parsePatchPolicyBlock } from './config-validate-patch-policy.js';
12
+ import { parseExternalToolchainsBlock, parseTestBlock } from './config-validate-test-toolchains.js';
12
13
  /**
13
14
  * Parses and validates the four required identity fields (`name`,
14
15
  * `vendor`, `appId`, `binaryName`): all non-empty strings, with
@@ -145,6 +146,8 @@ export function validateConfig(data) {
145
146
  const firefox = parseFirefoxBlock(rec);
146
147
  const config = { ...identity, firefox };
147
148
  parseBuildBlock(rec, config);
149
+ parseTestBlock(rec, config);
150
+ parseExternalToolchainsBlock(rec, config);
148
151
  parseWireBlock(rec, config);
149
152
  parseLicenseField(rec, config);
150
153
  // Marker comment — appended to lines FireForge writes into upstream files.
@@ -429,4 +432,3 @@ function parseTypecheckProjectOverrides(raw, projects) {
429
432
  }
430
433
  return out;
431
434
  }
432
- //# sourceMappingURL=config-validate.js.map
@@ -149,4 +149,3 @@ export async function withConfigFileLock(root, operation) {
149
149
  `(age: ${Math.round(ageMs / 1000)}s). A previous fireforge process may have crashed.`,
150
150
  });
151
151
  }
152
- //# sourceMappingURL=config.js.map
@@ -134,4 +134,3 @@ export async function appendHistory(patchesDir, entry) {
134
134
  const line = `${JSON.stringify(record)}\n`;
135
135
  await appendFile(join(patchesDir, HISTORY_LOG_FILENAME), line, 'utf-8');
136
136
  }
137
- //# sourceMappingURL=destructive.js.map
@@ -194,7 +194,6 @@ function coalescedHunk(oldLines, newLines, context) {
194
194
  return [];
195
195
  }
196
196
  const contextStart = Math.max(0, firstDiff - context);
197
- const contextEndOld = Math.min(oldLines.length - 1, lastOldDiff + context);
198
197
  const contextEndNew = Math.min(newLines.length - 1, lastNewDiff + context);
199
198
  const hunkLines = [];
200
199
  for (let k = contextStart; k < firstDiff; k++) {
@@ -206,13 +205,21 @@ function coalescedHunk(oldLines, newLines, context) {
206
205
  for (let k = firstDiff; k <= lastNewDiff; k++) {
207
206
  hunkLines.push({ marker: '+', content: newLines[k] });
208
207
  }
209
- const trailingStart = Math.max(lastOldDiff + 1, lastNewDiff + 1);
210
- const trailingEnd = Math.max(contextEndOld, contextEndNew);
211
- for (let k = trailingStart; k <= trailingEnd && k < newLines.length; k++) {
208
+ // Trailing context comes from the common suffix, which lives at
209
+ // DIFFERENT indices on each side (lastOldDiff+1… vs lastNewDiff+1…).
210
+ // The previous mixed-coordinate loop (max() of both sides indexing
211
+ // newLines) emitted wrong context on asymmetric edits, and the trailing
212
+ // lines were excluded from the @@ header lengths entirely — so on
213
+ // >LCS_LINE_LIMIT files the rendered header disagreed with the body.
214
+ for (let k = lastNewDiff + 1; k <= contextEndNew; k++) {
212
215
  hunkLines.push({ marker: ' ', content: newLines[k] });
213
216
  }
214
- const oldLength = lastOldDiff - firstDiff + 1 + (firstDiff - contextStart);
215
- const newLength = lastNewDiff - firstDiff + 1 + (firstDiff - contextStart);
217
+ const leadingContext = firstDiff - contextStart;
218
+ // Common-suffix symmetry: oldLen-1-lastOldDiff === newLen-1-lastNewDiff,
219
+ // so both sides clamp to the same trailing context count.
220
+ const trailingContext = contextEndNew - lastNewDiff;
221
+ const oldLength = lastOldDiff - firstDiff + 1 + leadingContext + trailingContext;
222
+ const newLength = lastNewDiff - firstDiff + 1 + leadingContext + trailingContext;
216
223
  return [
217
224
  {
218
225
  oldStart: contextStart + 1,
@@ -265,4 +272,3 @@ export function renderHunks(hunks) {
265
272
  }
266
273
  return out;
267
274
  }
268
- //# sourceMappingURL=diff-hunks.js.map
@@ -61,4 +61,3 @@ export async function collectChangedEnginePaths(engineDir, baseline, contextLabe
61
61
  }
62
62
  return [...collected].sort();
63
63
  }
64
- //# sourceMappingURL=engine-changes.js.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ *
3
+ */
4
+ export declare function withEngineSessionLock<T>(projectRoot: string, command: string, operation: () => Promise<T>): Promise<T>;
5
+ /**
6
+ *
7
+ */
8
+ export declare function snapshotEngineGeneration(engineDir: string): Promise<string>;
9
+ /**
10
+ *
11
+ */
12
+ export declare function assertEngineGenerationUnchanged(engineDir: string, before: string): Promise<void>;
@@ -0,0 +1,45 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { join } from 'node:path';
3
+ import { GeneralError } from '../errors/base.js';
4
+ import { withFileLock } from './file-lock.js';
5
+ import { git } from './git-base.js';
6
+ const ENGINE_SESSION_LOCK_PATH = join('.fireforge', 'engine-session.lock');
7
+ /**
8
+ *
9
+ */
10
+ export async function withEngineSessionLock(projectRoot, command, operation) {
11
+ if (process.env['FIREFORGE_ENABLE_ENGINE_SESSION_LOCK_IN_TEST'] !== '1' &&
12
+ (process.env['NODE_ENV'] === 'test' || process.env['VITEST'] !== undefined)) {
13
+ return operation();
14
+ }
15
+ return withFileLock(join(projectRoot, ENGINE_SESSION_LOCK_PATH), operation, {
16
+ timeoutMs: 1000,
17
+ ownerMetadata: [`command=${command}`, `started=${new Date().toISOString()}`],
18
+ onTimeoutMessage: `Another FireForge engine-mutating command is already running. ` +
19
+ `Wait for it to finish, then retry \`${command}\`.`,
20
+ onStaleLockMessage: (ageMs) => `Removed stale FireForge engine session lock (${Math.round(ageMs / 1000)}s old).`,
21
+ });
22
+ }
23
+ /**
24
+ *
25
+ */
26
+ export async function snapshotEngineGeneration(engineDir) {
27
+ try {
28
+ const head = (await git(['rev-parse', 'HEAD'], engineDir)).trim();
29
+ const status = await git(['status', '--porcelain=v1', '-z'], engineDir);
30
+ return `${head}\0${status}`;
31
+ }
32
+ catch (error) {
33
+ return `unavailable:${error instanceof Error ? error.message : String(error)}`;
34
+ }
35
+ }
36
+ /**
37
+ *
38
+ */
39
+ export async function assertEngineGenerationUnchanged(engineDir, before) {
40
+ const after = await snapshotEngineGeneration(engineDir);
41
+ if (after === before)
42
+ return;
43
+ throw new GeneralError('engine/ changed while `fireforge test` was running, so this test verdict is invalid/inconclusive.\n\n' +
44
+ 'Use one writer per engine checkout, then rebuild and rerun the affected tests.');
45
+ }
@@ -2,8 +2,16 @@ export interface FileLockOptions {
2
2
  timeoutMs?: number;
3
3
  pollMs?: number;
4
4
  staleMs?: number;
5
+ /**
6
+ * Interval between stale-lock probes while waiting (defaults to
7
+ * {@link STALE_REPROBE_INTERVAL_MS}). Exists mainly so tests can exercise
8
+ * the mid-wait reaping path without multi-second sleeps.
9
+ */
10
+ staleReprobeMs?: number;
5
11
  onTimeoutMessage?: string;
6
12
  onStaleLockMessage?: (ageMs: number) => string | undefined;
13
+ /** Extra owner-file lines for human diagnostics; ignored by lock mechanics. */
14
+ ownerMetadata?: readonly string[];
7
15
  }
8
16
  /** Derives the sibling lock-directory path used to guard a file-based resource. */
9
17
  export declare function createSiblingLockPath(filePath: string, suffix?: string): string;