@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
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { PatchesManifest, PatchMetadata } from '../types/commands/index.js';
|
|
10
10
|
import type { FireForgeConfig, PatchPolicyConfig, PatchPolicyRange } from '../types/config.js';
|
|
11
11
|
/** Stable issue codes returned by patch policy evaluation. */
|
|
12
|
-
export type PatchPolicyIssueCode = 'filename-pattern' | 'filename-captures' | 'filename-metadata-mismatch' | 'order-collision' | 'category-range' | 'reserved-range' | 'reserved-documentation' | 'reserved-files' | 'description-required' | 'numeric-gap';
|
|
12
|
+
export type PatchPolicyIssueCode = 'filename-pattern' | 'filename-captures' | 'filename-metadata-mismatch' | 'order-collision' | 'category-range' | 'reserved-range' | 'reserved-documentation' | 'reserved-files' | 'description-required' | 'numeric-gap' | 'upstream-allowlist' | 'add-only' | 'scope' | 'reserved-document-missing';
|
|
13
13
|
/** A single patch policy validation finding. */
|
|
14
14
|
export interface PatchPolicyIssue {
|
|
15
15
|
code: PatchPolicyIssueCode;
|
|
@@ -30,6 +30,15 @@ export interface PatchPolicyEnforcementInput {
|
|
|
30
30
|
* `patch move-files --create`).
|
|
31
31
|
*/
|
|
32
32
|
hints?: Partial<Record<PatchPolicyIssueCode, string>>;
|
|
33
|
+
/**
|
|
34
|
+
* Patch filenames whose findings this mutation answers for. When set, only
|
|
35
|
+
* issues attributed to these rows are enforced: queue-level findings
|
|
36
|
+
* (order collisions, numeric gaps) and other patches' findings are left to
|
|
37
|
+
* `verify` and `lint --per-patch`. For mutations that change only the
|
|
38
|
+
* scoped rows' contents (`re-export`), so one patch's stale row cannot fail
|
|
39
|
+
* an unrelated patch refreshed in the same call.
|
|
40
|
+
*/
|
|
41
|
+
scope?: readonly string[];
|
|
33
42
|
}
|
|
34
43
|
/** Returns valid categories for prompts and CLI validation under the config. */
|
|
35
44
|
export declare function getPatchPolicyCategories(config: FireForgeConfig): string[];
|
|
@@ -49,8 +58,12 @@ export declare function categoryRangeLabel(ranges: readonly PatchPolicyRange[],
|
|
|
49
58
|
*/
|
|
50
59
|
export declare function categoryRangeForOrder(cfg: PatchPolicyConfig, category: string, order: number): PatchPolicyRange | null;
|
|
51
60
|
/** Evaluates an entire patch manifest against the configured policy. */
|
|
52
|
-
export declare function evaluatePatchPolicy(config: FireForgeConfig, manifest: PatchesManifest): PatchPolicyIssue[];
|
|
53
|
-
/**
|
|
61
|
+
export declare function evaluatePatchPolicy(config: Pick<FireForgeConfig, 'patchPolicy'>, manifest: PatchesManifest): PatchPolicyIssue[];
|
|
62
|
+
/**
|
|
63
|
+
* Builds a sorted manifest snapshot from projected patch metadata. Retired
|
|
64
|
+
* slots of `current` carry over unless a projected patch fills them, so a
|
|
65
|
+
* projection judges gaps the way the written manifest will.
|
|
66
|
+
*/
|
|
54
67
|
export declare function buildProjectedManifest(current: PatchesManifest | null, patches: PatchMetadata[]): PatchesManifest;
|
|
55
68
|
/** Applies a filename/order rename projection to a manifest without mutating it. */
|
|
56
69
|
export declare function applyRenameMapToManifest(manifest: PatchesManifest, renameMap: ReadonlyMap<string, {
|
|
@@ -59,5 +72,9 @@ export declare function applyRenameMapToManifest(manifest: PatchesManifest, rena
|
|
|
59
72
|
}>): PatchesManifest;
|
|
60
73
|
/** Enforces patch policy according to the configured mutation mode. */
|
|
61
74
|
export declare function enforcePatchPolicy(input: PatchPolicyEnforcementInput): void;
|
|
62
|
-
/**
|
|
63
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Allocates the next available order inside the configured ranges for a
|
|
77
|
+
* category. Orders in `reserved` (`patch reserve`) are skipped: they are
|
|
78
|
+
* held for their owner's export.
|
|
79
|
+
*/
|
|
80
|
+
export declare function allocatePolicyOrder(config: FireForgeConfig, patches: readonly PatchMetadata[], category: string, reserved?: ReadonlySet<number>): number | null;
|
|
@@ -12,6 +12,7 @@ import { warn } from '../utils/logger.js';
|
|
|
12
12
|
import { PATCH_CATEGORIES } from '../utils/validation.js';
|
|
13
13
|
import { rewriteStagedDependencyOwners } from './patch-manifest-io.js';
|
|
14
14
|
import { formatOrderRange, formatPatchOrder } from './patch-parse.js';
|
|
15
|
+
import { manifestWithRetiredSlots, reservedOrderSet, retiredOrderSet } from './patch-retired.js';
|
|
15
16
|
/** Default patch filename contract used when a policy omits `filenamePattern`. */
|
|
16
17
|
const DEFAULT_PATCH_POLICY_FILENAME_PATTERN = '^(?<order>\\d{3})-(?<category>[a-z][a-z0-9-]*)-(?<slug>[a-z0-9-]+)\\.patch$';
|
|
17
18
|
function policy(config) {
|
|
@@ -222,7 +223,7 @@ function evaluatePatchMetadata(cfg, patch, severity) {
|
|
|
222
223
|
}
|
|
223
224
|
return issues;
|
|
224
225
|
}
|
|
225
|
-
function evaluateGaps(cfg, patches, severity) {
|
|
226
|
+
function evaluateGaps(cfg, patches, severity, retiredOrders = new Set()) {
|
|
226
227
|
if (cfg.allowGaps !== false)
|
|
227
228
|
return [];
|
|
228
229
|
const issues = [];
|
|
@@ -243,7 +244,11 @@ function evaluateGaps(cfg, patches, severity) {
|
|
|
243
244
|
const last = occupied[occupied.length - 1];
|
|
244
245
|
const missing = [];
|
|
245
246
|
for (let order = first; order <= last; order++) {
|
|
246
|
-
|
|
247
|
+
// A retired slot (`patch retire`) or a reservation (`patch reserve`)
|
|
248
|
+
// is an occupied order with no patch behind it, so it is not a gap.
|
|
249
|
+
if (!occupiedSet.has(order) &&
|
|
250
|
+
!retiredOrders.has(order) &&
|
|
251
|
+
reservedRangeForOrder(cfg, order) === null) {
|
|
247
252
|
missing.push(order);
|
|
248
253
|
}
|
|
249
254
|
}
|
|
@@ -290,18 +295,22 @@ export function evaluatePatchPolicy(config, manifest) {
|
|
|
290
295
|
const severity = mutationMode(config) === 'warn' ? 'warning' : 'error';
|
|
291
296
|
const issues = manifest.patches.flatMap((patch) => evaluatePatchMetadata(cfg, patch, severity));
|
|
292
297
|
issues.push(...evaluateOrderCollisions(manifest.patches, severity));
|
|
293
|
-
|
|
298
|
+
// Retired and reserved slots are both spent orders with no patch behind
|
|
299
|
+
// them: neither is a gap.
|
|
300
|
+
const placeholders = new Set([...retiredOrderSet(manifest), ...reservedOrderSet(manifest)]);
|
|
301
|
+
issues.push(...evaluateGaps(cfg, manifest.patches, severity, placeholders));
|
|
294
302
|
return issues;
|
|
295
303
|
}
|
|
296
|
-
/**
|
|
304
|
+
/**
|
|
305
|
+
* Builds a sorted manifest snapshot from projected patch metadata. Retired
|
|
306
|
+
* slots of `current` carry over unless a projected patch fills them, so a
|
|
307
|
+
* projection judges gaps the way the written manifest will.
|
|
308
|
+
*/
|
|
297
309
|
export function buildProjectedManifest(current, patches) {
|
|
298
310
|
const next = patches
|
|
299
311
|
.map((patch) => ({ ...patch, filesAffected: [...patch.filesAffected] }))
|
|
300
312
|
.sort((a, b) => a.order - b.order || a.filename.localeCompare(b.filename));
|
|
301
|
-
return
|
|
302
|
-
version: current?.version ?? 1,
|
|
303
|
-
patches: next,
|
|
304
|
-
};
|
|
313
|
+
return manifestWithRetiredSlots(current, next);
|
|
305
314
|
}
|
|
306
315
|
/** Applies a filename/order rename projection to a manifest without mutating it. */
|
|
307
316
|
export function applyRenameMapToManifest(manifest, renameMap) {
|
|
@@ -325,7 +334,8 @@ export function applyRenameMapToManifest(manifest, renameMap) {
|
|
|
325
334
|
export function enforcePatchPolicy(input) {
|
|
326
335
|
if (policy(input.config) === undefined)
|
|
327
336
|
return;
|
|
328
|
-
const
|
|
337
|
+
const scope = input.scope === undefined ? undefined : new Set(input.scope);
|
|
338
|
+
const issues = evaluatePatchPolicy(input.config, input.manifest).filter((issue) => scope === undefined || scope.has(issue.filename));
|
|
329
339
|
if (issues.length === 0)
|
|
330
340
|
return;
|
|
331
341
|
const mode = mutationMode(input.config);
|
|
@@ -350,8 +360,12 @@ export function enforcePatchPolicy(input) {
|
|
|
350
360
|
: '';
|
|
351
361
|
throw new InvalidArgumentError(`${input.command} would violate patchPolicy:\n${details.join('\n')}${suffix}`, mode === 'force' ? '--force-unsafe' : 'patchPolicy');
|
|
352
362
|
}
|
|
353
|
-
/**
|
|
354
|
-
|
|
363
|
+
/**
|
|
364
|
+
* Allocates the next available order inside the configured ranges for a
|
|
365
|
+
* category. Orders in `reserved` (`patch reserve`) are skipped: they are
|
|
366
|
+
* held for their owner's export.
|
|
367
|
+
*/
|
|
368
|
+
export function allocatePolicyOrder(config, patches, category, reserved = new Set()) {
|
|
355
369
|
const cfg = policy(config);
|
|
356
370
|
if (!cfg)
|
|
357
371
|
return null;
|
|
@@ -360,7 +374,7 @@ export function allocatePolicyOrder(config, patches, category) {
|
|
|
360
374
|
.sort((a, b) => a.from - b.from || a.to - b.to);
|
|
361
375
|
if (ranges.length === 0)
|
|
362
376
|
return null;
|
|
363
|
-
const occupied = new Set(patches.map((patch) => patch.order));
|
|
377
|
+
const occupied = new Set([...patches.map((patch) => patch.order), ...reserved]);
|
|
364
378
|
const highestInRanges = patches.reduce((highest, patch) => {
|
|
365
379
|
if (!ranges.some((range) => patch.order >= range.from && patch.order <= range.to)) {
|
|
366
380
|
return highest;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retired order slots: the placeholders `patch retire` leaves in
|
|
3
|
+
* `patches.json#retired` so a patch can leave the queue without opening a
|
|
4
|
+
* numeric gap or renumbering its successors.
|
|
5
|
+
*
|
|
6
|
+
* A slot is not a patch. It has no `.patch` file and no row in `patches`,
|
|
7
|
+
* so everything that applies, exports, lints or rebases the queue walks past
|
|
8
|
+
* it without knowing it exists. The one reader is the
|
|
9
|
+
* `patchPolicy.allowGaps: false` gap check, which counts a retired order as
|
|
10
|
+
* occupied. A patch that later takes the same order fills the slot, and
|
|
11
|
+
* {@link carryRetiredSlots} drops it on that write, so the manifest never
|
|
12
|
+
* holds a slot and a patch at one order.
|
|
13
|
+
*
|
|
14
|
+
* Reserved slots (`patch reserve`) follow the same rules: a placeholder for
|
|
15
|
+
* an order a session has claimed but not exported yet, counted as occupied
|
|
16
|
+
* by the gap check and filled (and dropped) by the export that lands on it.
|
|
17
|
+
*/
|
|
18
|
+
import type { PatchesManifest, PatchMetadata, PatchReservedSlot, PatchRetiredSlot } from '../types/commands/index.js';
|
|
19
|
+
/**
|
|
20
|
+
* The retired slots of `current` that no patch in `patches` occupies, or
|
|
21
|
+
* undefined when none remain. Every manifest write that rebuilds the
|
|
22
|
+
* `patches` list routes the slots through here, so a slot survives the
|
|
23
|
+
* rewrite and a filled one does not.
|
|
24
|
+
*
|
|
25
|
+
* @param current - Manifest the slots come from (null or slot-free: none)
|
|
26
|
+
* @param patches - The rows the written manifest will hold
|
|
27
|
+
*/
|
|
28
|
+
export declare function carryRetiredSlots(current: Pick<PatchesManifest, 'retired'> | null | undefined, patches: readonly Pick<PatchMetadata, 'order'>[]): PatchRetiredSlot[] | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Reserved twin of {@link carryRetiredSlots}: the reservations of `current`
|
|
31
|
+
* that no patch in `patches` has filled yet.
|
|
32
|
+
*
|
|
33
|
+
* @param current - Manifest the slots come from
|
|
34
|
+
* @param patches - The rows the written manifest will hold
|
|
35
|
+
*/
|
|
36
|
+
export declare function carryReservedSlots(current: Pick<PatchesManifest, 'reserved'> | null | undefined, patches: readonly Pick<PatchMetadata, 'order'>[]): PatchReservedSlot[] | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Builds a manifest from `patches` that keeps `current`'s unfilled retired
|
|
39
|
+
* and reserved slots. Each key is omitted entirely when no slot remains, so
|
|
40
|
+
* a queue that never retired or reserved anything writes the same JSON as
|
|
41
|
+
* before.
|
|
42
|
+
*
|
|
43
|
+
* @param current - Manifest the slots come from
|
|
44
|
+
* @param patches - The rows the written manifest will hold
|
|
45
|
+
*/
|
|
46
|
+
export declare function manifestWithRetiredSlots(current: Pick<PatchesManifest, 'retired' | 'reserved'> | null | undefined, patches: PatchMetadata[]): PatchesManifest;
|
|
47
|
+
/** The set of order numbers held by retired slots. */
|
|
48
|
+
export declare function retiredOrderSet(manifest: Pick<PatchesManifest, 'retired'>): Set<number>;
|
|
49
|
+
/** The set of order numbers held by reserved slots. */
|
|
50
|
+
export declare function reservedOrderSet(manifest: Pick<PatchesManifest, 'reserved'>): Set<number>;
|
|
51
|
+
/**
|
|
52
|
+
* The reservation at `order`, if any.
|
|
53
|
+
*
|
|
54
|
+
* @param manifest - Manifest to search (null: none)
|
|
55
|
+
* @param order - Order number
|
|
56
|
+
*/
|
|
57
|
+
export declare function reservationAt(manifest: Pick<PatchesManifest, 'reserved'> | null | undefined, order: number): PatchReservedSlot | undefined;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Retired order slots: the placeholders `patch retire` leaves in
|
|
4
|
+
* `patches.json#retired` so a patch can leave the queue without opening a
|
|
5
|
+
* numeric gap or renumbering its successors.
|
|
6
|
+
*
|
|
7
|
+
* A slot is not a patch. It has no `.patch` file and no row in `patches`,
|
|
8
|
+
* so everything that applies, exports, lints or rebases the queue walks past
|
|
9
|
+
* it without knowing it exists. The one reader is the
|
|
10
|
+
* `patchPolicy.allowGaps: false` gap check, which counts a retired order as
|
|
11
|
+
* occupied. A patch that later takes the same order fills the slot, and
|
|
12
|
+
* {@link carryRetiredSlots} drops it on that write, so the manifest never
|
|
13
|
+
* holds a slot and a patch at one order.
|
|
14
|
+
*
|
|
15
|
+
* Reserved slots (`patch reserve`) follow the same rules: a placeholder for
|
|
16
|
+
* an order a session has claimed but not exported yet, counted as occupied
|
|
17
|
+
* by the gap check and filled (and dropped) by the export that lands on it.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The retired slots of `current` that no patch in `patches` occupies, or
|
|
21
|
+
* undefined when none remain. Every manifest write that rebuilds the
|
|
22
|
+
* `patches` list routes the slots through here, so a slot survives the
|
|
23
|
+
* rewrite and a filled one does not.
|
|
24
|
+
*
|
|
25
|
+
* @param current - Manifest the slots come from (null or slot-free: none)
|
|
26
|
+
* @param patches - The rows the written manifest will hold
|
|
27
|
+
*/
|
|
28
|
+
export function carryRetiredSlots(current, patches) {
|
|
29
|
+
const slots = current?.retired;
|
|
30
|
+
if (slots === undefined || slots.length === 0)
|
|
31
|
+
return undefined;
|
|
32
|
+
const occupied = new Set(patches.map((patch) => patch.order));
|
|
33
|
+
const kept = slots
|
|
34
|
+
.filter((slot) => !occupied.has(slot.order))
|
|
35
|
+
.map((slot) => ({ ...slot }))
|
|
36
|
+
.sort((a, b) => a.order - b.order);
|
|
37
|
+
return kept.length > 0 ? kept : undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Reserved twin of {@link carryRetiredSlots}: the reservations of `current`
|
|
41
|
+
* that no patch in `patches` has filled yet.
|
|
42
|
+
*
|
|
43
|
+
* @param current - Manifest the slots come from
|
|
44
|
+
* @param patches - The rows the written manifest will hold
|
|
45
|
+
*/
|
|
46
|
+
export function carryReservedSlots(current, patches) {
|
|
47
|
+
const slots = current?.reserved;
|
|
48
|
+
if (slots === undefined || slots.length === 0)
|
|
49
|
+
return undefined;
|
|
50
|
+
const occupied = new Set(patches.map((patch) => patch.order));
|
|
51
|
+
const kept = slots
|
|
52
|
+
.filter((slot) => !occupied.has(slot.order))
|
|
53
|
+
.map((slot) => ({ ...slot }))
|
|
54
|
+
.sort((a, b) => a.order - b.order);
|
|
55
|
+
return kept.length > 0 ? kept : undefined;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Builds a manifest from `patches` that keeps `current`'s unfilled retired
|
|
59
|
+
* and reserved slots. Each key is omitted entirely when no slot remains, so
|
|
60
|
+
* a queue that never retired or reserved anything writes the same JSON as
|
|
61
|
+
* before.
|
|
62
|
+
*
|
|
63
|
+
* @param current - Manifest the slots come from
|
|
64
|
+
* @param patches - The rows the written manifest will hold
|
|
65
|
+
*/
|
|
66
|
+
export function manifestWithRetiredSlots(current, patches) {
|
|
67
|
+
const retired = carryRetiredSlots(current, patches);
|
|
68
|
+
const reserved = carryReservedSlots(current, patches);
|
|
69
|
+
return {
|
|
70
|
+
version: 1,
|
|
71
|
+
patches,
|
|
72
|
+
...(retired !== undefined ? { retired } : {}),
|
|
73
|
+
...(reserved !== undefined ? { reserved } : {}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** The set of order numbers held by retired slots. */
|
|
77
|
+
export function retiredOrderSet(manifest) {
|
|
78
|
+
return new Set((manifest.retired ?? []).map((slot) => slot.order));
|
|
79
|
+
}
|
|
80
|
+
/** The set of order numbers held by reserved slots. */
|
|
81
|
+
export function reservedOrderSet(manifest) {
|
|
82
|
+
return new Set((manifest.reserved ?? []).map((slot) => slot.order));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The reservation at `order`, if any.
|
|
86
|
+
*
|
|
87
|
+
* @param manifest - Manifest to search (null: none)
|
|
88
|
+
* @param order - Order number
|
|
89
|
+
*/
|
|
90
|
+
export function reservationAt(manifest, order) {
|
|
91
|
+
return manifest?.reserved?.find((slot) => slot.order === order);
|
|
92
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PatchesManifest, PatchMetadata } from '../types/commands/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a `--patches <name…>` subset against the manifest, matching each
|
|
4
|
+
* requested name tolerantly (exact filename, filename ± `.patch`, the bare
|
|
5
|
+
* order number, the manifest `name` field, or the slug). Accepts repeated
|
|
6
|
+
* values and comma lists. Throws listing the available patches when a
|
|
7
|
+
* requested name matches none, so a typo fails loud rather than silently
|
|
8
|
+
* judging nothing.
|
|
9
|
+
*
|
|
10
|
+
* @param manifest - The loaded patch manifest
|
|
11
|
+
* @param requested - Raw `--patches` values (each may be a comma list)
|
|
12
|
+
* @param refusalHint - Sentence inserted into the no-match refusal, naming
|
|
13
|
+
* what the caller's flag selects
|
|
14
|
+
* @returns The matching manifest rows, de-duplicated, in request order
|
|
15
|
+
*/
|
|
16
|
+
export declare function selectPatchSubset(manifest: PatchesManifest, requested: readonly string[], refusalHint?: string): PatchMetadata[];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* The `--patches <name…>` subset resolver shared by `lint --per-patch`,
|
|
4
|
+
* `status` and `verify`. One matcher, so a name that selects a patch in
|
|
5
|
+
* one gate selects the same patch in the others.
|
|
6
|
+
*/
|
|
7
|
+
import { GeneralError } from '../errors/base.js';
|
|
8
|
+
/** Every spelling an operator may use for one patch. */
|
|
9
|
+
function patchAliases(p) {
|
|
10
|
+
const aliases = new Set([p.filename, p.filename.replace(/\.patch$/, ''), p.name]);
|
|
11
|
+
// The bare order number, both as the manifest records it and as the
|
|
12
|
+
// filename spells it (`2` and `002`). The refusal message offers stems,
|
|
13
|
+
// and an operator reading `102-ui-canvas-tiles.patch` in a lint finding
|
|
14
|
+
// reaches for `102` first.
|
|
15
|
+
aliases.add(String(p.order));
|
|
16
|
+
const match = /^(\d+)-([a-z]+)-(.+)\.patch$/.exec(p.filename);
|
|
17
|
+
if (match?.[1])
|
|
18
|
+
aliases.add(match[1]);
|
|
19
|
+
if (match?.[2] && match[3]) {
|
|
20
|
+
aliases.add(`${match[2]}-${match[3]}`);
|
|
21
|
+
aliases.add(match[3]);
|
|
22
|
+
}
|
|
23
|
+
return aliases;
|
|
24
|
+
}
|
|
25
|
+
function matchesPatch(p, name) {
|
|
26
|
+
const aliases = patchAliases(p);
|
|
27
|
+
const stem = name.replace(/\.patch$/, '');
|
|
28
|
+
return aliases.has(name) || aliases.has(stem) || aliases.has(`${stem}.patch`);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolves a `--patches <name…>` subset against the manifest, matching each
|
|
32
|
+
* requested name tolerantly (exact filename, filename ± `.patch`, the bare
|
|
33
|
+
* order number, the manifest `name` field, or the slug). Accepts repeated
|
|
34
|
+
* values and comma lists. Throws listing the available patches when a
|
|
35
|
+
* requested name matches none, so a typo fails loud rather than silently
|
|
36
|
+
* judging nothing.
|
|
37
|
+
*
|
|
38
|
+
* @param manifest - The loaded patch manifest
|
|
39
|
+
* @param requested - Raw `--patches` values (each may be a comma list)
|
|
40
|
+
* @param refusalHint - Sentence inserted into the no-match refusal, naming
|
|
41
|
+
* what the caller's flag selects
|
|
42
|
+
* @returns The matching manifest rows, de-duplicated, in request order
|
|
43
|
+
*/
|
|
44
|
+
export function selectPatchSubset(manifest, requested, refusalHint = '') {
|
|
45
|
+
const normalizedRequests = requested
|
|
46
|
+
.flatMap((entry) => entry.split(','))
|
|
47
|
+
.map((entry) => entry.trim())
|
|
48
|
+
.filter((entry) => entry.length > 0);
|
|
49
|
+
const selected = [];
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
for (const name of normalizedRequests) {
|
|
52
|
+
const found = manifest.patches.filter((p) => matchesPatch(p, name));
|
|
53
|
+
if (found.length === 0) {
|
|
54
|
+
const available = manifest.patches.map((p) => p.filename).join(', ');
|
|
55
|
+
throw new GeneralError(`No patch in the queue matches "${name}".${refusalHint ? ` ${refusalHint}` : ''} ` +
|
|
56
|
+
`Available patches: ${available}`);
|
|
57
|
+
}
|
|
58
|
+
for (const p of found) {
|
|
59
|
+
if (!seen.has(p.filename)) {
|
|
60
|
+
seen.add(p.filename);
|
|
61
|
+
selected.push(p);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return selected;
|
|
66
|
+
}
|
|
@@ -1,2 +1,22 @@
|
|
|
1
1
|
/** Waits for a busy browser or port without changing or terminating its owner. */
|
|
2
2
|
export declare function waitForPreflight(action: () => Promise<void>, seconds?: number): Promise<void>;
|
|
3
|
+
/**
|
|
4
|
+
* Spends a `--wait-browser`/`--wait-port` budget BEFORE the engine session
|
|
5
|
+
* lock is taken, so a run queued behind a busy browser or port does not
|
|
6
|
+
* hold the lock (and every peer's build, test and re-export behind it)
|
|
7
|
+
* while it does nothing. The caller re-checks once under the lock.
|
|
8
|
+
*
|
|
9
|
+
* Never refuses on its own: when the budget runs out the run still joins
|
|
10
|
+
* the lock queue, and the single check under the lock decides. A peer's
|
|
11
|
+
* test run that holds the lock legitimately keeps its browser alive until
|
|
12
|
+
* it releases, and refusing here would turn that ordinary queue into a
|
|
13
|
+
* refusal the old in-lock wait never produced. Never terminates anything
|
|
14
|
+
* either: nothing is owned before the lock is won. A refusal whose note is
|
|
15
|
+
* in `clearNotes` (a stale browser the run is allowed to reap under the
|
|
16
|
+
* lock) counts as clear.
|
|
17
|
+
*
|
|
18
|
+
* @param action - The preflight probe, which throws `PreflightRefusalError` while busy
|
|
19
|
+
* @param seconds - The wait budget
|
|
20
|
+
* @param clearNotes - Refusal classes that do not need waiting out
|
|
21
|
+
*/
|
|
22
|
+
export declare function waitBeforeLock(action: () => Promise<void>, seconds: number | undefined, clearNotes?: readonly string[]): Promise<void>;
|
|
@@ -22,3 +22,41 @@ export async function waitForPreflight(action, seconds) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Spends a `--wait-browser`/`--wait-port` budget BEFORE the engine session
|
|
27
|
+
* lock is taken, so a run queued behind a busy browser or port does not
|
|
28
|
+
* hold the lock (and every peer's build, test and re-export behind it)
|
|
29
|
+
* while it does nothing. The caller re-checks once under the lock.
|
|
30
|
+
*
|
|
31
|
+
* Never refuses on its own: when the budget runs out the run still joins
|
|
32
|
+
* the lock queue, and the single check under the lock decides. A peer's
|
|
33
|
+
* test run that holds the lock legitimately keeps its browser alive until
|
|
34
|
+
* it releases, and refusing here would turn that ordinary queue into a
|
|
35
|
+
* refusal the old in-lock wait never produced. Never terminates anything
|
|
36
|
+
* either: nothing is owned before the lock is won. A refusal whose note is
|
|
37
|
+
* in `clearNotes` (a stale browser the run is allowed to reap under the
|
|
38
|
+
* lock) counts as clear.
|
|
39
|
+
*
|
|
40
|
+
* @param action - The preflight probe, which throws `PreflightRefusalError` while busy
|
|
41
|
+
* @param seconds - The wait budget
|
|
42
|
+
* @param clearNotes - Refusal classes that do not need waiting out
|
|
43
|
+
*/
|
|
44
|
+
export async function waitBeforeLock(action, seconds, clearNotes = []) {
|
|
45
|
+
try {
|
|
46
|
+
await waitForPreflight(async () => {
|
|
47
|
+
try {
|
|
48
|
+
await action();
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof PreflightRefusalError && clearNotes.includes(error.note))
|
|
52
|
+
return;
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}, seconds);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (!(error instanceof PreflightRefusalError))
|
|
59
|
+
throw error;
|
|
60
|
+
info(`Still busy after the ${seconds ?? 0}-second wait; joining the lock queue and re-checking once under the lock.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the "from" side of a `fireforge rebase` source move.
|
|
3
|
+
*
|
|
4
|
+
* The rebase target is the working `firefox.version` pin, which
|
|
5
|
+
* `fireforge source set` has already moved. The tree the queue was last
|
|
6
|
+
* applied to is the previous pin: the `firefox` block of `fireforge.json`
|
|
7
|
+
* as committed in the project repository. Patch `sourceVersion` stamps are
|
|
8
|
+
* a poor stand-in: patches untouched since an older hop keep their older
|
|
9
|
+
* stamp, so the oldest stamp can trail the real previous tree by several
|
|
10
|
+
* releases. The stamps are only the fallback, for a project that is not a
|
|
11
|
+
* git checkout or whose committed pin already equals the target.
|
|
12
|
+
*/
|
|
13
|
+
import type { FirefoxProduct } from '../types/config.js';
|
|
14
|
+
/** A `firefox.version` / `firefox.product` pair. */
|
|
15
|
+
export interface FirefoxPin {
|
|
16
|
+
version: string;
|
|
17
|
+
product?: FirefoxProduct;
|
|
18
|
+
}
|
|
19
|
+
/** Where the rebase's "from" label came from. */
|
|
20
|
+
export interface RebaseFromVersion extends FirefoxPin {
|
|
21
|
+
/** `committed-pin`: the project's committed `fireforge.json`; `patch-stamps`: the oldest stamp. */
|
|
22
|
+
origin: 'committed-pin' | 'patch-stamps';
|
|
23
|
+
}
|
|
24
|
+
/** Tally of the queue's `sourceVersion` stamps, oldest first. */
|
|
25
|
+
export interface PatchStampSpread {
|
|
26
|
+
versions: string[];
|
|
27
|
+
counts: Map<string, number>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Reads the `firefox` pin from `fireforge.json` as committed at the project
|
|
31
|
+
* repository's HEAD. `HEAD:./fireforge.json` resolves relative to the
|
|
32
|
+
* project root, so a project nested inside a larger repository works too.
|
|
33
|
+
* Returns `undefined` when there is no repository, no commit, no committed
|
|
34
|
+
* config, or no valid pin in it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function readCommittedFirefoxPin(projectRoot: string): Promise<FirefoxPin | undefined>;
|
|
37
|
+
/** Counts the queue's stamps and orders them semantically (a lexical sort put 153.10.0esr before 153.2.0esr). */
|
|
38
|
+
export declare function tallyPatchStamps(stamps: readonly string[]): PatchStampSpread;
|
|
39
|
+
/** `"317 at 153.0esr, 5 at 153.3.0esr"`. */
|
|
40
|
+
export declare function formatPatchStampSpread(spread: PatchStampSpread): string;
|
|
41
|
+
/**
|
|
42
|
+
* Picks the rebase's "from": the committed pin when it differs from the
|
|
43
|
+
* target, otherwise the oldest patch stamp (`oldestStampProduct` is that
|
|
44
|
+
* stamp's recorded product, when any patch carries one).
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveRebaseFromVersion(target: string, committed: FirefoxPin | undefined, spread: PatchStampSpread, oldestStampProduct: FirefoxProduct | undefined): RebaseFromVersion;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Resolves the "from" side of a `fireforge rebase` source move.
|
|
4
|
+
*
|
|
5
|
+
* The rebase target is the working `firefox.version` pin, which
|
|
6
|
+
* `fireforge source set` has already moved. The tree the queue was last
|
|
7
|
+
* applied to is the previous pin: the `firefox` block of `fireforge.json`
|
|
8
|
+
* as committed in the project repository. Patch `sourceVersion` stamps are
|
|
9
|
+
* a poor stand-in: patches untouched since an older hop keep their older
|
|
10
|
+
* stamp, so the oldest stamp can trail the real previous tree by several
|
|
11
|
+
* releases. The stamps are only the fallback, for a project that is not a
|
|
12
|
+
* git checkout or whose committed pin already equals the target.
|
|
13
|
+
*/
|
|
14
|
+
import { exec } from '../utils/process.js';
|
|
15
|
+
import { compareFirefoxVersions, isObject, isValidFirefoxProduct, isValidFirefoxVersion, } from '../utils/validation.js';
|
|
16
|
+
/**
|
|
17
|
+
* Reads the `firefox` pin from `fireforge.json` as committed at the project
|
|
18
|
+
* repository's HEAD. `HEAD:./fireforge.json` resolves relative to the
|
|
19
|
+
* project root, so a project nested inside a larger repository works too.
|
|
20
|
+
* Returns `undefined` when there is no repository, no commit, no committed
|
|
21
|
+
* config, or no valid pin in it.
|
|
22
|
+
*/
|
|
23
|
+
export async function readCommittedFirefoxPin(projectRoot) {
|
|
24
|
+
const result = await exec('git', ['show', 'HEAD:./fireforge.json'], { cwd: projectRoot }).catch(() => undefined);
|
|
25
|
+
if (result?.exitCode !== 0)
|
|
26
|
+
return undefined;
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
parsed = JSON.parse(result.stdout);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const firefox = isObject(parsed) ? parsed['firefox'] : undefined;
|
|
35
|
+
if (!isObject(firefox))
|
|
36
|
+
return undefined;
|
|
37
|
+
const version = firefox['version'];
|
|
38
|
+
if (typeof version !== 'string' || !isValidFirefoxVersion(version))
|
|
39
|
+
return undefined;
|
|
40
|
+
const product = firefox['product'];
|
|
41
|
+
return typeof product === 'string' && isValidFirefoxProduct(product)
|
|
42
|
+
? { version, product }
|
|
43
|
+
: { version };
|
|
44
|
+
}
|
|
45
|
+
/** Counts the queue's stamps and orders them semantically (a lexical sort put 153.10.0esr before 153.2.0esr). */
|
|
46
|
+
export function tallyPatchStamps(stamps) {
|
|
47
|
+
const counts = new Map();
|
|
48
|
+
for (const stamp of stamps)
|
|
49
|
+
counts.set(stamp, (counts.get(stamp) ?? 0) + 1);
|
|
50
|
+
return { versions: [...counts.keys()].sort(compareFirefoxVersions), counts };
|
|
51
|
+
}
|
|
52
|
+
/** `"317 at 153.0esr, 5 at 153.3.0esr"`. */
|
|
53
|
+
export function formatPatchStampSpread(spread) {
|
|
54
|
+
return spread.versions.map((v) => `${spread.counts.get(v)} at ${v}`).join(', ');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Picks the rebase's "from": the committed pin when it differs from the
|
|
58
|
+
* target, otherwise the oldest patch stamp (`oldestStampProduct` is that
|
|
59
|
+
* stamp's recorded product, when any patch carries one).
|
|
60
|
+
*/
|
|
61
|
+
export function resolveRebaseFromVersion(target, committed, spread, oldestStampProduct) {
|
|
62
|
+
if (committed !== undefined && committed.version !== target) {
|
|
63
|
+
return { ...committed, origin: 'committed-pin' };
|
|
64
|
+
}
|
|
65
|
+
const oldest = spread.versions[0] ?? target;
|
|
66
|
+
return {
|
|
67
|
+
version: oldest,
|
|
68
|
+
...(oldestStampProduct !== undefined ? { product: oldestStampProduct } : {}),
|
|
69
|
+
origin: 'patch-stamps',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -43,8 +43,34 @@ export declare function getRunLogDir(projectRoot: string): string;
|
|
|
43
43
|
* @returns The sink, or undefined when no log could be opened
|
|
44
44
|
*/
|
|
45
45
|
export declare function openRunLog(projectRoot: string, command: string, now?: Date): Promise<RunLog | undefined>;
|
|
46
|
+
/**
|
|
47
|
+
* Tees a run log into a caller-chosen copy (`--keep-log`,
|
|
48
|
+
* {@link LOG_KEEP_ENV_VAR}) that retention never prunes.
|
|
49
|
+
*
|
|
50
|
+
* `.fireforge/logs/` keeps twenty logs per command kind for EVERY session
|
|
51
|
+
* on the checkout, so on a shared checkout a log named by a verdict line
|
|
52
|
+
* can be gone within the hour, and a failure recorded only by its log path
|
|
53
|
+
* cannot be read later. The copy is written live, line for line with the
|
|
54
|
+
* same redaction, rather than copied at the end: a killed run keeps what it
|
|
55
|
+
* had written, like the primary log does.
|
|
56
|
+
*
|
|
57
|
+
* The primary path stays the one `log=` names. When the primary could not
|
|
58
|
+
* be opened the copy stands in for it.
|
|
59
|
+
*
|
|
60
|
+
* @param log - The primary run log, if one opened
|
|
61
|
+
* @param flag - The `--keep-log` value, if any
|
|
62
|
+
* @param command - Command kind, naming a copy that has no primary
|
|
63
|
+
* @returns A sink writing to both, or whichever one opened
|
|
64
|
+
*/
|
|
65
|
+
export declare function keepRunLogCopy(log: RunLog | undefined, flag: string | undefined, command: string): Promise<RunLog | undefined>;
|
|
46
66
|
/** Installs (or clears, with `undefined`) the run log for this process. */
|
|
47
67
|
export declare function setActiveRunLog(log: RunLog | undefined): void;
|
|
68
|
+
/**
|
|
69
|
+
* The 1-based line of the active run log the next write starts on.
|
|
70
|
+
* Redaction rewrites within a line and never adds or drops one, so the
|
|
71
|
+
* number holds for the file on disk.
|
|
72
|
+
*/
|
|
73
|
+
export declare function activeRunLogLine(): number;
|
|
48
74
|
/** Absolute path of the active run log, or undefined when none is open. */
|
|
49
75
|
export declare function getActiveRunLogPath(): string | undefined;
|
|
50
76
|
/** Appends to the active run log. A no-op when none is open. */
|