@hominis/fireforge 0.49.2 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -1
- package/README.md +1 -1
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/commands/build-prelock.d.ts +20 -0
- package/dist/src/commands/build-prelock.js +50 -0
- package/dist/src/commands/build.d.ts +3 -1
- package/dist/src/commands/build.js +13 -6
- package/dist/src/commands/check-report.d.ts +41 -0
- package/dist/src/commands/check-report.js +91 -0
- package/dist/src/commands/check-run.d.ts +33 -0
- package/dist/src/commands/check-run.js +112 -0
- package/dist/src/commands/check-steps.d.ts +75 -0
- package/dist/src/commands/check-steps.js +200 -0
- package/dist/src/commands/check.d.ts +32 -0
- package/dist/src/commands/check.js +160 -0
- package/dist/src/commands/config.js +5 -6
- package/dist/src/commands/declarations-report.d.ts +43 -0
- package/dist/src/commands/declarations-report.js +76 -0
- package/dist/src/commands/doctor-engine-eslint.js +6 -1
- package/dist/src/commands/doctor-source-pin.js +4 -1
- package/dist/src/commands/export-adopt-check.d.ts +38 -0
- package/dist/src/commands/export-adopt-check.js +98 -0
- package/dist/src/commands/export-flow.js +29 -14
- package/dist/src/commands/export-placement-gate.d.ts +6 -2
- package/dist/src/commands/export-placement-gate.js +8 -0
- package/dist/src/commands/export-shared.d.ts +2 -1
- package/dist/src/commands/export-staged-registration.d.ts +46 -0
- package/dist/src/commands/export-staged-registration.js +91 -0
- package/dist/src/commands/forward-import-remedy.d.ts +32 -0
- package/dist/src/commands/forward-import-remedy.js +63 -0
- package/dist/src/commands/furnace/apply.js +3 -3
- package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
- package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc.js +2 -2
- package/dist/src/commands/furnace/create.js +2 -2
- package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
- package/dist/src/commands/furnace/deploy-named.js +107 -0
- package/dist/src/commands/furnace/deploy.d.ts +3 -2
- package/dist/src/commands/furnace/deploy.js +50 -86
- package/dist/src/commands/furnace/diff.js +26 -3
- package/dist/src/commands/furnace/index.js +66 -41
- package/dist/src/commands/furnace/init.js +13 -6
- package/dist/src/commands/furnace/override.js +4 -2
- package/dist/src/commands/furnace/preview.js +4 -3
- package/dist/src/commands/furnace/refresh.js +2 -2
- package/dist/src/commands/furnace/remove.js +4 -3
- package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
- package/dist/src/commands/furnace/rename-dry-run.js +127 -0
- package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
- package/dist/src/commands/furnace/rename-helpers.js +15 -1
- package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
- package/dist/src/commands/furnace/rename-registrations.js +98 -0
- package/dist/src/commands/furnace/rename.d.ts +2 -1
- package/dist/src/commands/furnace/rename.js +28 -66
- package/dist/src/commands/furnace/scan.d.ts +9 -4
- package/dist/src/commands/furnace/scan.js +11 -9
- package/dist/src/commands/furnace/sync.js +3 -2
- package/dist/src/commands/furnace/validate-report.d.ts +45 -0
- package/dist/src/commands/furnace/validate-report.js +91 -0
- package/dist/src/commands/furnace/validate.d.ts +1 -1
- package/dist/src/commands/furnace/validate.js +151 -113
- package/dist/src/commands/furnace/validation-output.js +14 -3
- package/dist/src/commands/lint-per-patch.js +36 -61
- package/dist/src/commands/lint-per-run-checkjs.js +2 -2
- package/dist/src/commands/lint-report.d.ts +8 -4
- package/dist/src/commands/lint-report.js +20 -5
- package/dist/src/commands/lint.js +15 -34
- package/dist/src/commands/manifest.js +4 -0
- package/dist/src/commands/park.d.ts +22 -0
- package/dist/src/commands/park.js +232 -0
- package/dist/src/commands/patch/delete-gap-notice.js +3 -1
- package/dist/src/commands/patch/delete.d.ts +1 -1
- package/dist/src/commands/patch/delete.js +24 -104
- package/dist/src/commands/patch/index.d.ts +3 -3
- package/dist/src/commands/patch/index.js +10 -4
- package/dist/src/commands/patch/lint-ignore-from.d.ts +44 -0
- package/dist/src/commands/patch/lint-ignore-from.js +142 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
- package/dist/src/commands/patch/lint-ignore.js +78 -31
- package/dist/src/commands/patch/move-files-create.js +34 -18
- package/dist/src/commands/patch/move-files-into.js +15 -7
- package/dist/src/commands/patch/move-files.js +6 -2
- package/dist/src/commands/patch/removal-created-files.d.ts +48 -0
- package/dist/src/commands/patch/removal-created-files.js +83 -0
- package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
- package/dist/src/commands/patch/removal-dependents.js +133 -0
- package/dist/src/commands/patch/rename.js +20 -1
- package/dist/src/commands/patch/reserve.d.ts +33 -0
- package/dist/src/commands/patch/reserve.js +152 -0
- package/dist/src/commands/patch/retire.d.ts +34 -0
- package/dist/src/commands/patch/retire.js +133 -0
- package/dist/src/commands/patch/split-plan.d.ts +17 -8
- package/dist/src/commands/patch/split-plan.js +60 -28
- package/dist/src/commands/patch/split-staged.d.ts +108 -0
- package/dist/src/commands/patch/split-staged.js +202 -0
- package/dist/src/commands/patch/split.d.ts +5 -0
- package/dist/src/commands/patch/split.js +62 -17
- package/dist/src/commands/patch/stage.d.ts +34 -0
- package/dist/src/commands/patch/stage.js +153 -0
- package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
- package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
- package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
- package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
- package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
- package/dist/src/commands/patch/staged-dependency-from.js +108 -0
- package/dist/src/commands/patch/staged-dependency-remove.d.ts +31 -0
- package/dist/src/commands/patch/staged-dependency-remove.js +80 -0
- package/dist/src/commands/patch/staged-dependency.js +54 -139
- package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
- package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
- package/dist/src/commands/re-export-adjacent.d.ts +50 -2
- package/dist/src/commands/re-export-adjacent.js +160 -40
- package/dist/src/commands/re-export-bulk-scan.d.ts +2 -1
- package/dist/src/commands/re-export-bulk-scan.js +22 -7
- package/dist/src/commands/re-export-drift.d.ts +36 -8
- package/dist/src/commands/re-export-drift.js +73 -49
- package/dist/src/commands/re-export-files.js +11 -8
- package/dist/src/commands/re-export-hunks.d.ts +35 -0
- package/dist/src/commands/re-export-hunks.js +220 -0
- package/dist/src/commands/re-export-lint.d.ts +2 -1
- package/dist/src/commands/re-export-lint.js +23 -10
- package/dist/src/commands/re-export-options.js +74 -6
- package/dist/src/commands/re-export-preflight.d.ts +23 -0
- package/dist/src/commands/re-export-preflight.js +160 -0
- package/dist/src/commands/re-export-purity-scope.d.ts +18 -0
- package/dist/src/commands/re-export-purity-scope.js +131 -0
- package/dist/src/commands/re-export-register.js +56 -17
- package/dist/src/commands/re-export-removed.d.ts +13 -2
- package/dist/src/commands/re-export-removed.js +57 -12
- package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
- package/dist/src/commands/re-export-scan-owner.js +116 -0
- package/dist/src/commands/re-export-scan.d.ts +19 -9
- package/dist/src/commands/re-export-scan.js +60 -24
- package/dist/src/commands/re-export-single.js +122 -46
- package/dist/src/commands/re-export-stale-registrations.d.ts +18 -0
- package/dist/src/commands/re-export-stale-registrations.js +62 -0
- package/dist/src/commands/re-export-summary.d.ts +28 -0
- package/dist/src/commands/re-export-summary.js +36 -0
- package/dist/src/commands/re-export.js +71 -57
- package/dist/src/commands/rebase/index.js +17 -17
- package/dist/src/commands/run.js +16 -2
- package/dist/src/commands/status-check.d.ts +55 -16
- package/dist/src/commands/status-check.js +92 -37
- package/dist/src/commands/status-json.d.ts +22 -9
- package/dist/src/commands/status-json.js +47 -32
- package/dist/src/commands/status-output.js +3 -1
- package/dist/src/commands/status-scope.d.ts +37 -0
- package/dist/src/commands/status-scope.js +54 -0
- package/dist/src/commands/status.js +66 -23
- package/dist/src/commands/test-browser-preflight.js +19 -3
- package/dist/src/commands/test-build-furnace.d.ts +22 -0
- package/dist/src/commands/test-build-furnace.js +54 -0
- package/dist/src/commands/test-build-phase.js +64 -41
- package/dist/src/commands/test-checkjs.js +10 -2
- package/dist/src/commands/test-diagnose.js +7 -0
- package/dist/src/commands/test-dispatch.d.ts +39 -0
- package/dist/src/commands/test-dispatch.js +211 -0
- package/dist/src/commands/test-furnace-reach.d.ts +37 -0
- package/dist/src/commands/test-furnace-reach.js +132 -0
- package/dist/src/commands/test-modes.d.ts +16 -1
- package/dist/src/commands/test-modes.js +24 -1
- package/dist/src/commands/test-multi-verdict.d.ts +61 -0
- package/dist/src/commands/test-multi-verdict.js +111 -0
- package/dist/src/commands/test-orchestrate.d.ts +52 -0
- package/dist/src/commands/test-orchestrate.js +240 -0
- package/dist/src/commands/test-perf-scope.d.ts +50 -0
- package/dist/src/commands/test-perf-scope.js +138 -0
- package/dist/src/commands/test-prelock.d.ts +51 -0
- package/dist/src/commands/test-prelock.js +273 -0
- package/dist/src/commands/test-register.js +69 -10
- package/dist/src/commands/test-results.d.ts +62 -0
- package/dist/src/commands/test-results.js +62 -0
- package/dist/src/commands/test-run.d.ts +13 -55
- package/dist/src/commands/test-run.js +74 -99
- package/dist/src/commands/test-shards.d.ts +110 -0
- package/dist/src/commands/test-shards.js +255 -0
- package/dist/src/commands/test-stale-gate.d.ts +5 -1
- package/dist/src/commands/test-stale-gate.js +60 -28
- package/dist/src/commands/test-verdict.d.ts +11 -1
- package/dist/src/commands/test-verdict.js +28 -1
- package/dist/src/commands/test.d.ts +6 -1
- package/dist/src/commands/test.js +120 -124
- package/dist/src/commands/token-lint.d.ts +14 -0
- package/dist/src/commands/token-lint.js +151 -0
- package/dist/src/commands/token.js +10 -0
- package/dist/src/commands/verify-scope.d.ts +28 -0
- package/dist/src/commands/verify-scope.js +67 -0
- package/dist/src/commands/verify.d.ts +10 -2
- package/dist/src/commands/verify.js +67 -36
- package/dist/src/core/build-baseline-types.d.ts +46 -0
- package/dist/src/core/build-baseline-validate.js +19 -0
- package/dist/src/core/build-baseline.d.ts +5 -1
- package/dist/src/core/build-baseline.js +3 -1
- package/dist/src/core/build-overwrite-guard.d.ts +50 -0
- package/dist/src/core/build-overwrite-guard.js +84 -4
- package/dist/src/core/build-prepare.d.ts +13 -0
- package/dist/src/core/build-prepare.js +42 -10
- package/dist/src/core/build-run-record.d.ts +74 -0
- package/dist/src/core/build-run-record.js +136 -0
- package/dist/src/core/checkjs-support-heads.d.ts +41 -0
- package/dist/src/core/checkjs-support-heads.js +104 -0
- package/dist/src/core/config-paths.d.ts +2 -2
- package/dist/src/core/config-paths.js +10 -0
- package/dist/src/core/config-validate-check.d.ts +8 -0
- package/dist/src/core/config-validate-check.js +219 -0
- package/dist/src/core/config-validate-file-size.d.ts +9 -6
- package/dist/src/core/config-validate-file-size.js +91 -2
- package/dist/src/core/config-validate-history.d.ts +11 -0
- package/dist/src/core/config-validate-history.js +38 -0
- package/dist/src/core/config-validate-patch-policy.js +100 -0
- package/dist/src/core/config-validate-test-toolchains.js +21 -0
- package/dist/src/core/config-validate-token-scales.d.ts +9 -0
- package/dist/src/core/config-validate-token-scales.js +92 -0
- package/dist/src/core/config-validate.js +20 -5
- package/dist/src/core/config.d.ts +5 -0
- package/dist/src/core/config.js +16 -0
- package/dist/src/core/coverage-dispatch.d.ts +24 -0
- package/dist/src/core/coverage-dispatch.js +44 -0
- package/dist/src/core/coverage-extend.d.ts +70 -45
- package/dist/src/core/coverage-extend.js +199 -144
- package/dist/src/core/coverage-records.d.ts +108 -0
- package/dist/src/core/coverage-records.js +384 -0
- package/dist/src/core/destructive.d.ts +7 -4
- package/dist/src/core/destructive.js +10 -10
- package/dist/src/core/engine-session-lock.d.ts +16 -1
- package/dist/src/core/engine-session-lock.js +38 -10
- package/dist/src/core/export-moved-code-shape.d.ts +49 -11
- package/dist/src/core/export-moved-code-shape.js +135 -44
- package/dist/src/core/file-lock-queue.d.ts +67 -0
- package/dist/src/core/file-lock-queue.js +221 -0
- package/dist/src/core/file-lock-shared.d.ts +57 -0
- package/dist/src/core/file-lock-shared.js +117 -0
- package/dist/src/core/file-lock.d.ts +13 -25
- package/dist/src/core/file-lock.js +163 -140
- package/dist/src/core/furnace-apply-output.d.ts +10 -0
- package/dist/src/core/furnace-apply-output.js +31 -1
- package/dist/src/core/furnace-apply.js +22 -12
- package/dist/src/core/furnace-composed-source.d.ts +76 -0
- package/dist/src/core/furnace-composed-source.js +167 -0
- package/dist/src/core/furnace-config-validate-exceptions.d.ts +16 -0
- package/dist/src/core/furnace-config-validate-exceptions.js +102 -0
- package/dist/src/core/furnace-config-validate.js +14 -0
- package/dist/src/core/furnace-css-fragments.d.ts +10 -0
- package/dist/src/core/furnace-css-fragments.js +26 -0
- package/dist/src/core/furnace-operation.d.ts +3 -3
- package/dist/src/core/furnace-registration-jar-inplace.d.ts +12 -0
- package/dist/src/core/furnace-registration-jar-inplace.js +80 -0
- package/dist/src/core/furnace-registration-remove.d.ts +22 -1
- package/dist/src/core/furnace-registration-remove.js +53 -17
- package/dist/src/core/furnace-registration-rename.d.ts +26 -0
- package/dist/src/core/furnace-registration-rename.js +77 -0
- package/dist/src/core/furnace-registration.d.ts +14 -4
- package/dist/src/core/furnace-registration.js +44 -10
- package/dist/src/core/furnace-staleness.d.ts +7 -0
- package/dist/src/core/furnace-staleness.js +34 -24
- package/dist/src/core/furnace-validate-accessibility.d.ts +8 -4
- package/dist/src/core/furnace-validate-accessibility.js +71 -28
- package/dist/src/core/furnace-validate-exceptions.d.ts +48 -0
- package/dist/src/core/furnace-validate-exceptions.js +80 -0
- package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
- package/dist/src/core/furnace-validate-icon-button.js +78 -0
- package/dist/src/core/furnace-validate.js +1 -1
- package/dist/src/core/harness-port-fallback.d.ts +60 -0
- package/dist/src/core/harness-port-fallback.js +96 -0
- package/dist/src/core/history-log-file.d.ts +61 -0
- package/dist/src/core/history-log-file.js +164 -0
- package/dist/src/core/history-log.d.ts +2 -1
- package/dist/src/core/history-log.js +2 -1
- package/dist/src/core/json-preserving-edit.d.ts +22 -0
- package/dist/src/core/json-preserving-edit.js +199 -0
- package/dist/src/core/line-prefixer.d.ts +18 -0
- package/dist/src/core/line-prefixer.js +34 -0
- package/dist/src/core/mach.d.ts +7 -0
- package/dist/src/core/mach.js +32 -10
- package/dist/src/core/module-resolution-preflight.d.ts +19 -3
- package/dist/src/core/module-resolution-preflight.js +24 -6
- package/dist/src/core/negative-control-compiled.d.ts +27 -0
- package/dist/src/core/negative-control-compiled.js +73 -0
- package/dist/src/core/negative-control-diff.d.ts +51 -0
- package/dist/src/core/negative-control-diff.js +179 -0
- package/dist/src/core/negative-control.d.ts +150 -0
- package/dist/src/core/negative-control.js +399 -0
- package/dist/src/core/objdir-install-manifests.d.ts +73 -0
- package/dist/src/core/objdir-install-manifests.js +226 -0
- package/dist/src/core/park-store.d.ts +63 -0
- package/dist/src/core/park-store.js +216 -0
- package/dist/src/core/patch-export-update.d.ts +6 -0
- package/dist/src/core/patch-export-update.js +13 -3
- package/dist/src/core/patch-export.d.ts +3 -2
- package/dist/src/core/patch-export.js +22 -16
- package/dist/src/core/patch-files.js +26 -8
- package/dist/src/core/patch-lint-checkjs-any-import.d.ts +39 -0
- package/dist/src/core/patch-lint-checkjs-any-import.js +63 -0
- package/dist/src/core/patch-lint-checkjs-diagnostics.d.ts +17 -0
- package/dist/src/core/patch-lint-checkjs-diagnostics.js +32 -0
- package/dist/src/core/patch-lint-checkjs.d.ts +10 -1
- package/dist/src/core/patch-lint-checkjs.js +20 -13
- package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
- package/dist/src/core/patch-lint-code-lines.js +105 -0
- package/dist/src/core/patch-lint-cross.d.ts +3 -1
- package/dist/src/core/patch-lint-cross.js +17 -4
- package/dist/src/core/patch-lint-css.d.ts +8 -2
- package/dist/src/core/patch-lint-css.js +24 -14
- package/dist/src/core/patch-lint-file-size.d.ts +12 -5
- package/dist/src/core/patch-lint-file-size.js +22 -11
- package/dist/src/core/patch-lint-forward-registration-extended.d.ts +23 -0
- package/dist/src/core/patch-lint-forward-registration-extended.js +36 -11
- package/dist/src/core/patch-lint-forward-registration.js +10 -0
- package/dist/src/core/patch-lint-ignore.d.ts +196 -0
- package/dist/src/core/patch-lint-ignore.js +429 -0
- package/dist/src/core/patch-lint-ownership.d.ts +10 -0
- package/dist/src/core/patch-lint-ownership.js +26 -0
- package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
- package/dist/src/core/patch-lint-size-thresholds.js +110 -0
- package/dist/src/core/patch-lint-size.d.ts +117 -0
- package/dist/src/core/patch-lint-size.js +331 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
- package/dist/src/core/patch-lint-staged-registration.js +20 -0
- package/dist/src/core/patch-lint-token-scale.d.ts +57 -0
- package/dist/src/core/patch-lint-token-scale.js +286 -0
- package/dist/src/core/patch-lint.d.ts +11 -83
- package/dist/src/core/patch-lint.js +66 -288
- package/dist/src/core/patch-lock.js +7 -6
- package/dist/src/core/patch-manifest-consistency.js +7 -6
- package/dist/src/core/patch-manifest-io.d.ts +6 -3
- package/dist/src/core/patch-manifest-io.js +27 -9
- package/dist/src/core/patch-manifest-key-order.d.ts +24 -0
- package/dist/src/core/patch-manifest-key-order.js +61 -0
- package/dist/src/core/patch-manifest-stage.d.ts +46 -0
- package/dist/src/core/patch-manifest-stage.js +96 -0
- package/dist/src/core/patch-manifest-validate.js +61 -3
- package/dist/src/core/patch-order-legal.d.ts +26 -0
- package/dist/src/core/patch-order-legal.js +88 -0
- package/dist/src/core/patch-policy-content.d.ts +40 -0
- package/dist/src/core/patch-policy-content.js +216 -0
- package/dist/src/core/patch-policy.d.ts +22 -5
- package/dist/src/core/patch-policy.js +26 -12
- package/dist/src/core/patch-retired.d.ts +57 -0
- package/dist/src/core/patch-retired.js +92 -0
- package/dist/src/core/patch-subset.d.ts +16 -0
- package/dist/src/core/patch-subset.js +66 -0
- package/dist/src/core/preflight-wait.d.ts +20 -0
- package/dist/src/core/preflight-wait.js +38 -0
- package/dist/src/core/rebase-from-version.d.ts +46 -0
- package/dist/src/core/rebase-from-version.js +71 -0
- package/dist/src/core/run-log.d.ts +26 -0
- package/dist/src/core/run-log.js +106 -4
- package/dist/src/core/run-profile.d.ts +7 -0
- package/dist/src/core/run-profile.js +7 -1
- package/dist/src/core/smoke-patterns.d.ts +5 -0
- package/dist/src/core/smoke-patterns.js +13 -0
- package/dist/src/core/test-harness-crash.d.ts +19 -1
- package/dist/src/core/test-harness-crash.js +8 -9
- package/dist/src/core/test-harness-signature.d.ts +33 -0
- package/dist/src/core/test-harness-signature.js +23 -1
- package/dist/src/core/test-raw-log.d.ts +66 -0
- package/dist/src/core/test-raw-log.js +122 -0
- package/dist/src/core/test-run-completeness.d.ts +90 -0
- package/dist/src/core/test-run-completeness.js +184 -0
- package/dist/src/core/test-stale-check.d.ts +27 -1
- package/dist/src/core/test-stale-check.js +39 -49
- package/dist/src/core/test-stale-exempt.d.ts +40 -0
- package/dist/src/core/test-stale-exempt.js +70 -0
- package/dist/src/core/test-xpcshell-retry.d.ts +2 -0
- package/dist/src/core/test-xpcshell-retry.js +2 -1
- package/dist/src/core/token-lint.d.ts +53 -0
- package/dist/src/core/token-lint.js +131 -0
- package/dist/src/core/token-sibling-sheets.d.ts +46 -0
- package/dist/src/core/token-sibling-sheets.js +119 -0
- package/dist/src/core/tree-guard.js +5 -0
- package/dist/src/core/typecheck-shim.d.ts +6 -2
- package/dist/src/core/typecheck-shim.js +205 -4
- package/dist/src/core/typecheck.js +3 -1
- package/dist/src/index.d.ts +14 -3
- package/dist/src/index.js +6 -1
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +212 -24
- package/dist/src/types/commands/patches.d.ts +118 -1
- package/dist/src/types/config.d.ts +264 -0
- package/dist/src/types/config.js +9 -0
- package/dist/src/types/furnace.d.ts +63 -1
- package/dist/src/types/furnace.js +7 -0
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/utils/options.d.ts +36 -7
- package/dist/src/utils/options.js +94 -8
- package/dist/src/utils/path-glob.d.ts +15 -0
- package/dist/src/utils/path-glob.js +47 -0
- package/dist/src/utils/regex.d.ts +15 -3
- package/dist/src/utils/regex.js +37 -16
- package/docs/configuration.md +369 -11
- package/docs/exit-codes.md +18 -1
- package/docs/furnace.md +139 -3
- package/docs/lifecycle-invariants.md +65 -30
- package/docs/machine-output.md +120 -12
- package/docs/patch-workflow.md +458 -22
- package/docs/run-logs.md +20 -1
- package/docs/testing.md +346 -21
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,89 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.51.0
|
|
4
|
+
|
|
5
|
+
### Test harness
|
|
6
|
+
|
|
7
|
+
- `started=` counts start lines as the output streams, or reads the mozlog raw log when the run wrote one. A suite printing more than 2 MB, or any `--full-output` run, no longer reports files that ran as unstarted. A refusal names its evidence with `started-source=`.
|
|
8
|
+
- Sharded runs print a `FIREFORGE-SHARD:` line per shard and `failed-shards=` on the verdict. New `--shard-size <n>` runs n files per mach invocation, judged per file. New `--results <path>` writes per-file JSON (status, expected, duration, log offset) from mozlog and adds `results=` to the verdict.
|
|
9
|
+
- Negative controls accept a plain unified diff, and a `git diff --no-index` against an edited copy. A control on compiled source is refused without `--build`. With `--build` it goes in before the build and is restored afterwards (`control-build=compiled`). A build-less control on stale packaging is refused before the lock.
|
|
10
|
+
- Mid-run engine edits the running browser cannot see no longer make a run inconclusive (`drift-tolerated=<n>`). A scoped `--build` of one file keeps its manifest siblings' coverage, and packaging refusals arrive before the lock.
|
|
11
|
+
- `--furnace-apply all|reached|none` and `test.furnaceApply` scope the pre-test Furnace apply; components left out are named in `furnace-skipped=`. The default stays `all`.
|
|
12
|
+
|
|
13
|
+
### Locks
|
|
14
|
+
|
|
15
|
+
- New opt-in `--shared-lock` and `test.lockMode: "shared"` let build-less test runs with auto ports overlap on one objdir; builds, re-exports and every other writer stay exclusive. New `test --jobs <n>` runs a request's shards on n harness instances inside one hold, each on its own ports, with `[job i]`-prefixed output.
|
|
16
|
+
- `--wait-lock` and `FIREFORGE_WAIT_LOCK` accept up to 86400 seconds.
|
|
17
|
+
|
|
18
|
+
### Patch queue
|
|
19
|
+
|
|
20
|
+
- Added `patch reserve <order>`, which holds a claimed order under `allowGaps: false`; default allocation skips it, and the export that takes it fills it. Added `patch stage <name>...`, which stages only the named patches' rows of `patches.json` (validated) and their patch files. Every writer emits one canonical `patches.json` key order, so the first full save may reorder keys once.
|
|
21
|
+
- Added `re-export --hunks-from <diff>` and `--exclude-hunks <diff>`, which export a shared file hunk by hunk. Added `fireforge park`, which sets a session's edits to a patch's files aside and merges them back three-way, warning about Fluent ids and files still referenced outside the patch.
|
|
22
|
+
- `re-export --dry-run` takes no engine lock and runs on a private git index. Its purity guard covers only the selected patches, their rows and the engine files they own, and names a concurrent writer instead of calling a peer's write a FireForge bug.
|
|
23
|
+
- Refused `re-export --scan`, `patch move-files` and `patch split` write one `--from`-ready declaration report (`--declarations-out`) and name one `staged-dependency --add --from` call per holding patch. `--refuse-adjacent-unmanaged` combines with `--scan`.
|
|
24
|
+
- `staged-dependency --remove` accepts `--creates` alone, matches registration lines regardless of whitespace, and exits 8 when nothing matches. Re-export drops a staged registration whose `creates` path no patch owns. An `--expect-removed` path owned by an unselected patch names that owner.
|
|
25
|
+
- `patch delete` and `retire` name created files still on disk and gain `--delete-created`. A description-only `patch rename` needs no `--yes`.
|
|
26
|
+
- An unscoped `verify`, and so `fireforge check`, also reads unexported engine files for imports of unregistered `resource:///modules` modules.
|
|
27
|
+
|
|
28
|
+
### Lint and tokens
|
|
29
|
+
|
|
30
|
+
- `lintIgnore` ceilings take a free-form `meta` object and `share: "test"|"product"`, and a notice no longer exceeds a ceiling. `lint --report` adds `lintIgnoreWaivers` for bare waivers. `patch lint-ignore` gains `--add <rule>=<max>`, `--share` and `--from <json|report>`.
|
|
31
|
+
- `patchLint.tokenScales` skips `@font-face` and other descriptor at-rules. Each scale also takes a `prefix` list, `allowInCalc`, `offsetCap`, `exclude` globs and a `minFiles` floor (`token-scale-vacuous`). `token lint` treats a `var()` with a fallback as optional and resolves tokens declared by sibling sheets: from document links, from `@import`, or from `furnace.json#tokenSiblingSheets`.
|
|
32
|
+
- A test script's per-patch checkJs resolves modules created by earlier patches. Opt-in `typecheck.litTypes` types `LitElement` and `MozLitElement`. A scoped `lint --per-patch --patches` keeps stale staged declarations. `discoverPatches` follows symlinked patch files.
|
|
33
|
+
- The API exports `measurePatchSize`, `isPatchSizeRegistrationFile`, `codeLineMask` and `evaluatePatchPolicyContentForQueue`.
|
|
34
|
+
|
|
35
|
+
### Gates
|
|
36
|
+
|
|
37
|
+
- `fireforge check` gains `check.timeout` and `check.timeouts.<step>` for built-in steps, `check.failOnSkipped`, `check.lintReport` (passed to extra steps as `FIREFORGE_CHECK_LINT_REPORT`), and `extraSteps[].after`. The verdict line names `skipped-steps=`.
|
|
38
|
+
|
|
39
|
+
### Furnace
|
|
40
|
+
|
|
41
|
+
- `furnace validate`: an exception no longer suppresses an error (`validation-exception-on-error`), and an exception can name a `tag`. `minComponents` sets a floor (`component-floor`). `panel-item` counts as natively interactive, and the accessibility checks judge the host module together with its companion modules.
|
|
42
|
+
- `furnace deploy` and `furnace rename` edit `jar.mn` lines in place instead of re-sorting the widget's block.
|
|
43
|
+
- The `icon-button-label` check scans a button's content for text outside tags instead of stripping tags with a regex. A `>` inside a quoted attribute no longer ends the tag.
|
|
44
|
+
|
|
45
|
+
### Other
|
|
46
|
+
|
|
47
|
+
- Windows: when two commands race to migrate the legacy history log, the loser stands down instead of failing on a claim the other command holds. The `check` process test no longer assumes a POSIX shell, and the `build` and `test` pre-lock tests no longer assume POSIX paths.
|
|
48
|
+
- Dev tooling: `@types/node` 26.6.5, `@vitest/coverage-v8` and `vitest` 5.0.5, `eslint-plugin-jsdoc` 65.3.6, `knip` 6.41.0, `prettier` 3.9.10, `typescript-eslint` 8.71.2 and `vite` 8.3.4. Runtime: `acorn` 8.19.0 and `magic-string` 1.4.3. CI: `actions/setup-node` 7.1.0 and `github/codeql-action` 4.38.3.
|
|
49
|
+
|
|
50
|
+
## 0.50.0
|
|
51
|
+
|
|
52
|
+
### Size budget
|
|
53
|
+
|
|
54
|
+
- New `patchLint.fileSizeMetric` (`raw` or `code`), `patchSizeMetric` (`diff` or `addedCode`), per-tier `patchSizeThresholds` and `patchSizeRegistrationFiles`. Added code skips comments, blank lines and Furnace fragment copies. Defaults are unchanged. Size messages name their unit, and a notice no longer claims to fail `--max-warnings 0`.
|
|
55
|
+
- `lintIgnore` entries can carry a size ceiling (`{ rule, max, reason }`). Size helpers are exported, and `lint --report` rows carry the metric.
|
|
56
|
+
|
|
57
|
+
### Gates and policy
|
|
58
|
+
|
|
59
|
+
- Added `fireforge check`, which runs status, verify, per-patch lint, typecheck, Furnace validation, token coverage and `check.extraSteps` with one verdict. `status` gains `--fail-on ownership-conflict`, `--allow`, `--fail-on-unknown` and `--patches`; `verify` gains `--patches`.
|
|
60
|
+
- Opt-in `patchPolicy` rules (`upstreamAllowlist`, add-only marker mode, `scopeRules`, `verifyDocumentPaths`), `patchLint.tokenScales` and `fireforge token lint`. `furnace validate` gains `--json`, `--max-warnings` and `validationExceptions`.
|
|
61
|
+
|
|
62
|
+
### Test harness
|
|
63
|
+
|
|
64
|
+
- Packaging coverage is kept per staged path, so a peer's scoped build no longer replaces it. The stale-build check ignores edits the packaged runtime already serves.
|
|
65
|
+
- Mixed xpcshell and browser paths, `--repeat <n>` and `--negative-control <diff>` run under one lock hold. A truncated exit-0 run is `FAIL`. The binary, checkJs and browser/port waits run before the engine lock.
|
|
66
|
+
- Added `--keep-log`, `--auto-ports`, `--no-furnace-apply` and `--restart-between-tests`. A path after an optional-value flag such as `--wait-lock` is read as a test path.
|
|
67
|
+
|
|
68
|
+
### Locks and history
|
|
69
|
+
|
|
70
|
+
- File locks are granted in arrival order, and crashed or stopped waiters are skipped.
|
|
71
|
+
- The patch history log moves to `.fireforge/history.jsonl` (configurable as `history.path`) and is migrated on first write. Records carry an invocation id, and staged-dependency records log only the delta.
|
|
72
|
+
|
|
73
|
+
### Patch queue
|
|
74
|
+
|
|
75
|
+
- `--refuse-foreign-drift` counts removed lines and combines with `--scan`. `--scan-file` accepts `<patch>:<path>`. `--refuse-adjacent-unmanaged` judges only files a re-export would capture, and gains `--expect-new` and `--adjacent-ignore`. Arguments are checked before the lock, and `--expect-from` is new.
|
|
76
|
+
- Added `patch retire`, which keeps the retired patch's order as a slot. `staged-dependency --add --from` takes a lint report. `move-files --create` and `split` keep staged declarations in step. Refusals name the earliest legal order and the exact commands.
|
|
77
|
+
|
|
78
|
+
### Furnace
|
|
79
|
+
|
|
80
|
+
- `--wait-lock` works on every leaf. `deploy` takes several names, and `rename` rewrites registrations in place and gains `--dry-run`. `apply` no longer duplicates jar lines and says why it skipped. The unexported-drift belt and `furnace diff` compare against the fragment-expanded source.
|
|
81
|
+
|
|
82
|
+
### Other
|
|
83
|
+
|
|
84
|
+
- Config writes keep `fireforge.json`'s formatting. `rebase` labels the move from the committed pin. `doctor` tolerates the `esr` suffix and names `./mach npm ci`. `Cu` is typed in the shim. `run --profile` purges caches, and `--smoke-exit` fails a silent early exit.
|
|
85
|
+
- Dev tooling: `eslint` 10.12.0 and `eslint-plugin-jsdoc` 65.0.2.
|
|
86
|
+
|
|
3
87
|
## 0.49.0
|
|
4
88
|
|
|
5
89
|
### Furnace and builds
|
|
@@ -7,6 +91,7 @@
|
|
|
7
91
|
- Safer Furnace deployment and objdir cloning protect component sources and peer checkouts from shared files and directory aliases. Failed copies finish before rollback; cloned build metadata is relocated and verified.
|
|
8
92
|
- `furnace create --localized` no longer scaffolds a `connectedCallback`/`disconnectedCallback` pair that calls `l10n.connectRoot(shadowRoot)` and `disconnectRoot`. `MozLitElement` already registers its shadow render root with `document.l10n`, so the generated pair registered the same root twice, which `DOMLocalization::ConnectRoot` asserts against in debug builds. The scaffold keeps the module-level `window.MozXULElement?.insertFTLIfNeeded(...)` call.
|
|
9
93
|
- Build and test preflight detect broken partial configuration, stale artifacts and invalid build baselines. Scoped builds retain coverage only while their inputs remain unchanged, and builds remove dangling test links.
|
|
94
|
+
- Objdir relocation on Windows rewrites both the backslash and the forward-slash spelling of the old root in dependency metadata (mozbuild writes both), and replaces a directory link that `rename` refuses with `EPERM` in two steps.
|
|
10
95
|
|
|
11
96
|
### Test harness
|
|
12
97
|
|
|
@@ -25,11 +110,12 @@
|
|
|
25
110
|
### Command reliability
|
|
26
111
|
|
|
27
112
|
- Fixed leaf-command `--wait-lock` handling, JSON error reporting and watch retries. Process tracking and independent cleanup preserve the original run outcome through cancellation and cleanup failures.
|
|
113
|
+
- Diff header parsing splits a rename/copy header with `indexOf` instead of a lazy regex, and token-category parsing strips CSS strings in one pass, so both run in linear time on pathological input.
|
|
28
114
|
|
|
29
115
|
### Tooling
|
|
30
116
|
|
|
31
117
|
- Full-Firefox integration preserves existing workspace files and Git index state, and fails on incomplete recovery. Build identity includes untracked contents and binary changes. Release checks enforce the pinned toolchain and additional module coverage floors.
|
|
32
|
-
- Dev tooling: `eslint-plugin-jsdoc` 65.0.1
|
|
118
|
+
- Dev tooling: `eslint-plugin-jsdoc` 65.0.1, `@types/node` 26.6.4, `knip` 6.39.0 and `prettier` 3.9.9. Transitive `source-map-js` 1.2.2 (GHSA-68fv-2mgg-jv7q) and `smol-toml` 1.9.0 (GHSA-r4xh-jqrq-34v2) clear `npm audit`.
|
|
33
119
|
|
|
34
120
|
## 0.48.0
|
|
35
121
|
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Inspired by [fern.js](https://github.com/ghostery/user-agent-desktop) and [Melon
|
|
|
15
15
|
- **Firefox source and build helpers** Download, bootstrap, build, run, test, package, smoke-check, etc.
|
|
16
16
|
- **Wiring and registration** Add chrome scripts, DOM fragments, modules, styles, tests and manifest entries through commands built by learning from existing Firefox conventions.
|
|
17
17
|
- **Furnace components** Create or override `MozLitElement` widgets easily to add new or adapt existing UI components to your needs.
|
|
18
|
-
- **Quality** `lint`, `typecheck`, `verify` and `doctor` catch common issues early.
|
|
18
|
+
- **Quality** `lint`, `typecheck`, `verify` and `doctor` catch common issues early, and `check` runs the static gates together with one verdict.
|
|
19
19
|
- **Tests** FireForge was built by taking apart and applying patches of all sorts to original Firefox source code across different versions and products, learning what works vs doesn't and creating some quite extensive tests based on that covering all manner of scenarios. Yes, we mock quite a bit, but when building a tool that modifies a separate code base, I think it's a solid compromise for the time being. Full end-to-end runs are currently run locally on my MacBook, as they require about 30 GB of disk and significant compute for multiple full builds. Full end-to-end via Actions will be added soonishlyTM but might need a different runner...
|
|
20
20
|
|
|
21
21
|
## Requirements
|
package/dist/bin/fireforge.js
CHANGED
|
@@ -12,6 +12,7 @@ import { removeActivePgidFile } from '../src/commands/test-harness-teardown.js';
|
|
|
12
12
|
import { emitKilledVerdict } from '../src/commands/test-verdict.js';
|
|
13
13
|
import { forceReleaseHeldLocksForSignal } from '../src/core/file-lock.js';
|
|
14
14
|
import { forceReleaseFurnaceLocksForActiveOperations, isSignalRollbackInFlight, rollbackActiveOperationsForSignal, } from '../src/core/furnace-operation.js';
|
|
15
|
+
import { restoreActiveNegativeControlsForSignal } from '../src/core/negative-control.js';
|
|
15
16
|
import { closeActiveRunLog } from '../src/core/run-log.js';
|
|
16
17
|
import { removeActiveRunProfiles } from '../src/core/run-profile.js';
|
|
17
18
|
import { waitForActiveCriticalSections } from '../src/core/signal-critical.js';
|
|
@@ -97,6 +98,11 @@ function installFurnaceSignalHandler(signal, exitCode) {
|
|
|
97
98
|
waitForActiveCriticalSections(SIGNAL_CRITICAL_SECTION_TIMEOUT_MS),
|
|
98
99
|
waitForActiveChildShutdown(CHILD_SHUTDOWN_TIMEOUT_MS),
|
|
99
100
|
])
|
|
101
|
+
// A `test --negative-control` mutation must be undone while this
|
|
102
|
+
// process still holds the engine lock, or the next lock holder builds
|
|
103
|
+
// and tests the neuter. Runs after child shutdown, so the dying
|
|
104
|
+
// harness no longer reads the files. Never throws.
|
|
105
|
+
.then(() => restoreActiveNegativeControlsForSignal())
|
|
100
106
|
// `withFileLock`'s `finally { rm }` never runs across `process.exit`,
|
|
101
107
|
// so both sweeps exist for the same reason. The generic one covers
|
|
102
108
|
// every lock this process holds (engine session, patch directory,
|
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.
|
|
4
|
-
"commit": "
|
|
5
|
-
"shortCommit": "
|
|
3
|
+
"version": "0.51.0",
|
|
4
|
+
"commit": "bd93338b037832fdcd69259a70a3a4b134b98906",
|
|
5
|
+
"shortCommit": "bd93338b0378",
|
|
6
6
|
"dirty": false,
|
|
7
7
|
"dirtyHash": null,
|
|
8
|
-
"buildTime": "2026-10-
|
|
8
|
+
"buildTime": "2026-10-19T12:33:48.474Z"
|
|
9
9
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fireforge build --wait-browser` spends its budget before the engine
|
|
3
|
+
* session lock, not while holding it.
|
|
4
|
+
*
|
|
5
|
+
* A build queued behind a running objdir browser (a demo batch, an
|
|
6
|
+
* end-to-end harness) used to wait inside `withEngineSessionLock`, so every
|
|
7
|
+
* peer's build, test and re-export queued behind a run that was doing
|
|
8
|
+
* nothing. The wait now happens first, and the in-lock objdir preflight
|
|
9
|
+
* re-checks once (the spent budget is stripped from the options it gets).
|
|
10
|
+
*/
|
|
11
|
+
import type { BuildOptions } from '../types/commands/index.js';
|
|
12
|
+
/**
|
|
13
|
+
* Waits for this objdir's browser before the lock when `--wait-browser`
|
|
14
|
+
* was given and there is a launchable bundle to watch.
|
|
15
|
+
*
|
|
16
|
+
* @param projectRoot - Project root
|
|
17
|
+
* @param options - Parsed build options
|
|
18
|
+
* @returns The options the in-lock run should use
|
|
19
|
+
*/
|
|
20
|
+
export declare function waitForBuildBrowserBeforeLock(projectRoot: string, options: BuildOptions): Promise<BuildOptions>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `fireforge build --wait-browser` spends its budget before the engine
|
|
4
|
+
* session lock, not while holding it.
|
|
5
|
+
*
|
|
6
|
+
* A build queued behind a running objdir browser (a demo batch, an
|
|
7
|
+
* end-to-end harness) used to wait inside `withEngineSessionLock`, so every
|
|
8
|
+
* peer's build, test and re-export queued behind a run that was doing
|
|
9
|
+
* nothing. The wait now happens first, and the in-lock objdir preflight
|
|
10
|
+
* re-checks once (the spent budget is stripped from the options it gets).
|
|
11
|
+
*/
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
14
|
+
import { hasBuildArtifacts, hasRunnableBundle } from '../core/mach.js';
|
|
15
|
+
import { ensureLaunchableBrowserNotRunning } from '../core/marionette-port.js';
|
|
16
|
+
import { waitBeforeLock } from '../core/preflight-wait.js';
|
|
17
|
+
import { pathExists } from '../utils/fs.js';
|
|
18
|
+
import { resolveWaitLockSeconds } from '../utils/options.js';
|
|
19
|
+
/**
|
|
20
|
+
* Waits for this objdir's browser before the lock when `--wait-browser`
|
|
21
|
+
* was given and there is a launchable bundle to watch.
|
|
22
|
+
*
|
|
23
|
+
* @param projectRoot - Project root
|
|
24
|
+
* @param options - Parsed build options
|
|
25
|
+
* @returns The options the in-lock run should use
|
|
26
|
+
*/
|
|
27
|
+
export async function waitForBuildBrowserBeforeLock(projectRoot, options) {
|
|
28
|
+
const { waitBrowser, ...rest } = options;
|
|
29
|
+
if (waitBrowser === undefined)
|
|
30
|
+
return options;
|
|
31
|
+
const paths = getProjectPaths(projectRoot);
|
|
32
|
+
if (!(await pathExists(paths.engine)))
|
|
33
|
+
return options;
|
|
34
|
+
const { objDir } = await hasBuildArtifacts(paths.engine);
|
|
35
|
+
if (objDir === undefined)
|
|
36
|
+
return options;
|
|
37
|
+
let binaryName;
|
|
38
|
+
try {
|
|
39
|
+
({ binaryName } = await loadConfig(projectRoot));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return options;
|
|
43
|
+
}
|
|
44
|
+
const bundle = await hasRunnableBundle(paths.engine, binaryName, objDir);
|
|
45
|
+
const expectedPath = bundle.expectedPath;
|
|
46
|
+
if (!bundle.runnable || expectedPath === undefined)
|
|
47
|
+
return options;
|
|
48
|
+
await waitBeforeLock(() => ensureLaunchableBrowserNotRunning(join(paths.engine, expectedPath)), resolveWaitLockSeconds(waitBrowser));
|
|
49
|
+
return rest;
|
|
50
|
+
}
|
|
@@ -5,7 +5,9 @@ import type { BuildOptions } from '../types/commands/index.js';
|
|
|
5
5
|
* Runs the build command.
|
|
6
6
|
* @param projectRoot - Root directory of the project
|
|
7
7
|
* @param options - Build options
|
|
8
|
+
* @param underLock - Runs the build under the engine session lock; a
|
|
9
|
+
* `--wait-browser` budget is spent before it (see build-prelock.ts)
|
|
8
10
|
*/
|
|
9
|
-
export declare function buildCommand(projectRoot: string, options: BuildOptions): Promise<void>;
|
|
11
|
+
export declare function buildCommand(projectRoot: string, options: BuildOptions, underLock?: (body: () => Promise<void>) => Promise<void>): Promise<void>;
|
|
10
12
|
/** Registers the build command on the CLI program. */
|
|
11
13
|
export declare function registerBuild(program: Command, { getProjectRoot, withErrorHandling }: CommandContext): void;
|
|
@@ -13,7 +13,7 @@ import { findObjdirRelocationViolation } from '../core/mach-build-artifacts.js';
|
|
|
13
13
|
import { ensureLaunchableBrowserNotRunning } from '../core/marionette-port.js';
|
|
14
14
|
import { assertHealthyPartialConfig, pruneDanglingTestLinks } from '../core/objdir-maintenance.js';
|
|
15
15
|
import { waitForPreflight } from '../core/preflight-wait.js';
|
|
16
|
-
import { closeActiveRunLog, getActiveRunLogPath, openRunLog, setActiveRunLog, } from '../core/run-log.js';
|
|
16
|
+
import { closeActiveRunLog, getActiveRunLogPath, keepRunLogCopy, openRunLog, setActiveRunLog, } from '../core/run-log.js';
|
|
17
17
|
import { buildHarnessCrashMessage } from '../core/test-harness-crash.js';
|
|
18
18
|
import { formatToolchainMismatchMessage, runToolchainPreflight, } from '../core/toolchain-preflight.js';
|
|
19
19
|
import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
@@ -24,6 +24,7 @@ import { checkDiskSpace } from '../utils/fs.js';
|
|
|
24
24
|
import { error, info, intro, notice, outro, spinner, verbose, warn } from '../utils/logger.js';
|
|
25
25
|
import { addWaitLockOption, commanderArgParser, pickDefined, resolveWaitLockSeconds, } from '../utils/options.js';
|
|
26
26
|
import { isPositiveInteger } from '../utils/validation.js';
|
|
27
|
+
import { waitForBuildBrowserBeforeLock } from './build-prelock.js';
|
|
27
28
|
function parseJobCount(value) {
|
|
28
29
|
const parsed = Number(value);
|
|
29
30
|
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
@@ -177,17 +178,20 @@ function buildFailureDiagnostics(result, engineDir, objDir, machCommand) {
|
|
|
177
178
|
* Runs the build command.
|
|
178
179
|
* @param projectRoot - Root directory of the project
|
|
179
180
|
* @param options - Build options
|
|
181
|
+
* @param underLock - Runs the build under the engine session lock; a
|
|
182
|
+
* `--wait-browser` budget is spent before it (see build-prelock.ts)
|
|
180
183
|
*/
|
|
181
|
-
export async function buildCommand(projectRoot, options) {
|
|
184
|
+
export async function buildCommand(projectRoot, options, underLock = (body) => body()) {
|
|
182
185
|
const buildType = options.ui ? 'UI-only' : 'Full';
|
|
183
186
|
const brandInfo = options.brand ? ` [${options.brand}]` : '';
|
|
184
187
|
intro(`FireForge Build (${buildType}${brandInfo})`);
|
|
185
188
|
// A build's own complete output, kept independently of whatever the
|
|
186
189
|
// operator piped the run through. `build` prints no FIREFORGE-VERDICT
|
|
187
190
|
// line, so the path is announced directly at the end of the run.
|
|
188
|
-
setActiveRunLog(await openRunLog(projectRoot, 'build'));
|
|
191
|
+
setActiveRunLog(await keepRunLogCopy(await openRunLog(projectRoot, 'build'), options.keepLog, 'build'));
|
|
189
192
|
try {
|
|
190
|
-
await
|
|
193
|
+
const lockedOptions = await waitForBuildBrowserBeforeLock(projectRoot, options);
|
|
194
|
+
await underLock(() => runBuildCommandBody(projectRoot, lockedOptions));
|
|
191
195
|
}
|
|
192
196
|
finally {
|
|
193
197
|
const logPath = getActiveRunLogPath();
|
|
@@ -400,11 +404,12 @@ export function registerBuild(program, { getProjectRoot, withErrorHandling }) {
|
|
|
400
404
|
.command('build')
|
|
401
405
|
.description('Build the browser (auto-applies Furnace components first)')
|
|
402
406
|
.option('--wait-browser [seconds]', 'Wait for this objdir browser to exit before changing its build (default 60)', commanderArgParser((raw) => resolveWaitLockSeconds(raw)))
|
|
407
|
+
.option('--keep-log <path>', "Also write this run's log to <path> (a directory receives it under its own name), outside .fireforge/logs/ retention. FIREFORGE_LOG_KEEP sets a session default.")
|
|
403
408
|
.option('--ui', 'Fast UI-only rebuild')
|
|
404
409
|
.option('-j, --jobs <n>', 'Number of parallel jobs', parseJobCount)
|
|
405
410
|
.option('--brand <name>', 'Build specific brand')
|
|
406
411
|
.option('--rewrite-mozinfo', 'On a mozinfo path mismatch, patch mozinfo.json paths in place and run mach configure instead of aborting with a clean-rebuild instruction. Refuses anything that is not a pure prefix-move.')
|
|
407
|
-
.option('--refuse-unexported-drift', 'Refuse (instead of warning) when the build would overwrite engine content recorded in neither a patch body nor the pristine baseline — the multi-session hazard where an unexported edit is silently reverted and a later re-export captures a hybrid')
|
|
412
|
+
.option('--refuse-unexported-drift', 'Refuse (instead of warning) when the build would overwrite engine content recorded in neither a patch body nor the pristine baseline — the multi-session hazard where an unexported edit is silently reverted and a later re-export captures a hybrid. A deployed Furnace file that is a deployment of its own component source is allowed')
|
|
408
413
|
.addHelpText('after', [
|
|
409
414
|
'',
|
|
410
415
|
'Furnace apply runs automatically before the build step, so edits in',
|
|
@@ -425,6 +430,8 @@ export function registerBuild(program, { getProjectRoot, withErrorHandling }) {
|
|
|
425
430
|
].join('\n'));
|
|
426
431
|
addWaitLockOption(build).action(withErrorHandling(async (options) => {
|
|
427
432
|
const projectRoot = getProjectRoot();
|
|
428
|
-
await
|
|
433
|
+
await buildCommand(projectRoot, pickDefined(options), (body) => withEngineSessionLock(projectRoot, 'build', body, {
|
|
434
|
+
waitLockSeconds: resolveWaitLockSeconds(options.waitLock),
|
|
435
|
+
}));
|
|
429
436
|
}));
|
|
430
437
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { CheckStepResult } from './check-run.js';
|
|
2
|
+
/**
|
|
3
|
+
* Why a check failed. `step-failures`: at least one step went red.
|
|
4
|
+
* `lock-timeout`: every failing step exited 15, so nothing was judged red,
|
|
5
|
+
* the steps just never got their locks. Re-queue rather than fix.
|
|
6
|
+
* `skipped-steps`: nothing went red, but `check.failOnSkipped` is set and a
|
|
7
|
+
* step had nothing to run.
|
|
8
|
+
*/
|
|
9
|
+
export type CheckVerdictReason = 'step-failures' | 'lock-timeout' | 'skipped-steps';
|
|
10
|
+
/** How the verdict treats skipped steps. */
|
|
11
|
+
export interface CheckJudgeOptions {
|
|
12
|
+
/** `check.failOnSkipped`: a step skipped for want of anything to run fails the check. */
|
|
13
|
+
failOnSkipped?: boolean | undefined;
|
|
14
|
+
}
|
|
15
|
+
/** The verdict over all steps. */
|
|
16
|
+
export interface CheckVerdict {
|
|
17
|
+
passed: boolean;
|
|
18
|
+
reason?: CheckVerdictReason;
|
|
19
|
+
failed: string[];
|
|
20
|
+
skipped: string[];
|
|
21
|
+
steps: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Judges the results: any failing step fails the check. Under
|
|
25
|
+
* `failOnSkipped`, so does a step skipped because the project has nothing
|
|
26
|
+
* for it to run; it is then listed in `failed` too.
|
|
27
|
+
*/
|
|
28
|
+
export declare function judgeCheck(results: readonly CheckStepResult[], options?: CheckJudgeOptions): CheckVerdict;
|
|
29
|
+
/**
|
|
30
|
+
* The verdict line: `FIREFORGE-VERDICT: PASS steps=<n> skipped=<n>` or
|
|
31
|
+
* `FIREFORGE-VERDICT: FAIL reason=<reason> failed=<a,b> steps=<n> skipped=<n>`,
|
|
32
|
+
* with an additive `skipped-steps=<a,b>` naming the skipped steps when
|
|
33
|
+
* there are any.
|
|
34
|
+
*/
|
|
35
|
+
export declare function formatCheckVerdictLine(verdict: CheckVerdict): string;
|
|
36
|
+
/** The per-step table: one aligned row per step, with its detail. */
|
|
37
|
+
export declare function formatCheckTable(results: readonly CheckStepResult[]): string;
|
|
38
|
+
/** Writes the verdict line as the run's last stdout write, and seals stdout. */
|
|
39
|
+
export declare function writeCheckVerdictLine(verdict: CheckVerdict): void;
|
|
40
|
+
/** Writes the `--json` document (schemaVersion 1) and seals stdout. */
|
|
41
|
+
export declare function writeCheckJson(results: readonly CheckStepResult[], verdict: CheckVerdict): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `fireforge check` output: the per-step table, the single
|
|
4
|
+
* `FIREFORGE-VERDICT:` line, and the `--json` document. The verdict is
|
|
5
|
+
* computed once here and shared by all three, so the exit code, the line
|
|
6
|
+
* and the JSON can never disagree.
|
|
7
|
+
*/
|
|
8
|
+
import { setStdoutSealed } from '../utils/logger.js';
|
|
9
|
+
/** Exit code a step reports when its lock wait expired. */
|
|
10
|
+
const LOCK_TIMEOUT_EXIT = 15;
|
|
11
|
+
/**
|
|
12
|
+
* Judges the results: any failing step fails the check. Under
|
|
13
|
+
* `failOnSkipped`, so does a step skipped because the project has nothing
|
|
14
|
+
* for it to run; it is then listed in `failed` too.
|
|
15
|
+
*/
|
|
16
|
+
export function judgeCheck(results, options = {}) {
|
|
17
|
+
const failedResults = results.filter((result) => result.status === 'fail');
|
|
18
|
+
const gaps = options.failOnSkipped === true
|
|
19
|
+
? results.filter((result) => result.status === 'skipped' && result.skipKind !== 'config')
|
|
20
|
+
: [];
|
|
21
|
+
const verdict = {
|
|
22
|
+
passed: failedResults.length === 0 && gaps.length === 0,
|
|
23
|
+
failed: [...failedResults, ...gaps].map((result) => result.name),
|
|
24
|
+
skipped: results.filter((result) => result.status === 'skipped').map((result) => result.name),
|
|
25
|
+
steps: results.length,
|
|
26
|
+
};
|
|
27
|
+
if (failedResults.length > 0) {
|
|
28
|
+
verdict.reason = failedResults.every((result) => result.exitCode === LOCK_TIMEOUT_EXIT)
|
|
29
|
+
? 'lock-timeout'
|
|
30
|
+
: 'step-failures';
|
|
31
|
+
}
|
|
32
|
+
else if (gaps.length > 0) {
|
|
33
|
+
verdict.reason = 'skipped-steps';
|
|
34
|
+
}
|
|
35
|
+
return verdict;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The verdict line: `FIREFORGE-VERDICT: PASS steps=<n> skipped=<n>` or
|
|
39
|
+
* `FIREFORGE-VERDICT: FAIL reason=<reason> failed=<a,b> steps=<n> skipped=<n>`,
|
|
40
|
+
* with an additive `skipped-steps=<a,b>` naming the skipped steps when
|
|
41
|
+
* there are any.
|
|
42
|
+
*/
|
|
43
|
+
export function formatCheckVerdictLine(verdict) {
|
|
44
|
+
const skippedNames = verdict.skipped.length > 0 ? ` skipped-steps=${verdict.skipped.join(',')}` : '';
|
|
45
|
+
const counts = `steps=${verdict.steps} skipped=${verdict.skipped.length}${skippedNames}`;
|
|
46
|
+
if (verdict.passed)
|
|
47
|
+
return `FIREFORGE-VERDICT: PASS ${counts}`;
|
|
48
|
+
return `FIREFORGE-VERDICT: FAIL reason=${verdict.reason ?? 'step-failures'} failed=${verdict.failed.join(',')} ${counts}`;
|
|
49
|
+
}
|
|
50
|
+
function formatDuration(ms) {
|
|
51
|
+
return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
52
|
+
}
|
|
53
|
+
/** The per-step table: one aligned row per step, with its detail. */
|
|
54
|
+
export function formatCheckTable(results) {
|
|
55
|
+
const rows = results.map((result) => [
|
|
56
|
+
result.name,
|
|
57
|
+
result.status.toUpperCase(),
|
|
58
|
+
result.status === 'skipped' ? '-' : formatDuration(result.durationMs),
|
|
59
|
+
result.detail,
|
|
60
|
+
]);
|
|
61
|
+
const header = ['STEP', 'RESULT', 'TIME', 'DETAIL'];
|
|
62
|
+
const widths = header.map((title, column) => Math.max(title.length, ...rows.map((row) => row[column]?.length ?? 0)));
|
|
63
|
+
return [header, ...rows]
|
|
64
|
+
.map((row) => row
|
|
65
|
+
.map((cell, column) => column === row.length - 1 ? cell : cell.padEnd(widths[column] ?? 0))
|
|
66
|
+
.join(' ')
|
|
67
|
+
.trimEnd())
|
|
68
|
+
.join('\n');
|
|
69
|
+
}
|
|
70
|
+
/** Writes the verdict line as the run's last stdout write, and seals stdout. */
|
|
71
|
+
export function writeCheckVerdictLine(verdict) {
|
|
72
|
+
process.stdout.write(`${formatCheckVerdictLine(verdict)}\n`);
|
|
73
|
+
setStdoutSealed(true);
|
|
74
|
+
}
|
|
75
|
+
/** Writes the `--json` document (schemaVersion 1) and seals stdout. */
|
|
76
|
+
export function writeCheckJson(results, verdict) {
|
|
77
|
+
const payload = {
|
|
78
|
+
schemaVersion: 1,
|
|
79
|
+
verdict: verdict.passed ? 'pass' : 'fail',
|
|
80
|
+
...(verdict.reason !== undefined ? { reason: verdict.reason } : {}),
|
|
81
|
+
summary: {
|
|
82
|
+
steps: verdict.steps,
|
|
83
|
+
passed: results.filter((result) => result.status === 'pass').length,
|
|
84
|
+
failed: verdict.failed.length,
|
|
85
|
+
skipped: verdict.skipped.length,
|
|
86
|
+
},
|
|
87
|
+
steps: results,
|
|
88
|
+
};
|
|
89
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
90
|
+
setStdoutSealed(true);
|
|
91
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CheckStepPlan } from './check-steps.js';
|
|
2
|
+
/** What a step's table row and JSON entry report. */
|
|
3
|
+
export interface CheckStepResult {
|
|
4
|
+
name: string;
|
|
5
|
+
kind: 'builtin' | 'extra';
|
|
6
|
+
/** The command line, as the table prints it. */
|
|
7
|
+
command: string;
|
|
8
|
+
status: 'pass' | 'fail' | 'skipped';
|
|
9
|
+
/**
|
|
10
|
+
* For a skipped step: `config` when `check.skip` names it, `unavailable`
|
|
11
|
+
* when the project has nothing for it to run.
|
|
12
|
+
*/
|
|
13
|
+
skipKind?: 'config' | 'unavailable';
|
|
14
|
+
/** The child's exit code. Null when it never ran or was killed at its budget. */
|
|
15
|
+
exitCode: number | null;
|
|
16
|
+
durationMs: number;
|
|
17
|
+
/** One-line summary for the table. */
|
|
18
|
+
detail: string;
|
|
19
|
+
/** Set when the step was killed at its `timeout`. */
|
|
20
|
+
timedOut?: true;
|
|
21
|
+
/** The tail of the step's combined output, for a failed step. */
|
|
22
|
+
outputTail?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Runs every step and returns the results in plan order. A step starts
|
|
26
|
+
* once a slot is free and every step it runs `after` has finished
|
|
27
|
+
* (whatever its outcome: the constraint is ordering, not success).
|
|
28
|
+
*
|
|
29
|
+
* @param plans - The plan from `buildCheckPlan`, already filtered
|
|
30
|
+
* @param jobs - How many steps may run at once (at least 1)
|
|
31
|
+
* @param now - Clock, injectable for tests
|
|
32
|
+
*/
|
|
33
|
+
export declare function runCheckSteps(plans: readonly CheckStepPlan[], jobs: number, now?: () => number): Promise<CheckStepResult[]>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Runs the `fireforge check` plan: up to `jobs` steps at once, each as a
|
|
4
|
+
* child process in its own process group (so a timeout or a Ctrl+C takes
|
|
5
|
+
* the step's whole tree), honouring each step's `after` ordering.
|
|
6
|
+
*/
|
|
7
|
+
import { ExecTimeoutError } from '../errors/base.js';
|
|
8
|
+
import { toError } from '../utils/errors.js';
|
|
9
|
+
import { execStream } from '../utils/process.js';
|
|
10
|
+
/** Combined output kept per step: enough for a tail, bounded for a chatty audit. */
|
|
11
|
+
const COMBINED_OUTPUT_CAP = 256 * 1024;
|
|
12
|
+
/** Lines of output a failed step carries in its result. */
|
|
13
|
+
const OUTPUT_TAIL_LINES = 40;
|
|
14
|
+
function tailLines(text) {
|
|
15
|
+
const lines = text.trimEnd().split(/\r?\n/);
|
|
16
|
+
return lines.slice(-OUTPUT_TAIL_LINES).join('\n');
|
|
17
|
+
}
|
|
18
|
+
/** Runs one planned step to completion. Never throws. */
|
|
19
|
+
async function runStep(plan, now) {
|
|
20
|
+
const base = { name: plan.name, kind: plan.kind, command: plan.display };
|
|
21
|
+
if (plan.skipReason !== undefined) {
|
|
22
|
+
return {
|
|
23
|
+
...base,
|
|
24
|
+
status: 'skipped',
|
|
25
|
+
skipKind: plan.skipKind ?? 'unavailable',
|
|
26
|
+
exitCode: null,
|
|
27
|
+
durationMs: 0,
|
|
28
|
+
detail: plan.skipReason,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
let stdout = '';
|
|
32
|
+
let combined = '';
|
|
33
|
+
const append = (chunk) => {
|
|
34
|
+
combined += chunk;
|
|
35
|
+
if (combined.length > COMBINED_OUTPUT_CAP)
|
|
36
|
+
combined = combined.slice(-COMBINED_OUTPUT_CAP);
|
|
37
|
+
};
|
|
38
|
+
const started = now();
|
|
39
|
+
try {
|
|
40
|
+
const exitCode = await execStream(plan.command, plan.args, {
|
|
41
|
+
cwd: plan.cwd,
|
|
42
|
+
processGroup: true,
|
|
43
|
+
...(plan.timeoutMs !== undefined ? { timeout: plan.timeoutMs } : {}),
|
|
44
|
+
...(plan.env !== undefined ? { env: plan.env } : {}),
|
|
45
|
+
onStdout: (chunk) => {
|
|
46
|
+
stdout += chunk;
|
|
47
|
+
append(chunk);
|
|
48
|
+
},
|
|
49
|
+
onStderr: append,
|
|
50
|
+
});
|
|
51
|
+
const status = exitCode === 0 ? 'pass' : 'fail';
|
|
52
|
+
return {
|
|
53
|
+
...base,
|
|
54
|
+
status,
|
|
55
|
+
exitCode,
|
|
56
|
+
durationMs: now() - started,
|
|
57
|
+
detail: plan.summarize({ stdout, combined, exitCode }),
|
|
58
|
+
...(status === 'fail' ? { outputTail: tailLines(combined) } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const timedOut = error instanceof ExecTimeoutError;
|
|
63
|
+
return {
|
|
64
|
+
...base,
|
|
65
|
+
status: 'fail',
|
|
66
|
+
exitCode: null,
|
|
67
|
+
durationMs: now() - started,
|
|
68
|
+
detail: timedOut
|
|
69
|
+
? `timed out after ${Math.round((plan.timeoutMs ?? 0) / 1000)}s`
|
|
70
|
+
: `could not start: ${toError(error).message}`,
|
|
71
|
+
...(timedOut ? { timedOut: true } : {}),
|
|
72
|
+
outputTail: tailLines(combined),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Runs every step and returns the results in plan order. A step starts
|
|
78
|
+
* once a slot is free and every step it runs `after` has finished
|
|
79
|
+
* (whatever its outcome: the constraint is ordering, not success).
|
|
80
|
+
*
|
|
81
|
+
* @param plans - The plan from `buildCheckPlan`, already filtered
|
|
82
|
+
* @param jobs - How many steps may run at once (at least 1)
|
|
83
|
+
* @param now - Clock, injectable for tests
|
|
84
|
+
*/
|
|
85
|
+
export async function runCheckSteps(plans, jobs, now = Date.now) {
|
|
86
|
+
const names = new Set(plans.map((plan) => plan.name));
|
|
87
|
+
const results = new Map();
|
|
88
|
+
const pending = [...plans];
|
|
89
|
+
const running = new Map();
|
|
90
|
+
const isReady = (plan) => plan.after.every((dependency) => !names.has(dependency) || results.has(dependency));
|
|
91
|
+
while (pending.length > 0 || running.size > 0) {
|
|
92
|
+
for (let index = 0; index < pending.length && running.size < Math.max(1, jobs);) {
|
|
93
|
+
const plan = pending[index];
|
|
94
|
+
if (plan === undefined || !isReady(plan)) {
|
|
95
|
+
index++;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
pending.splice(index, 1);
|
|
99
|
+
running.set(plan.name, runStep(plan, now).then((result) => {
|
|
100
|
+
results.set(plan.name, result);
|
|
101
|
+
running.delete(plan.name);
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
if (running.size === 0)
|
|
105
|
+
break;
|
|
106
|
+
await Promise.race(running.values());
|
|
107
|
+
}
|
|
108
|
+
return plans.flatMap((plan) => {
|
|
109
|
+
const result = results.get(plan.name);
|
|
110
|
+
return result === undefined ? [] : [result];
|
|
111
|
+
});
|
|
112
|
+
}
|