@hominis/fireforge 0.45.0 → 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 (359) hide show
  1. package/CHANGELOG.md +80 -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.d.ts +2 -0
  18. package/dist/src/commands/doctor.js +7 -8
  19. package/dist/src/commands/download.js +35 -24
  20. package/dist/src/commands/export-all.js +21 -4
  21. package/dist/src/commands/export-flow.js +15 -45
  22. package/dist/src/commands/export-placement-conflicts.js +3 -3
  23. package/dist/src/commands/export-placement-gate.js +2 -8
  24. package/dist/src/commands/export-placement-policy.js +3 -5
  25. package/dist/src/commands/export-shared.d.ts +42 -23
  26. package/dist/src/commands/export-shared.js +4 -19
  27. package/dist/src/commands/export.js +10 -1
  28. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  29. package/dist/src/commands/furnace/authoring-config.js +19 -0
  30. package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
  31. package/dist/src/commands/furnace/create-browser-test.js +3 -8
  32. package/dist/src/commands/furnace/create-dry-run.js +2 -5
  33. package/dist/src/commands/furnace/create-templates.d.ts +17 -1
  34. package/dist/src/commands/furnace/create-templates.js +2 -1
  35. package/dist/src/commands/furnace/create.js +25 -19
  36. package/dist/src/commands/furnace/deploy.js +11 -2
  37. package/dist/src/commands/furnace/init.js +2 -1
  38. package/dist/src/commands/furnace/override.js +28 -19
  39. package/dist/src/commands/furnace/refresh.js +9 -1
  40. package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
  41. package/dist/src/commands/furnace/remove-mutations.js +0 -1
  42. package/dist/src/commands/furnace/remove.js +2 -6
  43. package/dist/src/commands/furnace/rename-browser-test.js +1 -7
  44. package/dist/src/commands/furnace/rename.js +29 -29
  45. package/dist/src/commands/furnace/status.js +12 -9
  46. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  47. package/dist/src/commands/furnace/test-file-name.js +39 -0
  48. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  49. package/dist/src/commands/furnace/validation-output.js +3 -8
  50. package/dist/src/commands/import.js +2 -9
  51. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  52. package/dist/src/commands/lint-per-patch.js +19 -4
  53. package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
  54. package/dist/src/commands/lint.d.ts +2 -1
  55. package/dist/src/commands/lint.js +1 -1
  56. package/dist/src/commands/manifest.js +1 -1
  57. package/dist/src/commands/package.js +2 -4
  58. package/dist/src/commands/patch/compact.js +14 -15
  59. package/dist/src/commands/patch/delete.js +15 -25
  60. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  61. package/dist/src/commands/patch/entry-projection.js +24 -0
  62. package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
  63. package/dist/src/commands/patch/lint-ignore.js +19 -29
  64. package/dist/src/commands/patch/move-files-create.js +24 -10
  65. package/dist/src/commands/patch/move-files-into.js +38 -34
  66. package/dist/src/commands/patch/move-files.js +4 -13
  67. package/dist/src/commands/patch/rename.js +27 -41
  68. package/dist/src/commands/patch/reorder.js +25 -38
  69. package/dist/src/commands/patch/split-plan.d.ts +4 -5
  70. package/dist/src/commands/patch/split-plan.js +4 -8
  71. package/dist/src/commands/patch/split.js +45 -43
  72. package/dist/src/commands/patch/staged-dependency.js +16 -22
  73. package/dist/src/commands/patch/tier.js +15 -25
  74. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  75. package/dist/src/commands/patch-rename-map.js +36 -0
  76. package/dist/src/commands/pending-resolution.d.ts +15 -0
  77. package/dist/src/commands/pending-resolution.js +25 -0
  78. package/dist/src/commands/re-export-adjacent.d.ts +24 -2
  79. package/dist/src/commands/re-export-adjacent.js +32 -16
  80. package/dist/src/commands/re-export-bulk-scan.js +6 -10
  81. package/dist/src/commands/re-export-drift.js +4 -4
  82. package/dist/src/commands/re-export-files.d.ts +2 -3
  83. package/dist/src/commands/re-export-files.js +38 -29
  84. package/dist/src/commands/re-export-lint.d.ts +3 -4
  85. package/dist/src/commands/re-export-lint.js +9 -2
  86. package/dist/src/commands/re-export-scan.js +5 -2
  87. package/dist/src/commands/re-export-single.d.ts +22 -3
  88. package/dist/src/commands/re-export-single.js +19 -11
  89. package/dist/src/commands/re-export.js +28 -11
  90. package/dist/src/commands/rebase/abort.js +3 -8
  91. package/dist/src/commands/rebase/continue.js +12 -12
  92. package/dist/src/commands/rebase/index.js +2 -2
  93. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  94. package/dist/src/commands/rebase/patch-loop.js +2 -7
  95. package/dist/src/commands/resolve.js +18 -10
  96. package/dist/src/commands/run.d.ts +0 -13
  97. package/dist/src/commands/run.js +12 -25
  98. package/dist/src/commands/setup-support.d.ts +16 -4
  99. package/dist/src/commands/setup-support.js +16 -16
  100. package/dist/src/commands/setup.js +3 -9
  101. package/dist/src/commands/source.js +8 -1
  102. package/dist/src/commands/status-check.js +2 -2
  103. package/dist/src/commands/status-output.js +2 -2
  104. package/dist/src/commands/status.js +3 -4
  105. package/dist/src/commands/test-build-phase.d.ts +2 -2
  106. package/dist/src/commands/test-build-phase.js +31 -7
  107. package/dist/src/commands/test-modes.d.ts +2 -2
  108. package/dist/src/commands/test-modes.js +0 -1
  109. package/dist/src/commands/test-register.js +5 -3
  110. package/dist/src/commands/test-run.js +25 -18
  111. package/dist/src/commands/test-stale-gate.js +4 -4
  112. package/dist/src/commands/test-verdict.d.ts +16 -2
  113. package/dist/src/commands/test-verdict.js +18 -3
  114. package/dist/src/commands/test.js +42 -5
  115. package/dist/src/commands/token-list.d.ts +26 -0
  116. package/dist/src/commands/token-list.js +153 -0
  117. package/dist/src/commands/token.js +17 -1
  118. package/dist/src/commands/tree.js +7 -2
  119. package/dist/src/commands/typecheck.js +9 -9
  120. package/dist/src/commands/wire.js +4 -3
  121. package/dist/src/core/branding.d.ts +0 -7
  122. package/dist/src/core/branding.js +3 -2
  123. package/dist/src/core/browser-wire.js +6 -3
  124. package/dist/src/core/build-audit-platform.d.ts +29 -0
  125. package/dist/src/core/build-audit-platform.js +140 -0
  126. package/dist/src/core/build-audit-registration.js +3 -2
  127. package/dist/src/core/build-audit-skip.d.ts +57 -0
  128. package/dist/src/core/build-audit-skip.js +112 -0
  129. package/dist/src/core/build-audit.d.ts +15 -2
  130. package/dist/src/core/build-audit.js +110 -10
  131. package/dist/src/core/build-baseline.d.ts +45 -28
  132. package/dist/src/core/build-baseline.js +6 -29
  133. package/dist/src/core/build-jar-escalation.js +3 -2
  134. package/dist/src/core/build-overwrite-guard.js +1 -1
  135. package/dist/src/core/build-prepare.d.ts +0 -8
  136. package/dist/src/core/build-prepare.js +7 -12
  137. package/dist/src/core/config-paths.d.ts +2 -6
  138. package/dist/src/core/config-paths.js +4 -2
  139. package/dist/src/core/config-state.d.ts +0 -6
  140. package/dist/src/core/config-state.js +1 -19
  141. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  142. package/dist/src/core/config-validate-build-audit.js +80 -0
  143. package/dist/src/core/config-validate-file-size.js +1 -1
  144. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  145. package/dist/src/core/config-validate-patch-policy.js +3 -3
  146. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  147. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  148. package/dist/src/core/config-validate.js +14 -3
  149. package/dist/src/core/config.d.ts +2 -2
  150. package/dist/src/core/config.js +11 -15
  151. package/dist/src/core/coverage-extend.js +4 -4
  152. package/dist/src/core/discard-baseline.js +7 -7
  153. package/dist/src/core/engine-precondition.d.ts +1 -9
  154. package/dist/src/core/engine-precondition.js +3 -8
  155. package/dist/src/core/engine-session-lock.js +13 -17
  156. package/dist/src/core/export-moved-code-shape.js +2 -2
  157. package/dist/src/core/file-lock-owner.d.ts +103 -0
  158. package/dist/src/core/file-lock-owner.js +259 -0
  159. package/dist/src/core/file-lock.d.ts +2 -7
  160. package/dist/src/core/file-lock.js +40 -91
  161. package/dist/src/core/firefox-cache.d.ts +13 -1
  162. package/dist/src/core/firefox-cache.js +59 -34
  163. package/dist/src/core/firefox-download.d.ts +15 -0
  164. package/dist/src/core/firefox-download.js +37 -21
  165. package/dist/src/core/firefox-extract.d.ts +6 -5
  166. package/dist/src/core/firefox-extract.js +36 -18
  167. package/dist/src/core/firefox.d.ts +30 -32
  168. package/dist/src/core/firefox.js +5 -36
  169. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  170. package/dist/src/core/furnace-apply-dry-run.js +1 -7
  171. package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
  172. package/dist/src/core/furnace-apply-helpers.js +8 -19
  173. package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
  174. package/dist/src/core/furnace-apply.d.ts +1 -1
  175. package/dist/src/core/furnace-apply.js +30 -10
  176. package/dist/src/core/furnace-config.d.ts +1 -1
  177. package/dist/src/core/furnace-config.js +23 -36
  178. package/dist/src/core/furnace-constants.js +3 -2
  179. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  180. package/dist/src/core/furnace-dir-entry.js +21 -0
  181. package/dist/src/core/furnace-operation.d.ts +2 -0
  182. package/dist/src/core/furnace-operation.js +3 -1
  183. package/dist/src/core/furnace-refresh.d.ts +18 -6
  184. package/dist/src/core/furnace-refresh.js +3 -6
  185. package/dist/src/core/furnace-registration-ast.d.ts +0 -1
  186. package/dist/src/core/furnace-registration-ast.js +0 -3
  187. package/dist/src/core/furnace-registration.d.ts +2 -1
  188. package/dist/src/core/furnace-registration.js +22 -29
  189. package/dist/src/core/furnace-scanner.js +3 -6
  190. package/dist/src/core/furnace-state-persist.d.ts +2 -2
  191. package/dist/src/core/furnace-validate-registration.js +5 -7
  192. package/dist/src/core/git-base.d.ts +3 -1
  193. package/dist/src/core/git-base.js +25 -3
  194. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  195. package/dist/src/core/git-diff-new-file.js +119 -0
  196. package/dist/src/core/git-diff.d.ts +9 -9
  197. package/dist/src/core/git-diff.js +88 -157
  198. package/dist/src/core/git-file-ops.d.ts +0 -7
  199. package/dist/src/core/git-file-ops.js +4 -28
  200. package/dist/src/core/git-readonly-index.d.ts +0 -2
  201. package/dist/src/core/git-readonly-index.js +0 -4
  202. package/dist/src/core/git-status.d.ts +7 -0
  203. package/dist/src/core/git-status.js +21 -9
  204. package/dist/src/core/git.d.ts +0 -6
  205. package/dist/src/core/git.js +14 -43
  206. package/dist/src/core/harness-orphans.js +8 -7
  207. package/dist/src/core/history-log.d.ts +19 -0
  208. package/dist/src/core/history-log.js +29 -0
  209. package/dist/src/core/mach-error-hints.d.ts +0 -5
  210. package/dist/src/core/mach-error-hints.js +1 -1
  211. package/dist/src/core/mach-known-noise-filter.d.ts +4 -3
  212. package/dist/src/core/mach-known-noise-filter.js +7 -9
  213. package/dist/src/core/mach-mozconfig.js +2 -1
  214. package/dist/src/core/mach-python.d.ts +2 -0
  215. package/dist/src/core/mach-python.js +12 -7
  216. package/dist/src/core/mach-resource-shim.d.ts +13 -1
  217. package/dist/src/core/mach-resource-shim.js +112 -9
  218. package/dist/src/core/mach.d.ts +39 -53
  219. package/dist/src/core/mach.js +13 -81
  220. package/dist/src/core/marionette-port.d.ts +0 -17
  221. package/dist/src/core/marionette-port.js +12 -58
  222. package/dist/src/core/marionette-preflight.js +9 -27
  223. package/dist/src/core/mochitest-server-port.js +3 -3
  224. package/dist/src/core/moz-manifest-rules.js +37 -33
  225. package/dist/src/core/patch-apply-fuzz.js +4 -4
  226. package/dist/src/core/patch-apply.d.ts +4 -5
  227. package/dist/src/core/patch-apply.js +6 -12
  228. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  229. package/dist/src/core/patch-export-coverage.js +1 -30
  230. package/dist/src/core/patch-export-update.d.ts +18 -1
  231. package/dist/src/core/patch-export-update.js +4 -3
  232. package/dist/src/core/patch-export.d.ts +11 -21
  233. package/dist/src/core/patch-export.js +44 -66
  234. package/dist/src/core/patch-files.d.ts +0 -4
  235. package/dist/src/core/patch-files.js +0 -11
  236. package/dist/src/core/patch-lint-binary.d.ts +6 -12
  237. package/dist/src/core/patch-lint-cache.d.ts +17 -1
  238. package/dist/src/core/patch-lint-cache.js +3 -6
  239. package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
  240. package/dist/src/core/patch-lint-checkjs.js +37 -24
  241. package/dist/src/core/patch-lint-creators.d.ts +2 -6
  242. package/dist/src/core/patch-lint-cross.d.ts +16 -8
  243. package/dist/src/core/patch-lint-cross.js +49 -92
  244. package/dist/src/core/patch-lint-css.js +10 -6
  245. package/dist/src/core/patch-lint-file-size.js +3 -3
  246. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  247. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  248. package/dist/src/core/patch-lint-jsdoc.js +36 -36
  249. package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
  250. package/dist/src/core/patch-lint-prettier.js +1 -1
  251. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  252. package/dist/src/core/patch-lint-queue-types.js +12 -0
  253. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  254. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  255. package/dist/src/core/patch-lint.d.ts +14 -9
  256. package/dist/src/core/patch-lint.js +4 -3
  257. package/dist/src/core/patch-lock.js +3 -3
  258. package/dist/src/core/patch-manifest-io.js +8 -7
  259. package/dist/src/core/patch-parse.d.ts +19 -8
  260. package/dist/src/core/patch-parse.js +25 -0
  261. package/dist/src/core/patch-policy.js +15 -27
  262. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  263. package/dist/src/core/patch-source-metadata.js +0 -4
  264. package/dist/src/core/patch-transform.d.ts +0 -8
  265. package/dist/src/core/patch-transform.js +0 -10
  266. package/dist/src/core/process-cpu-time.js +1 -1
  267. package/dist/src/core/rebase-session.d.ts +2 -8
  268. package/dist/src/core/rebase-session.js +5 -17
  269. package/dist/src/core/register-browser-content.js +3 -2
  270. package/dist/src/core/register-browser-test.js +2 -1
  271. package/dist/src/core/register-module.js +2 -1
  272. package/dist/src/core/register-scaffold.js +5 -4
  273. package/dist/src/core/register-shared-css.js +2 -1
  274. package/dist/src/core/run-log-redact.d.ts +38 -0
  275. package/dist/src/core/run-log-redact.js +69 -0
  276. package/dist/src/core/run-log.d.ts +13 -2
  277. package/dist/src/core/run-log.js +67 -18
  278. package/dist/src/core/signal-critical.js +2 -4
  279. package/dist/src/core/smoke-patterns.d.ts +0 -1
  280. package/dist/src/core/smoke-patterns.js +6 -11
  281. package/dist/src/core/state-file.d.ts +12 -1
  282. package/dist/src/core/state-file.js +14 -1
  283. package/dist/src/core/status-classify.js +2 -1
  284. package/dist/src/core/test-changed-prefs.js +26 -11
  285. package/dist/src/core/test-harness-crash.js +2 -2
  286. package/dist/src/core/test-stale-check.d.ts +0 -2
  287. package/dist/src/core/test-stale-check.js +2 -2
  288. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  289. package/dist/src/core/test-xpcshell-retry.js +14 -11
  290. package/dist/src/core/token-category.d.ts +33 -0
  291. package/dist/src/core/token-category.js +56 -48
  292. package/dist/src/core/token-dark-mode.js +2 -14
  293. package/dist/src/core/token-inventory.d.ts +56 -0
  294. package/dist/src/core/token-inventory.js +109 -0
  295. package/dist/src/core/token-manager.d.ts +1 -1
  296. package/dist/src/core/token-manager.js +1 -1
  297. package/dist/src/core/token-variant.js +21 -22
  298. package/dist/src/core/toolchain-preflight.js +1 -1
  299. package/dist/src/core/tree-cow.d.ts +0 -2
  300. package/dist/src/core/tree-cow.js +2 -2
  301. package/dist/src/core/tree-store.js +4 -5
  302. package/dist/src/core/typecheck.js +2 -2
  303. package/dist/src/core/wire-dom-fragment.js +6 -17
  304. package/dist/src/core/wire-utils.js +5 -17
  305. package/dist/src/core/xpcshell-appdir.js +2 -4
  306. package/dist/src/errors/base.d.ts +53 -1
  307. package/dist/src/errors/base.js +79 -11
  308. package/dist/src/errors/build.js +20 -28
  309. package/dist/src/errors/config.d.ts +0 -14
  310. package/dist/src/errors/config.js +13 -34
  311. package/dist/src/errors/download.d.ts +13 -0
  312. package/dist/src/errors/download.js +53 -24
  313. package/dist/src/errors/furnace.js +8 -9
  314. package/dist/src/errors/git.d.ts +8 -8
  315. package/dist/src/errors/git.js +20 -30
  316. package/dist/src/errors/patch.js +8 -10
  317. package/dist/src/errors/rebase.js +7 -7
  318. package/dist/src/types/commands/options.d.ts +77 -123
  319. package/dist/src/types/config.d.ts +34 -2
  320. package/dist/src/types/index.d.ts +0 -1
  321. package/dist/src/utils/assert.d.ts +3 -15
  322. package/dist/src/utils/assert.js +1 -18
  323. package/dist/src/utils/build-info.d.ts +1 -1
  324. package/dist/src/utils/child-env.d.ts +2 -1
  325. package/dist/src/utils/elapsed.js +10 -1
  326. package/dist/src/utils/fs.js +8 -13
  327. package/dist/src/utils/hash.d.ts +12 -0
  328. package/dist/src/utils/hash.js +16 -0
  329. package/dist/src/utils/line-dispatch.d.ts +15 -0
  330. package/dist/src/utils/line-dispatch.js +48 -0
  331. package/dist/src/utils/options.d.ts +1 -7
  332. package/dist/src/utils/options.js +6 -7
  333. package/dist/src/utils/package-root.js +3 -12
  334. package/dist/src/utils/platform.d.ts +0 -4
  335. package/dist/src/utils/process-group.d.ts +2 -1
  336. package/dist/src/utils/process-group.js +20 -19
  337. package/dist/src/utils/process.d.ts +40 -26
  338. package/dist/src/utils/process.js +115 -166
  339. package/dist/src/utils/ps-duration.js +3 -3
  340. package/dist/src/utils/sleep.d.ts +26 -0
  341. package/dist/src/utils/sleep.js +31 -0
  342. package/dist/src/utils/validation.d.ts +2 -38
  343. package/dist/src/utils/validation.js +7 -16
  344. package/docs/configuration.md +89 -1
  345. package/docs/exit-codes.md +31 -19
  346. package/docs/lifecycle-invariants.md +18 -12
  347. package/docs/machine-output.md +31 -8
  348. package/docs/patch-workflow.md +2 -1
  349. package/docs/run-logs.md +44 -4
  350. package/docs/testing.md +8 -1
  351. package/package.json +8 -7
  352. package/dist/src/commands/rebase.d.ts +0 -4
  353. package/dist/src/commands/rebase.js +0 -5
  354. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  355. package/dist/src/core/patch-artifact-normalize.js +0 -24
  356. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  357. package/dist/src/core/patch-lint-reexports.js +0 -8
  358. package/dist/src/core/wire-targets.d.ts +0 -7
  359. package/dist/src/core/wire-targets.js +0 -8
@@ -0,0 +1,112 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Skip classification for the post-build packaging audit.
4
+ *
5
+ * The audit is warn-only, which makes a false positive cheap individually
6
+ * and expensive in aggregate: an operator who learns that `Audit:` lines are
7
+ * usually wrong stops reading the one that is right. Every classifier here
8
+ * exists to remove a warning whose "missing packaged artifact" claim is
9
+ * structurally impossible, and each carries its own reason so the
10
+ * `Packaged:` summary can say what it skipped and why — a run reporting
11
+ * `4 missing` with zero real misses is what this replaces.
12
+ */
13
+ import { readText } from '../utils/fs.js';
14
+ import { normalizePathSlashes } from '../utils/paths.js';
15
+ import { escapeRegex } from '../utils/regex.js';
16
+ import { extractWithBrandingPath } from './mach-mozconfig.js';
17
+ /** Render order for the `Packaged:` summary's per-class breakdown. */
18
+ export const AUDIT_SKIP_REASONS = [
19
+ 'not-packageable',
20
+ 'platform-gated',
21
+ 'platform-gated-ancestor',
22
+ 'tests-not-packaged',
23
+ 'source-unreadable',
24
+ 'branding-not-selected',
25
+ 'storybook-story',
26
+ 'declared-unpackaged',
27
+ ];
28
+ /**
29
+ * Suffixes of Storybook story modules.
30
+ *
31
+ * Firefox's in-tree Storybook renders these through its own Vite config;
32
+ * no `jar.mn` or `moz.build` entry packages one, so "no packaged artifact"
33
+ * is the expected state rather than a missing registration. Kept as a
34
+ * CLOSED list on purpose: generalising to "anything with a dotted infix"
35
+ * would swallow `*.worker.js` and `*.sys.mjs`, which are packaged.
36
+ */
37
+ const STORYBOOK_SUFFIXES = ['.stories.mjs', '.stories.js'];
38
+ /** True for a Storybook story module. */
39
+ export function isStorybookStoryPath(sourcePath) {
40
+ const normalised = normalizePathSlashes(sourcePath);
41
+ return STORYBOOK_SUFFIXES.some((suffix) => normalised.endsWith(suffix));
42
+ }
43
+ /** Prefix under which every branding tree lives. */
44
+ const BRANDING_ROOT = 'browser/branding/';
45
+ /**
46
+ * Reads the branding directory this objdir was configured with.
47
+ *
48
+ * FireForge generates `engine/mozconfig` and writes the
49
+ * `ac_add_options --with-branding=…` line into it, so the selected branding
50
+ * is knowable from the tree. Returns undefined when the mozconfig cannot be
51
+ * read or carries no directive — the caller must then keep warning, because
52
+ * a skip that cannot name its evidence is a masked warning.
53
+ *
54
+ * @param engineDir - Absolute path to the engine root
55
+ * @returns Engine-relative branding path, or undefined
56
+ */
57
+ export async function resolveSelectedBranding(engineDir) {
58
+ try {
59
+ const content = await readText(`${engineDir.replace(/[/\\]+$/, '')}/mozconfig`);
60
+ const selected = extractWithBrandingPath(content);
61
+ return selected === undefined ? undefined : normalizePathSlashes(selected).replace(/\/+$/, '');
62
+ }
63
+ catch {
64
+ return undefined;
65
+ }
66
+ }
67
+ /**
68
+ * True when `sourcePath` lives under a branding tree that is NOT the one
69
+ * this objdir was configured with.
70
+ *
71
+ * An unselected branding directory is not an input to this build at all, so
72
+ * its files can never appear in `dist/`. Worse, the basename walker matched
73
+ * them against the SELECTED branding's same-named artifact and reported the
74
+ * unselected file as "newer than its packaged artifact" — a stale warning
75
+ * about a comparison that has no meaning.
76
+ *
77
+ * @param sourcePath - Engine-relative POSIX path
78
+ * @param selectedBranding - Engine-relative selected branding path
79
+ */
80
+ export function isUnselectedBrandingPath(sourcePath, selectedBranding) {
81
+ if (selectedBranding === undefined)
82
+ return false;
83
+ const normalised = normalizePathSlashes(sourcePath);
84
+ if (!normalised.startsWith(BRANDING_ROOT))
85
+ return false;
86
+ const selected = `${selectedBranding}/`;
87
+ return !normalised.startsWith(selected);
88
+ }
89
+ /**
90
+ * Finds the declaration admitting `sourcePath`, if any.
91
+ *
92
+ * Exact match or a `*`-within-one-segment glob, matching the
93
+ * `support-files` matcher's deliberate narrowness: `**` and other
94
+ * cross-directory shapes are not accepted, because a carve-out that
95
+ * silently widens is exactly the failure this feature is warned against.
96
+ *
97
+ * @param sourcePath - Engine-relative POSIX path
98
+ * @param declarations - The project's `buildAudit.unpackaged` entries
99
+ */
100
+ export function matchUnpackagedDeclaration(sourcePath, declarations) {
101
+ const normalised = normalizePathSlashes(sourcePath);
102
+ return declarations.find((declaration) => {
103
+ const pattern = normalizePathSlashes(declaration.path);
104
+ if (!pattern.includes('*'))
105
+ return pattern === normalised;
106
+ const regex = new RegExp(`^${pattern
107
+ .split('*')
108
+ .map((part) => escapeRegex(part))
109
+ .join('[^/]*')}$`);
110
+ return regex.test(normalised);
111
+ });
112
+ }
@@ -1,3 +1,4 @@
1
+ import { type AuditSkipReason, type UnpackagedDeclaration } from './build-audit-skip.js';
1
2
  import type { BuildBaseline } from './build-baseline-types.js';
2
3
  /**
3
4
  * True for the build-input manifests the build consumes but never packages
@@ -34,9 +35,17 @@ export interface AuditEntry {
34
35
  * updated: an artifact exists and is at least as new as the source.
35
36
  * stale: artifact exists but is older than the source (probable packaging drop).
36
37
  * missing: no artifact with a matching basename was found anywhere under dist/.
37
- * skipped: the file extension / path does not imply packaging; not counted.
38
+ * skipped: the file was not audited; `skipReason` says why.
38
39
  */
39
40
  status: 'updated' | 'stale' | 'missing' | 'skipped';
41
+ /**
42
+ * Why a `skipped` entry was skipped. Present only for `skipped`; the
43
+ * reasons used to exist only as verbose log strings, so the summary line
44
+ * could report `4 missing` on a run with zero real misses and nothing in
45
+ * the counts distinguished a structural non-input from an unregistered
46
+ * file an operator must act on.
47
+ */
48
+ skipReason?: AuditSkipReason;
40
49
  }
41
50
  /** Summary counts for the "Packaged:" end-of-build line. */
42
51
  export interface AuditSummary {
@@ -44,6 +53,8 @@ export interface AuditSummary {
44
53
  stale: number;
45
54
  missing: number;
46
55
  skipped: number;
56
+ /** Per-class skip counts, so the summary can name what it dismissed. */
57
+ skippedByReason: Record<AuditSkipReason, number>;
47
58
  entries: AuditEntry[];
48
59
  }
49
60
  /**
@@ -78,4 +89,6 @@ export declare function isXpcomManifestPath(sourcePath: string): boolean;
78
89
  * @param baseline Optional previous-build baseline marker.
79
90
  * @returns Summary of artifact status counts.
80
91
  */
81
- export declare function auditBuildArtifacts(projectRoot: string, engineDir: string, baseline: BuildBaseline | undefined): Promise<AuditSummary>;
92
+ export declare function auditBuildArtifacts(projectRoot: string, engineDir: string, baseline: BuildBaseline | undefined, options?: {
93
+ unpackaged?: readonly UnpackagedDeclaration[];
94
+ }): Promise<AuditSummary>;
@@ -39,9 +39,10 @@ import { toError } from '../utils/errors.js';
39
39
  import { pathExists } from '../utils/fs.js';
40
40
  import { info, verbose, warn } from '../utils/logger.js';
41
41
  import { normalizePathSlashes } from '../utils/paths.js';
42
- import { detectPlatformGate } from './build-audit-platform.js';
42
+ import { detectAncestorDirsGate, detectPlatformGate } from './build-audit-platform.js';
43
43
  import { collectSameBasenameCandidates, findRegisteredTarget, resolveArtifactByRegistration, } from './build-audit-registration.js';
44
44
  import { countTrailingSegmentMatches, GENERIC_PATH_SEGMENTS, isTestPath, resolveBestArtifact, } from './build-audit-resolve.js';
45
+ import { AUDIT_SKIP_REASONS, isStorybookStoryPath, isUnselectedBrandingPath, matchUnpackagedDeclaration, resolveSelectedBranding, } from './build-audit-skip.js';
45
46
  import { resolveArtifactByKnownTransform } from './build-audit-transforms.js';
46
47
  import { collectChangedEnginePaths } from './engine-changes.js';
47
48
  import { loadPatchesManifest } from './patch-manifest-io.js';
@@ -101,6 +102,10 @@ export function isBuildInputPath(sourcePath) {
101
102
  export function isJarManifestPath(sourcePath) {
102
103
  return sourcePath === 'jar.mn' || sourcePath.endsWith('/jar.mn');
103
104
  }
105
+ /** Zeroed per-class skip counters. */
106
+ function emptySkipCounts() {
107
+ return Object.fromEntries(AUDIT_SKIP_REASONS.map((reason) => [reason, 0]));
108
+ }
104
109
  /**
105
110
  * Decides whether a source path should be packaged. Returns true for paths
106
111
  * whose extension or directory fragment matches a known-packaged pattern,
@@ -147,7 +152,7 @@ export function isPackageablePath(sourcePath) {
147
152
  * @returns True when the path is a `components.conf` manifest.
148
153
  */
149
154
  export function isXpcomManifestPath(sourcePath) {
150
- return basename(sourcePath.replace(/\\/g, '/')) === 'components.conf';
155
+ return basename(normalizePathSlashes(sourcePath)) === 'components.conf';
151
156
  }
152
157
  /*
153
158
  * Finds the unique obj-star directory with a dist subtree, or undefined
@@ -273,6 +278,55 @@ function isConfidentMatch(source, candidate) {
273
278
  }
274
279
  return false;
275
280
  }
281
+ /**
282
+ * Applies a `buildAudit.unpackaged` carve-out to a classified result.
283
+ *
284
+ * Two behaviours, both mirroring the `--expect-unmanaged` model:
285
+ *
286
+ * - An admitted path is LISTED, not silenced. A carve-out nobody can see
287
+ * is how one quietly widens, so the run says which paths it admitted and
288
+ * on what stated reason.
289
+ * - An admitted path that DOES resolve to a packaged artifact is a STALE
290
+ * carve-out: the declaration asserts a fact about the tree that is no
291
+ * longer true, and suppressing on it would hide a real packaging change.
292
+ * That is reported as a warning, not accepted.
293
+ *
294
+ * A declaration matching nothing that changed this run says nothing —
295
+ * unlike `--expect-unmanaged`, whose unseen entries are reported. The
296
+ * difference is deliberate: that is a per-invocation flag list, where an
297
+ * unmet entry is probably a typo in the command just typed. This is a
298
+ * standing config list checked against only the files that happened to
299
+ * change, so "not met" is the normal case and reporting it would put a
300
+ * warning on every build.
301
+ */
302
+ function applyUnpackagedCarveOut(result, ctx) {
303
+ // Already skipped for a more specific structural reason — the carve-out
304
+ // is redundant there and reporting it would be noise.
305
+ if (result.status === 'skipped')
306
+ return result;
307
+ const declaration = matchUnpackagedDeclaration(result.source, ctx.unpackaged);
308
+ if (declaration === undefined)
309
+ return result;
310
+ if (result.artifact !== undefined) {
311
+ return {
312
+ source: result.source,
313
+ artifact: result.artifact,
314
+ status: 'skipped',
315
+ skipReason: 'declared-unpackaged',
316
+ warning: `Audit: engine/${result.source} is declared unpackaged in fireforge.json ` +
317
+ `("${declaration.reason}") but a packaged artifact exists at ${result.artifact}. ` +
318
+ 'The declaration is stale — remove it, or correct its path.',
319
+ };
320
+ }
321
+ return {
322
+ source: result.source,
323
+ artifact: undefined,
324
+ status: 'skipped',
325
+ skipReason: 'declared-unpackaged',
326
+ notice: `Audit: engine/${result.source} admitted as unpackaged by ` +
327
+ `buildAudit.unpackaged "${declaration.path}" — ${declaration.reason}`,
328
+ };
329
+ }
276
330
  /**
277
331
  * Audits one engine source path and returns its entry. Pure orchestration
278
332
  * helper kept separate so `auditBuildArtifacts` stays under the per-function
@@ -280,12 +334,30 @@ function isConfidentMatch(source, candidate) {
280
334
  */
281
335
  async function auditSinglePath(source, ctx) {
282
336
  if (!isPackageablePath(source)) {
283
- return { source, artifact: undefined, status: 'skipped' };
337
+ return { source, artifact: undefined, status: 'skipped', skipReason: 'not-packageable' };
338
+ }
339
+ if (isStorybookStoryPath(source)) {
340
+ verbose(`Audit: skipping engine/${source} — Storybook story, never packaged.`);
341
+ return { source, artifact: undefined, status: 'skipped', skipReason: 'storybook-story' };
342
+ }
343
+ if (isUnselectedBrandingPath(source, ctx.selectedBranding)) {
344
+ verbose(`Audit: skipping engine/${source} — branding tree is not the selected ${ctx.selectedBranding ?? '?'}, so it is not an input to this objdir.`);
345
+ return { source, artifact: undefined, status: 'skipped', skipReason: 'branding-not-selected' };
284
346
  }
285
347
  const gate = await detectPlatformGate(ctx.engineDir, source);
286
348
  if (gate.gatedOff) {
287
349
  verbose(`Audit: skipping engine/${source} — gated off by "${gate.gateExpression ?? '?'}".`);
288
- return { source, artifact: undefined, status: 'skipped' };
350
+ return { source, artifact: undefined, status: 'skipped', skipReason: 'platform-gated' };
351
+ }
352
+ const ancestorGate = await detectAncestorDirsGate(ctx.engineDir, source);
353
+ if (ancestorGate.gatedOff) {
354
+ verbose(`Audit: skipping engine/${source} — its directory is reached through a DIRS entry in engine/${ancestorGate.gateFile ?? '?'} gated by "${ancestorGate.gateExpression ?? '?'}".`);
355
+ return {
356
+ source,
357
+ artifact: undefined,
358
+ status: 'skipped',
359
+ skipReason: 'platform-gated-ancestor',
360
+ };
289
361
  }
290
362
  // Tests only end up under `_tests/` after `mach package-tests` (or a
291
363
  // test-run that invokes the target) has executed. A plain `mach build`
@@ -294,7 +366,7 @@ async function auditSinglePath(source, ctx) {
294
366
  // Skip audit for test sources when no packaged-tests marker is present.
295
367
  if (isTestPath(source) && !ctx.testsPackaged) {
296
368
  verbose(`Audit: skipping engine/${source} — _tests/${PACKAGED_TESTS_MARKER} not present; full test packaging has not run for this build.`);
297
- return { source, artifact: undefined, status: 'skipped' };
369
+ return { source, artifact: undefined, status: 'skipped', skipReason: 'tests-not-packaged' };
298
370
  }
299
371
  const sourcePath = join(ctx.engineDir, source);
300
372
  let sourceMtime;
@@ -304,7 +376,7 @@ async function auditSinglePath(source, ctx) {
304
376
  }
305
377
  catch {
306
378
  // Deletion that didn't propagate — distinct class of bug, not audited yet.
307
- return { source, artifact: undefined, status: 'skipped' };
379
+ return { source, artifact: undefined, status: 'skipped', skipReason: 'source-unreadable' };
308
380
  }
309
381
  const roots = searchRootsFor(source, ctx.distRoot, ctx.testsRoot);
310
382
  // Registration-aware resolution first: a `jar.mn` entry whose `(source)`
@@ -488,12 +560,13 @@ function describeOwnership(ownersByPath, source) {
488
560
  * @param baseline Optional previous-build baseline marker.
489
561
  * @returns Summary of artifact status counts.
490
562
  */
491
- export async function auditBuildArtifacts(projectRoot, engineDir, baseline) {
563
+ export async function auditBuildArtifacts(projectRoot, engineDir, baseline, options = {}) {
492
564
  const summary = {
493
565
  updated: 0,
494
566
  stale: 0,
495
567
  missing: 0,
496
568
  skipped: 0,
569
+ skippedByReason: emptySkipCounts(),
497
570
  entries: [],
498
571
  };
499
572
  const distRoot = await resolveDistRoot(engineDir);
@@ -514,18 +587,45 @@ export async function auditBuildArtifacts(projectRoot, engineDir, baseline) {
514
587
  // it. FireForge already resolves this for `status --ownership`; carrying
515
588
  // it here turns a three-line mystery into three dismissible lines.
516
589
  const ownersByPath = await resolveAuditOwnership(projectRoot);
517
- const ctx = { engineDir, distRoot, testsRoot, testsPackaged };
590
+ const ctx = {
591
+ engineDir,
592
+ distRoot,
593
+ testsRoot,
594
+ testsPackaged,
595
+ selectedBranding: await resolveSelectedBranding(engineDir),
596
+ unpackaged: options.unpackaged ?? [],
597
+ };
518
598
  for (const source of changed) {
519
- const result = await auditSinglePath(source, ctx);
599
+ const result = applyUnpackagedCarveOut(await auditSinglePath(source, ctx), ctx);
520
600
  summary[result.status] += 1;
601
+ if (result.status === 'skipped' && result.skipReason !== undefined) {
602
+ summary.skippedByReason[result.skipReason] += 1;
603
+ }
521
604
  summary.entries.push({
522
605
  source: result.source,
523
606
  artifact: result.artifact,
524
607
  status: result.status,
608
+ ...(result.skipReason === undefined ? {} : { skipReason: result.skipReason }),
525
609
  });
526
610
  if (result.warning)
527
611
  warn(`${result.warning}${describeOwnership(ownersByPath, result.source)}`);
612
+ if (result.notice)
613
+ info(result.notice);
528
614
  }
529
- info(`Packaged: ${summary.updated} updated, ${summary.stale} stale, ${summary.missing} missing, ${summary.skipped} skipped`);
615
+ info(`Packaged: ${summary.updated} updated, ${summary.stale} stale, ${summary.missing} missing, ` +
616
+ `${summary.skipped} skipped${describeSkipBreakdown(summary)}`);
530
617
  return summary;
531
618
  }
619
+ /**
620
+ * Renders the per-class skip breakdown appended to the `Packaged:` line.
621
+ *
622
+ * Without it the summary reported one undifferentiated `skipped` count, so
623
+ * a run that dismissed four unselected-branding files and one
624
+ * ancestor-gated directory looked identical to one that dismissed five
625
+ * unregistered sources — and the four false `missing` entries downstream
626
+ * reported had no counterpart in the counts an operator could check.
627
+ */
628
+ function describeSkipBreakdown(summary) {
629
+ const parts = AUDIT_SKIP_REASONS.filter((reason) => summary.skippedByReason[reason] > 0).map((reason) => `${reason} ${String(summary.skippedByReason[reason])}`);
630
+ return parts.length === 0 ? '' : ` (${parts.join(', ')})`;
631
+ }
@@ -25,8 +25,6 @@ import { type BuildBaseline, type TestPackagingCoverage } from './build-baseline
25
25
  * `'full'`.
26
26
  */
27
27
  export type BaselineBuildKind = 'full' | 'faster';
28
- /** Name of the last-build marker file under `.fireforge/`. */
29
- export declare const BUILD_BASELINE_FILENAME = "last-build.json";
30
28
  /**
31
29
  * Resolves the on-disk path of the build baseline marker.
32
30
  * @param projectRoot - Root directory of the project
@@ -40,36 +38,55 @@ export declare function getBuildBaselinePath(projectRoot: string): string;
40
38
  * @param projectRoot - Root directory of the project
41
39
  */
42
40
  export declare function readBuildBaseline(projectRoot: string): Promise<BuildBaseline | undefined>;
41
+ export interface WriteBuildBaselineOptions {
42
+ /** Root directory of the project. */
43
+ projectRoot: string;
44
+ /** Path to the engine directory. */
45
+ engineDir: string;
46
+ /** Current `binaryName` from fireforge.json. */
47
+ binaryName: string;
48
+ /**
49
+ * Coverage claim of the packaged test runtime this build produced
50
+ * (`'full'`, or the scoped request paths of a `test --build` invocation).
51
+ * Omitted → field left off the marker.
52
+ */
53
+ testPackagingCoverage?: TestPackagingCoverage | undefined;
54
+ /**
55
+ * The baseline this write replaces, when the caller has it. A scoped
56
+ * (non-`'full'`) write carries its `staticComponentsBaseline` forward
57
+ * verbatim — `mach build faster` does not rebake `components.conf`
58
+ * registrations, so the last FULL build stays the honest anchor for the
59
+ * compiled StaticComponents table.
60
+ */
61
+ previousBaseline?: BuildBaseline | undefined;
62
+ /** Invocation shape that produced this baseline. */
63
+ recordedBy?: string | undefined;
64
+ /**
65
+ * `'auto'` (default) refreshes the static-components anchor whenever the
66
+ * coverage claim is `'full'` or absent. `'refresh'` records it even for a
67
+ * scoped coverage claim whose implementation escalated to a full build.
68
+ * `'carry-forward'` always keeps the previous anchor: needed by
69
+ * `--extend-coverage`, whose union can EVALUATE to `'full'` while the
70
+ * build that produced it was still a scoped `mach build faster` that did
71
+ * not rebake the compiled table.
72
+ */
73
+ staticComponentsHandling?: 'auto' | 'refresh' | 'carry-forward' | undefined;
74
+ /**
75
+ * Which mach build produced this baseline (default `'full'`). A
76
+ * `'faster'` write carries the previous record's `jar.mn` fingerprints
77
+ * forward instead of refreshing them — see
78
+ * {@link BuildBaseline.buildInputFingerprints}. Every caller that ran
79
+ * `mach build faster` MUST say so, or the next pre-test build skips an
80
+ * escalation no full build has honoured.
81
+ */
82
+ buildKind?: BaselineBuildKind | undefined;
83
+ }
43
84
  /**
44
85
  * Records a successful build by writing a fresh baseline marker. Captures
45
86
  * engine HEAD SHA (or an empty string when the engine has no HEAD yet) and
46
87
  * the current binaryName. Caller is responsible for only invoking this
47
88
  * after the build exit code was zero.
48
89
  *
49
- * @param projectRoot - Root directory of the project
50
- * @param engineDir - Path to the engine directory
51
- * @param binaryName - Current `binaryName` from fireforge.json
52
- * @param testPackagingCoverage - Coverage claim of the packaged test
53
- * runtime this build produced (`'full'`, or the scoped request paths of
54
- * a `test --build` invocation). Omitted → field left off the marker.
55
- * @param previousBaseline - The baseline this write replaces, when the
56
- * caller has it. A scoped (non-`'full'`) write carries its
57
- * `staticComponentsBaseline` forward verbatim — `mach build faster`
58
- * does not rebake `components.conf` registrations, so the last FULL
59
- * build stays the honest anchor for the compiled StaticComponents table.
60
- * @param recordedBy - Invocation shape that produced this baseline.
61
- * @param staticComponentsHandling - `'auto'` (default) refreshes the
62
- * static-components anchor whenever the coverage claim is `'full'` or
63
- * absent. `'refresh'` records it even for a scoped coverage claim whose
64
- * implementation escalated to a full build. `'carry-forward'` always
65
- * keeps the previous anchor: needed by `--extend-coverage`, whose union
66
- * can EVALUATE to `'full'` while the build that produced it was still a
67
- * scoped `mach build faster` that did not rebake the compiled table.
68
- * @param buildKind - Which mach build produced this baseline (default
69
- * `'full'`). A `'faster'` write carries the previous record's `jar.mn`
70
- * fingerprints forward instead of refreshing them — see
71
- * {@link BuildBaseline.buildInputFingerprints}. Every caller that ran
72
- * `mach build faster` MUST say so, or the next pre-test build skips an
73
- * escalation no full build has honoured.
90
+ * @param options - See {@link WriteBuildBaselineOptions}
74
91
  */
75
- export declare function writeBuildBaseline(projectRoot: string, engineDir: string, binaryName: string, testPackagingCoverage?: TestPackagingCoverage, previousBaseline?: BuildBaseline, recordedBy?: string, staticComponentsHandling?: 'auto' | 'refresh' | 'carry-forward', buildKind?: BaselineBuildKind): Promise<void>;
92
+ export declare function writeBuildBaseline(options: WriteBuildBaselineOptions): Promise<void>;
@@ -16,12 +16,12 @@
16
16
  * on successful build completion; a failed build does not update it, so a
17
17
  * subsequent run still audits against the last known-good tree.
18
18
  */
19
- import { createHash } from 'node:crypto';
20
19
  import { readFile } from 'node:fs/promises';
21
20
  import { join } from 'node:path';
22
21
  import { mapWithConcurrency } from '../utils/concurrency.js';
23
22
  import { getNodeErrorCode, toError } from '../utils/errors.js';
24
23
  import { pathExists, readJson, writeJson } from '../utils/fs.js';
24
+ import { sha256Hex } from '../utils/hash.js';
25
25
  import { verbose } from '../utils/logger.js';
26
26
  import { isBuildInputPath, isJarManifestPath, isPackageablePath, isXpcomManifestPath, } from './build-audit.js';
27
27
  import { DELETED_FILE_FINGERPRINT, } from './build-baseline-types.js';
@@ -36,7 +36,7 @@ import { getUntrackedFiles } from './git-status.js';
36
36
  */
37
37
  const FINGERPRINT_IO_CONCURRENCY = 16;
38
38
  /** Name of the last-build marker file under `.fireforge/`. */
39
- export const BUILD_BASELINE_FILENAME = 'last-build.json';
39
+ const BUILD_BASELINE_FILENAME = 'last-build.json';
40
40
  /**
41
41
  * Resolves the on-disk path of the build baseline marker.
42
42
  * @param projectRoot - Root directory of the project
@@ -71,33 +71,10 @@ export async function readBuildBaseline(projectRoot) {
71
71
  * the current binaryName. Caller is responsible for only invoking this
72
72
  * after the build exit code was zero.
73
73
  *
74
- * @param projectRoot - Root directory of the project
75
- * @param engineDir - Path to the engine directory
76
- * @param binaryName - Current `binaryName` from fireforge.json
77
- * @param testPackagingCoverage - Coverage claim of the packaged test
78
- * runtime this build produced (`'full'`, or the scoped request paths of
79
- * a `test --build` invocation). Omitted → field left off the marker.
80
- * @param previousBaseline - The baseline this write replaces, when the
81
- * caller has it. A scoped (non-`'full'`) write carries its
82
- * `staticComponentsBaseline` forward verbatim — `mach build faster`
83
- * does not rebake `components.conf` registrations, so the last FULL
84
- * build stays the honest anchor for the compiled StaticComponents table.
85
- * @param recordedBy - Invocation shape that produced this baseline.
86
- * @param staticComponentsHandling - `'auto'` (default) refreshes the
87
- * static-components anchor whenever the coverage claim is `'full'` or
88
- * absent. `'refresh'` records it even for a scoped coverage claim whose
89
- * implementation escalated to a full build. `'carry-forward'` always
90
- * keeps the previous anchor: needed by `--extend-coverage`, whose union
91
- * can EVALUATE to `'full'` while the build that produced it was still a
92
- * scoped `mach build faster` that did not rebake the compiled table.
93
- * @param buildKind - Which mach build produced this baseline (default
94
- * `'full'`). A `'faster'` write carries the previous record's `jar.mn`
95
- * fingerprints forward instead of refreshing them — see
96
- * {@link BuildBaseline.buildInputFingerprints}. Every caller that ran
97
- * `mach build faster` MUST say so, or the next pre-test build skips an
98
- * escalation no full build has honoured.
74
+ * @param options - See {@link WriteBuildBaselineOptions}
99
75
  */
100
- export async function writeBuildBaseline(projectRoot, engineDir, binaryName, testPackagingCoverage, previousBaseline, recordedBy, staticComponentsHandling = 'auto', buildKind = 'full') {
76
+ export async function writeBuildBaseline(options) {
77
+ const { projectRoot, engineDir, binaryName, testPackagingCoverage, previousBaseline, recordedBy, staticComponentsHandling = 'auto', buildKind = 'full', } = options;
101
78
  let engineHeadSha = '';
102
79
  try {
103
80
  engineHeadSha = await getHead(engineDir);
@@ -224,7 +201,7 @@ async function collectDirtyFingerprints(engineDir, includePath, contextLabel) {
224
201
  const entries = await mapWithConcurrency(included, FINGERPRINT_IO_CONCURRENCY, async (relPath) => {
225
202
  try {
226
203
  const buffer = await readFile(join(engineDir, relPath));
227
- return [relPath, createHash('sha256').update(buffer).digest('hex')];
204
+ return [relPath, sha256Hex(buffer)];
228
205
  }
229
206
  catch (fileError) {
230
207
  if (getNodeErrorCode(fileError) === 'ENOENT') {
@@ -30,6 +30,7 @@
30
30
  * error direction stays the slow-but-correct one.
31
31
  */
32
32
  import { join } from 'node:path';
33
+ import { toError } from '../utils/errors.js';
33
34
  import { readText } from '../utils/fs.js';
34
35
  import { verbose } from '../utils/logger.js';
35
36
  import { isJarManifestPath } from './build-audit.js';
@@ -82,7 +83,7 @@ export async function evaluateJarManifestEscalation(engineDir, changedPaths, bas
82
83
  }
83
84
  catch (error) {
84
85
  // Fail closed: an unanswerable "is this new?" keeps the old rule.
85
- verbose(`jar escalation: could not classify engine/${path} (${String(error)}); escalating.`);
86
+ verbose(`jar escalation: could not classify engine/${path} (${toError(error).message}); escalating.`);
86
87
  causes.push({ path, reason: 'could not determine whether the manifest is new' });
87
88
  continue;
88
89
  }
@@ -95,7 +96,7 @@ export async function evaluateJarManifestEscalation(engineDir, changedPaths, bas
95
96
  body = await readText(join(engineDir, path));
96
97
  }
97
98
  catch (error) {
98
- verbose(`jar escalation: could not read engine/${path} (${String(error)}); escalating.`);
99
+ verbose(`jar escalation: could not read engine/${path} (${toError(error).message}); escalating.`);
99
100
  causes.push({ path, reason: 'manifest could not be read' });
100
101
  continue;
101
102
  }
@@ -143,7 +143,7 @@ export function formatUnexportedDriftWarning(files) {
143
143
  const owner = entry.owner !== undefined ? ` (owned by ${entry.owner})` : '';
144
144
  return ` ${entry.file} [${entry.classification}]${owner}`;
145
145
  });
146
- return (`This build is about to rewrite ${String(files.length)} engine file(s) from FireForge-owned ` +
146
+ return (`This build is about to rewrite ${files.length} engine file(s) from FireForge-owned ` +
147
147
  'sources, and their current content matches NEITHER a patch body NOR the pristine ' +
148
148
  'baseline — so the edits below are recorded nowhere and the build will destroy them:\n' +
149
149
  `${rows.join('\n')}\n\n` +
@@ -36,14 +36,6 @@ export interface PrepareBuildOptions {
36
36
  */
37
37
  refuseUnexportedDrift?: boolean | undefined;
38
38
  }
39
- /**
40
- * Packaging manifests whose graph/destination directories MAY need a full
41
- * build. Path-shape only: {@link evaluateJarManifestEscalation} decides
42
- * whether a given changed manifest actually forces one (a new manifest, or
43
- * one that redirects its install base directory), so an entry added to an
44
- * existing `dist/bin` manifest no longer costs a ~10-minute build.
45
- */
46
- export declare function requiresFullBuildForIncrementalTest(path: string): boolean;
47
39
  /**
48
40
  * Describes an exit code in the shell's 128+N signal convention. Truncated
49
41
  * configure/build logs with a signal-shaped exit (e.g. 144 = 128+16, SIGURG
@@ -24,16 +24,6 @@ import { generateMozconfig, runMachCapture } from './mach.js';
24
24
  import { explainMachError } from './mach-error-hints.js';
25
25
  /** Path fragments of files whose edits invalidate the recursive-make backend. */
26
26
  const BACKEND_INVALIDATING_SUFFIXES = ['moz.build', 'moz.configure', 'Makefile.in'];
27
- /**
28
- * Packaging manifests whose graph/destination directories MAY need a full
29
- * build. Path-shape only: {@link evaluateJarManifestEscalation} decides
30
- * whether a given changed manifest actually forces one (a new manifest, or
31
- * one that redirects its install base directory), so an entry added to an
32
- * existing `dist/bin` manifest no longer costs a ~10-minute build.
33
- */
34
- export function requiresFullBuildForIncrementalTest(path) {
35
- return isJarManifestPath(path);
36
- }
37
27
  /**
38
28
  * Build-input manifests changed since the last SUCCESSFUL build — not
39
29
  * merely dirty against HEAD. `collectChangedEnginePaths` unions the
@@ -126,9 +116,14 @@ function buildConfigureFailureError(captured) {
126
116
  * @param baseline - Last successful build's baseline
127
117
  */
128
118
  async function decideJarEscalation(engineDir, changed, baseline) {
129
- const decision = await evaluateJarManifestEscalation(engineDir, changed.filter(requiresFullBuildForIncrementalTest), baseline);
119
+ // Packaging manifests whose graph/destination directories MAY need a full
120
+ // build. Path-shape only: `evaluateJarManifestEscalation` decides whether a
121
+ // given changed manifest actually forces one (a new manifest, or one that
122
+ // redirects its install base directory), so an entry added to an existing
123
+ // `dist/bin` manifest no longer costs a ~10-minute build.
124
+ const decision = await evaluateJarManifestEscalation(engineDir, changed.filter(isJarManifestPath), baseline);
130
125
  if (decision.cleared.length > 0) {
131
- verbose(`jar escalation: ${String(decision.cleared.length)} changed jar.mn add entries to an existing dist/bin manifest; not escalating for ${decision.cleared.join(', ')}.`);
126
+ verbose(`jar escalation: ${decision.cleared.length} changed jar.mn add entries to an existing dist/bin manifest; not escalating for ${decision.cleared.join(', ')}.`);
132
127
  }
133
128
  return decision.escalate ? formatJarEscalationNotice(decision) : undefined;
134
129
  }
@@ -2,16 +2,12 @@
2
2
  * Project path derivation from a root directory.
3
3
  */
4
4
  import type { ProjectPaths } from '../types/config.js';
5
- /** Name of the configuration file */
6
- export declare const CONFIG_FILENAME = "fireforge.json";
7
5
  /** Name of the fireforge data directory */
8
6
  export declare const FIREFORGE_DIR = ".fireforge";
9
- /** Name of the state file */
10
- export declare const STATE_FILENAME = "state.json";
11
7
  /** 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", "test", "externalToolchains", "license", "wire", "patchLint", "patchPolicy", "typecheck", "markerComment"];
8
+ export declare const SUPPORTED_CONFIG_ROOT_KEYS: readonly ["name", "vendor", "appId", "binaryName", "firefox", "build", "buildAudit", "test", "externalToolchains", "license", "wire", "patchLint", "patchPolicy", "typecheck", "markerComment"];
13
9
  /** 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", "firefox.candidate", "build", "build.jobs", "test", "test.canaryPath", "test.canaryTimeoutSeconds", "externalToolchains", "wire", "wire.subscriptDir", "patchLint", "patchLint.checkJs", "patchLint.checkJsStrict", "patchLint.checkJsCompilerOptions", "patchLint.checkJsExtraShim", "patchLint.checkJsTestFiles", "patchLint.checkJsTestShim", "patchLint.rawColorAllowlist", "patchLint.jsdocClassMethods", "patchLint.testAssertionFloor", "patchLint.chromeScriptJsDoc", "patchPolicy", "typecheck", "typecheck.projects", "typecheck.extraShim", "markerComment"];
10
+ export declare const SUPPORTED_CONFIG_PATHS: readonly ["name", "vendor", "appId", "binaryName", "license", "firefox", "firefox.version", "firefox.product", "firefox.sha256", "firefox.candidate", "firefox.allowUnverifiedDownload", "build", "build.jobs", "test", "test.canaryPath", "test.canaryTimeoutSeconds", "externalToolchains", "wire", "wire.subscriptDir", "patchLint", "patchLint.checkJs", "patchLint.checkJsStrict", "patchLint.checkJsCompilerOptions", "patchLint.checkJsExtraShim", "patchLint.checkJsTestFiles", "patchLint.checkJsTestShim", "patchLint.rawColorAllowlist", "patchLint.jsdocClassMethods", "patchLint.testAssertionFloor", "patchLint.chromeScriptJsDoc", "patchPolicy", "typecheck", "typecheck.projects", "typecheck.extraShim", "markerComment"];
15
11
  /**
16
12
  * Gets all project paths based on a root directory.
17
13
  * @param root - Root directory of the project
@@ -4,11 +4,11 @@
4
4
  */
5
5
  import { join } from 'node:path';
6
6
  /** Name of the configuration file */
7
- export const CONFIG_FILENAME = 'fireforge.json';
7
+ const CONFIG_FILENAME = 'fireforge.json';
8
8
  /** Name of the fireforge data directory */
9
9
  export const FIREFORGE_DIR = '.fireforge';
10
10
  /** Name of the state file */
11
- export const STATE_FILENAME = 'state.json';
11
+ const STATE_FILENAME = 'state.json';
12
12
  /** Name of the engine directory */
13
13
  const ENGINE_DIR = 'engine';
14
14
  /** Name of the patches directory */
@@ -25,6 +25,7 @@ export const SUPPORTED_CONFIG_ROOT_KEYS = [
25
25
  'binaryName',
26
26
  'firefox',
27
27
  'build',
28
+ 'buildAudit',
28
29
  'test',
29
30
  'externalToolchains',
30
31
  'license',
@@ -46,6 +47,7 @@ export const SUPPORTED_CONFIG_PATHS = [
46
47
  'firefox.product',
47
48
  'firefox.sha256',
48
49
  'firefox.candidate',
50
+ 'firefox.allowUnverifiedDownload',
49
51
  'build',
50
52
  'build.jobs',
51
53
  'test',