@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
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `patch delete` and `patch retire` do to the files the removed patch
|
|
3
|
+
* claims, shared so both commands say and do the same.
|
|
4
|
+
*
|
|
5
|
+
* A file the patch modifies stays in the engine with its edits, unmanaged.
|
|
6
|
+
* A file the patch creates is different: once the patch is gone nothing
|
|
7
|
+
* owns it, so a created file still on disk turns into an unmanaged file the
|
|
8
|
+
* engine-clean gate reports. The summary names each such file (in the dry
|
|
9
|
+
* run and at the prompt), and `--delete-created` removes them as part of
|
|
10
|
+
* the confirmed operation.
|
|
11
|
+
*/
|
|
12
|
+
import type { PatchQueueContext } from '../../core/patch-lint.js';
|
|
13
|
+
import type { PatchMetadata } from '../../types/commands/index.js';
|
|
14
|
+
/** The summary lines for a removal's claimed files, and what to delete. */
|
|
15
|
+
export interface ClaimedFilePlan {
|
|
16
|
+
/** One summary line per claimed file, plus a header. */
|
|
17
|
+
summary: string[];
|
|
18
|
+
/** Created files still on disk that the removal will delete. */
|
|
19
|
+
deleteOnCommit: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Plans the claimed-file half of a removal.
|
|
23
|
+
*
|
|
24
|
+
* @param ctx - Queue context built before the removal
|
|
25
|
+
* @param target - The patch about to leave the queue
|
|
26
|
+
* @param engineDir - Engine directory
|
|
27
|
+
* @param deleteCreated - Whether `--delete-created` was passed
|
|
28
|
+
*/
|
|
29
|
+
export declare function planClaimedFiles(ctx: PatchQueueContext, target: PatchMetadata, engineDir: string, deleteCreated: boolean): Promise<ClaimedFilePlan>;
|
|
30
|
+
/**
|
|
31
|
+
* Deletes the created files a confirmed removal planned to delete.
|
|
32
|
+
*
|
|
33
|
+
* @param engineDir - Engine directory
|
|
34
|
+
* @param files - Engine-relative paths from {@link planClaimedFiles}
|
|
35
|
+
*/
|
|
36
|
+
export declare function deleteCreatedFiles(engineDir: string, files: readonly string[]): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Runs a removal's locked commit, inside the engine-session lock as well
|
|
39
|
+
* when it will delete engine files, so no engine-mutating peer runs while
|
|
40
|
+
* created files disappear under it.
|
|
41
|
+
*
|
|
42
|
+
* @param projectRoot - Project root directory
|
|
43
|
+
* @param command - Command name for lock attribution
|
|
44
|
+
* @param plan - The claimed-file plan
|
|
45
|
+
* @param waitLockSeconds - Wait budget for the engine lock
|
|
46
|
+
* @param operation - The patch-directory-locked commit
|
|
47
|
+
*/
|
|
48
|
+
export declare function withCreatedFileLock(projectRoot: string, command: string, plan: ClaimedFilePlan, waitLockSeconds: number | undefined, operation: () => Promise<void>): Promise<void>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* What `patch delete` and `patch retire` do to the files the removed patch
|
|
4
|
+
* claims, shared so both commands say and do the same.
|
|
5
|
+
*
|
|
6
|
+
* A file the patch modifies stays in the engine with its edits, unmanaged.
|
|
7
|
+
* A file the patch creates is different: once the patch is gone nothing
|
|
8
|
+
* owns it, so a created file still on disk turns into an unmanaged file the
|
|
9
|
+
* engine-clean gate reports. The summary names each such file (in the dry
|
|
10
|
+
* run and at the prompt), and `--delete-created` removes them as part of
|
|
11
|
+
* the confirmed operation.
|
|
12
|
+
*/
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { withEngineSessionLock } from '../../core/engine-session-lock.js';
|
|
15
|
+
import { pathExists, removeFile } from '../../utils/fs.js';
|
|
16
|
+
/**
|
|
17
|
+
* Plans the claimed-file half of a removal.
|
|
18
|
+
*
|
|
19
|
+
* @param ctx - Queue context built before the removal
|
|
20
|
+
* @param target - The patch about to leave the queue
|
|
21
|
+
* @param engineDir - Engine directory
|
|
22
|
+
* @param deleteCreated - Whether `--delete-created` was passed
|
|
23
|
+
*/
|
|
24
|
+
export async function planClaimedFiles(ctx, target, engineDir, deleteCreated) {
|
|
25
|
+
const created = ctx.entries.find((entry) => entry.filename === target.filename)?.createdFiles;
|
|
26
|
+
const summary = [`files currently claimed by this patch (${target.filesAffected.length}):`];
|
|
27
|
+
const deleteOnCommit = [];
|
|
28
|
+
let lingering = 0;
|
|
29
|
+
for (const file of target.filesAffected) {
|
|
30
|
+
if (created?.has(file) !== true) {
|
|
31
|
+
summary.push(` ${file} → will become unmanaged`);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (!(await pathExists(join(engineDir, file)))) {
|
|
35
|
+
summary.push(` ${file} → created by this patch; already gone`);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (deleteCreated) {
|
|
39
|
+
deleteOnCommit.push(file);
|
|
40
|
+
summary.push(` ${file} → created by this patch; will be deleted (--delete-created)`);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
lingering++;
|
|
44
|
+
summary.push(` ${file} → created by this patch and still on disk: will become unmanaged`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (lingering > 0) {
|
|
48
|
+
summary.push(`${lingering} created file(s) are still on disk. Delete them first, or pass ` +
|
|
49
|
+
'--delete-created to remove them with the patch; otherwise status reports them as unmanaged.');
|
|
50
|
+
}
|
|
51
|
+
return { summary, deleteOnCommit };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Deletes the created files a confirmed removal planned to delete.
|
|
55
|
+
*
|
|
56
|
+
* @param engineDir - Engine directory
|
|
57
|
+
* @param files - Engine-relative paths from {@link planClaimedFiles}
|
|
58
|
+
*/
|
|
59
|
+
export async function deleteCreatedFiles(engineDir, files) {
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
await removeFile(join(engineDir, file));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Runs a removal's locked commit, inside the engine-session lock as well
|
|
66
|
+
* when it will delete engine files, so no engine-mutating peer runs while
|
|
67
|
+
* created files disappear under it.
|
|
68
|
+
*
|
|
69
|
+
* @param projectRoot - Project root directory
|
|
70
|
+
* @param command - Command name for lock attribution
|
|
71
|
+
* @param plan - The claimed-file plan
|
|
72
|
+
* @param waitLockSeconds - Wait budget for the engine lock
|
|
73
|
+
* @param operation - The patch-directory-locked commit
|
|
74
|
+
*/
|
|
75
|
+
export async function withCreatedFileLock(projectRoot, command, plan, waitLockSeconds, operation) {
|
|
76
|
+
if (plan.deleteOnCommit.length === 0) {
|
|
77
|
+
await operation();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
await withEngineSessionLock(projectRoot, command, operation, {
|
|
81
|
+
...(waitLockSeconds !== undefined ? { waitLockSeconds } : {}),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dependency scan `patch delete` and `patch retire` share: which later
|
|
3
|
+
* patches import a file the removed patch creates, and which staged
|
|
4
|
+
* declarations name it as their owner. Both commands take the patch out of
|
|
5
|
+
* the queue, so both must refuse and warn on exactly the same evidence.
|
|
6
|
+
*/
|
|
7
|
+
import type { ConflictReport } from '../../core/destructive.js';
|
|
8
|
+
import { type PatchQueueContext } from '../../core/patch-lint.js';
|
|
9
|
+
import type { PatchMetadata } from '../../types/commands/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Lists every later patch site that imports a file `target` creates.
|
|
12
|
+
*
|
|
13
|
+
* Both source-site maps are covered: `newFiles` (files the later patch
|
|
14
|
+
* creates) and `modifiedFileAdditions` (added lines against files that
|
|
15
|
+
* already exist). Scanning only newFiles misses a later patch adding
|
|
16
|
+
* `import "./TargetHelper.sys.mjs"` to an existing file. Uses the shared
|
|
17
|
+
* specifier extractor so dynamic `import()` and
|
|
18
|
+
* `ChromeUtils.defineESModuleGetters` are picked up: the forward-import lint
|
|
19
|
+
* rule already covers those forms, and removal safety must match the same
|
|
20
|
+
* set or it silently drops dependencies.
|
|
21
|
+
*
|
|
22
|
+
* @param baseCtx - Queue context built before the removal
|
|
23
|
+
* @param target - The patch about to leave the queue
|
|
24
|
+
* @param command - Names for the "queue changed" refusal, e.g.
|
|
25
|
+
* `{ noun: 'deletion', verb: 'delete' }`
|
|
26
|
+
* @returns One description per dependent patch
|
|
27
|
+
*/
|
|
28
|
+
export declare function collectRemovalDependents(baseCtx: PatchQueueContext, target: PatchMetadata, command: {
|
|
29
|
+
noun: string;
|
|
30
|
+
verb: string;
|
|
31
|
+
}): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Warns once per patch whose staged forward-import names `target` as its
|
|
34
|
+
* owner. The dangling reference also surfaces via cross-patch lint later,
|
|
35
|
+
* but warning here puts the exact cleanup command in front of the operator
|
|
36
|
+
* at decision time.
|
|
37
|
+
*
|
|
38
|
+
* @param baseCtx - Queue context built before the removal
|
|
39
|
+
* @param target - The patch about to leave the queue
|
|
40
|
+
* @param verb - Command name used in the warning ("delete", "retire")
|
|
41
|
+
*/
|
|
42
|
+
export declare function warnDanglingStagedOwners(baseCtx: PatchQueueContext, target: PatchMetadata, verb: string): void;
|
|
43
|
+
/**
|
|
44
|
+
* The structural conflict a removal with dependents raises.
|
|
45
|
+
*
|
|
46
|
+
* The wording spells out the runtime impact: `git apply` does not resolve
|
|
47
|
+
* imports and succeeds even when a later patch imports a file the target
|
|
48
|
+
* created, so the queue re-imports cleanly. The breakage surfaces at
|
|
49
|
+
* browser startup when `ChromeUtils.importESModule` cannot locate the
|
|
50
|
+
* deleted module. Operators who plan to re-introduce the imported files
|
|
51
|
+
* (rename, refactor) need to know this is the impact model rather than a
|
|
52
|
+
* patch-application failure.
|
|
53
|
+
*
|
|
54
|
+
* @param dependents - Result of {@link collectRemovalDependents}
|
|
55
|
+
* @param target - The patch about to leave the queue
|
|
56
|
+
*/
|
|
57
|
+
export declare function removalConflict(dependents: readonly string[], target: PatchMetadata): ConflictReport | null;
|
|
58
|
+
/**
|
|
59
|
+
* Snapshot of the queue inputs a removal's dependency scan read, compared
|
|
60
|
+
* again under the lock so a change between the prompt and the commit
|
|
61
|
+
* refuses instead of committing an unreviewed removal.
|
|
62
|
+
*/
|
|
63
|
+
export declare function queueSnapshot(context: PatchQueueContext): string;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* The dependency scan `patch delete` and `patch retire` share: which later
|
|
4
|
+
* patches import a file the removed patch creates, and which staged
|
|
5
|
+
* declarations name it as their owner. Both commands take the patch out of
|
|
6
|
+
* the queue, so both must refuse and warn on exactly the same evidence.
|
|
7
|
+
*/
|
|
8
|
+
import { basename } from 'node:path';
|
|
9
|
+
import { extractImportSpecifiersWithLines, findForwardImportIgnoreLines, isForwardImportableFile, } from '../../core/patch-lint.js';
|
|
10
|
+
import { GeneralError } from '../../errors/base.js';
|
|
11
|
+
import { warn } from '../../utils/logger.js';
|
|
12
|
+
/**
|
|
13
|
+
* Lists every later patch site that imports a file `target` creates.
|
|
14
|
+
*
|
|
15
|
+
* Both source-site maps are covered: `newFiles` (files the later patch
|
|
16
|
+
* creates) and `modifiedFileAdditions` (added lines against files that
|
|
17
|
+
* already exist). Scanning only newFiles misses a later patch adding
|
|
18
|
+
* `import "./TargetHelper.sys.mjs"` to an existing file. Uses the shared
|
|
19
|
+
* specifier extractor so dynamic `import()` and
|
|
20
|
+
* `ChromeUtils.defineESModuleGetters` are picked up: the forward-import lint
|
|
21
|
+
* rule already covers those forms, and removal safety must match the same
|
|
22
|
+
* set or it silently drops dependencies.
|
|
23
|
+
*
|
|
24
|
+
* @param baseCtx - Queue context built before the removal
|
|
25
|
+
* @param target - The patch about to leave the queue
|
|
26
|
+
* @param command - Names for the "queue changed" refusal, e.g.
|
|
27
|
+
* `{ noun: 'deletion', verb: 'delete' }`
|
|
28
|
+
* @returns One description per dependent patch
|
|
29
|
+
*/
|
|
30
|
+
export function collectRemovalDependents(baseCtx, target, command) {
|
|
31
|
+
const targetEntry = baseCtx.entries.find((e) => e.filename === target.filename);
|
|
32
|
+
if (!targetEntry || JSON.stringify(targetEntry.metadata) !== JSON.stringify(target)) {
|
|
33
|
+
throw new GeneralError(`Patch queue changed while preparing ${command.noun}. Re-run patch ${command.verb} to review the current target.`);
|
|
34
|
+
}
|
|
35
|
+
const targetNewFileLeaves = new Set();
|
|
36
|
+
for (const fullPath of targetEntry.newFiles.keys()) {
|
|
37
|
+
targetNewFileLeaves.add(basename(fullPath));
|
|
38
|
+
}
|
|
39
|
+
const dependents = [];
|
|
40
|
+
const scanSite = (entryFilename, sitePath, content) => {
|
|
41
|
+
if (!isForwardImportableFile(sitePath))
|
|
42
|
+
return false;
|
|
43
|
+
const ignoredLines = findForwardImportIgnoreLines(content);
|
|
44
|
+
const specifiers = extractImportSpecifiersWithLines(content);
|
|
45
|
+
for (const { specifier, line } of specifiers) {
|
|
46
|
+
if (ignoredLines.has(line))
|
|
47
|
+
continue;
|
|
48
|
+
const cleaned = specifier.split(/[?#]/)[0] ?? specifier;
|
|
49
|
+
const leaf = basename(cleaned);
|
|
50
|
+
if (!leaf || !isForwardImportableFile(leaf))
|
|
51
|
+
continue;
|
|
52
|
+
if (targetNewFileLeaves.has(leaf)) {
|
|
53
|
+
dependents.push(`${entryFilename} (${sitePath}) imports "${specifier}" which would be deleted`);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
};
|
|
59
|
+
for (const entry of baseCtx.entries) {
|
|
60
|
+
if (entry.filename === target.filename)
|
|
61
|
+
continue;
|
|
62
|
+
if (entry.order < target.order)
|
|
63
|
+
continue;
|
|
64
|
+
let matched = false;
|
|
65
|
+
for (const [newFile, content] of entry.newFiles) {
|
|
66
|
+
if (scanSite(entry.filename, newFile, content)) {
|
|
67
|
+
matched = true;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (matched)
|
|
72
|
+
continue;
|
|
73
|
+
for (const [modifiedPath, addedContent] of entry.modifiedFileAdditions) {
|
|
74
|
+
if (scanSite(entry.filename, modifiedPath, addedContent))
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return dependents;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Warns once per patch whose staged forward-import names `target` as its
|
|
82
|
+
* owner. The dangling reference also surfaces via cross-patch lint later,
|
|
83
|
+
* but warning here puts the exact cleanup command in front of the operator
|
|
84
|
+
* at decision time.
|
|
85
|
+
*
|
|
86
|
+
* @param baseCtx - Queue context built before the removal
|
|
87
|
+
* @param target - The patch about to leave the queue
|
|
88
|
+
* @param verb - Command name used in the warning ("delete", "retire")
|
|
89
|
+
*/
|
|
90
|
+
export function warnDanglingStagedOwners(baseCtx, target, verb) {
|
|
91
|
+
const holders = baseCtx.entries.filter((entry) => entry.filename !== target.filename &&
|
|
92
|
+
(entry.metadata?.stagedDependencies?.forwardImports ?? []).some((fi) => fi.owner === target.filename));
|
|
93
|
+
for (const holder of holders) {
|
|
94
|
+
warn(`${holder.filename} declares a staged dependency with owner ${target.filename}; ` +
|
|
95
|
+
`after the ${verb}, update it via "fireforge patch staged-dependency ${holder.filename} --remove ..." ` +
|
|
96
|
+
'or re-point the owner at the patch that will create the file.');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The structural conflict a removal with dependents raises.
|
|
101
|
+
*
|
|
102
|
+
* The wording spells out the runtime impact: `git apply` does not resolve
|
|
103
|
+
* imports and succeeds even when a later patch imports a file the target
|
|
104
|
+
* created, so the queue re-imports cleanly. The breakage surfaces at
|
|
105
|
+
* browser startup when `ChromeUtils.importESModule` cannot locate the
|
|
106
|
+
* deleted module. Operators who plan to re-introduce the imported files
|
|
107
|
+
* (rename, refactor) need to know this is the impact model rather than a
|
|
108
|
+
* patch-application failure.
|
|
109
|
+
*
|
|
110
|
+
* @param dependents - Result of {@link collectRemovalDependents}
|
|
111
|
+
* @param target - The patch about to leave the queue
|
|
112
|
+
*/
|
|
113
|
+
export function removalConflict(dependents, target) {
|
|
114
|
+
if (dependents.length === 0)
|
|
115
|
+
return null;
|
|
116
|
+
return {
|
|
117
|
+
reason: `${dependents.length} later patch(es) contain import statements that reference files created by ${target.filename}. Patch application itself will still succeed, but runtime imports will fail at browser startup until those files are re-introduced.`,
|
|
118
|
+
details: [...dependents],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Snapshot of the queue inputs a removal's dependency scan read, compared
|
|
123
|
+
* again under the lock so a change between the prompt and the commit
|
|
124
|
+
* refuses instead of committing an unreviewed removal.
|
|
125
|
+
*/
|
|
126
|
+
export function queueSnapshot(context) {
|
|
127
|
+
return JSON.stringify(context.entries.map(({ filename, order, diff, metadata }) => ({
|
|
128
|
+
filename,
|
|
129
|
+
order,
|
|
130
|
+
diff,
|
|
131
|
+
metadata,
|
|
132
|
+
})));
|
|
133
|
+
}
|
|
@@ -26,6 +26,7 @@ import { buildPatchQueueContext, formatPatchLintIssue, lintPatchQueue, } from '.
|
|
|
26
26
|
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
27
27
|
import { loadPatchesManifest, rewriteStagedDependencyOwners, savePatchesManifest, } from '../../core/patch-manifest.js';
|
|
28
28
|
import { buildProjectedManifest, enforcePatchPolicy, getPatchPolicyCategories, isCategoryAllowedByConfig, } from '../../core/patch-policy.js';
|
|
29
|
+
import { carryReservedSlots, carryRetiredSlots } from '../../core/patch-retired.js';
|
|
29
30
|
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
30
31
|
import { toError } from '../../utils/errors.js';
|
|
31
32
|
import { pathExists } from '../../utils/fs.js';
|
|
@@ -115,6 +116,17 @@ async function commitRenameUnderLock(input) {
|
|
|
115
116
|
...(descriptionChanging ? { description: newDescription ?? '' } : {}),
|
|
116
117
|
};
|
|
117
118
|
}
|
|
119
|
+
// A rename onto a retired or reserved order fills that slot.
|
|
120
|
+
const retired = carryRetiredSlots(fresh, fresh.patches);
|
|
121
|
+
if (retired === undefined)
|
|
122
|
+
delete fresh.retired;
|
|
123
|
+
else
|
|
124
|
+
fresh.retired = retired;
|
|
125
|
+
const reserved = carryReservedSlots(fresh, fresh.patches);
|
|
126
|
+
if (reserved === undefined)
|
|
127
|
+
delete fresh.reserved;
|
|
128
|
+
else
|
|
129
|
+
fresh.reserved = reserved;
|
|
118
130
|
enforcePatchPolicy({
|
|
119
131
|
config: input.config,
|
|
120
132
|
manifest: buildProjectedManifest(fresh, fresh.patches),
|
|
@@ -299,11 +311,18 @@ export async function patchRenameCommand(projectRoot, identifier, options = {})
|
|
|
299
311
|
command: 'patch rename',
|
|
300
312
|
forceUnsafe: options.forceUnsafe === true,
|
|
301
313
|
});
|
|
314
|
+
// A description-only edit renames nothing and moves no order, so it needs
|
|
315
|
+
// no confirmation: scripts may change a description without --yes.
|
|
316
|
+
const descriptionOnly = descriptionChanging &&
|
|
317
|
+
!filenameChanging &&
|
|
318
|
+
!nameChanging &&
|
|
319
|
+
!orderChanging &&
|
|
320
|
+
!categoryChanging;
|
|
302
321
|
const decision = await confirmDestructive({
|
|
303
322
|
operation: 'patch-rename',
|
|
304
323
|
title: `Rename ${target.filename}`,
|
|
305
324
|
summary,
|
|
306
|
-
yes: options.yes === true,
|
|
325
|
+
yes: options.yes === true || descriptionOnly,
|
|
307
326
|
dryRun: isDryRun,
|
|
308
327
|
conflicts: null,
|
|
309
328
|
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fireforge patch reserve <order>`: holds an order number for a patch a
|
|
3
|
+
* session has agreed to export but has not exported yet.
|
|
4
|
+
*
|
|
5
|
+
* The case it exists for: under `patchPolicy.allowGaps: false`, a session
|
|
6
|
+
* cannot `export --order 571` while a peer has claimed 569 and 570 but not
|
|
7
|
+
* exported them, because the unfilled orders read as a gap. A reservation
|
|
8
|
+
* is a placeholder in `patches.json#reserved` that the gap check counts as
|
|
9
|
+
* occupied and the earliest-legal-order search counts as taken. The export
|
|
10
|
+
* that lands on the order fills it (see `core/patch-retired.ts`), and
|
|
11
|
+
* `--release` drops a reservation whose patch will not come.
|
|
12
|
+
*
|
|
13
|
+
* Metadata-only: no `.patch` file is written and no row is added, so
|
|
14
|
+
* apply, lint and rebase never see a reservation.
|
|
15
|
+
*/
|
|
16
|
+
import { Command } from 'commander';
|
|
17
|
+
import type { CommandContext } from '../../types/cli.js';
|
|
18
|
+
import type { PatchReserveOptions } from '../../types/commands/index.js';
|
|
19
|
+
/**
|
|
20
|
+
* Runs the `patch reserve` command.
|
|
21
|
+
*
|
|
22
|
+
* @param projectRoot - Project root directory
|
|
23
|
+
* @param order - Order number to reserve or release
|
|
24
|
+
* @param options - Command options
|
|
25
|
+
*/
|
|
26
|
+
export declare function patchReserveCommand(projectRoot: string, order: number, options?: PatchReserveOptions): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Registers the `patch reserve` subcommand on the `patch` parent.
|
|
29
|
+
*
|
|
30
|
+
* @param parent - Parent Commander command
|
|
31
|
+
* @param context - Shared CLI registration context
|
|
32
|
+
*/
|
|
33
|
+
export declare function registerPatchReserve(parent: Command, context: CommandContext): void;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `fireforge patch reserve <order>`: holds an order number for a patch a
|
|
4
|
+
* session has agreed to export but has not exported yet.
|
|
5
|
+
*
|
|
6
|
+
* The case it exists for: under `patchPolicy.allowGaps: false`, a session
|
|
7
|
+
* cannot `export --order 571` while a peer has claimed 569 and 570 but not
|
|
8
|
+
* exported them, because the unfilled orders read as a gap. A reservation
|
|
9
|
+
* is a placeholder in `patches.json#reserved` that the gap check counts as
|
|
10
|
+
* occupied and the earliest-legal-order search counts as taken. The export
|
|
11
|
+
* that lands on the order fills it (see `core/patch-retired.ts`), and
|
|
12
|
+
* `--release` drops a reservation whose patch will not come.
|
|
13
|
+
*
|
|
14
|
+
* Metadata-only: no `.patch` file is written and no row is added, so
|
|
15
|
+
* apply, lint and rebase never see a reservation.
|
|
16
|
+
*/
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import { getProjectPaths, loadConfig } from '../../core/config.js';
|
|
19
|
+
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
20
|
+
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
21
|
+
import { loadPatchesManifestForWrite, savePatchesManifest } from '../../core/patch-manifest.js';
|
|
22
|
+
import { formatPatchOrder } from '../../core/patch-parse.js';
|
|
23
|
+
import { categoryRangeForOrder, categoryRangeLabel, isCategoryAllowedByConfig, } from '../../core/patch-policy.js';
|
|
24
|
+
import { InvalidArgumentError } from '../../errors/base.js';
|
|
25
|
+
import { info, intro, outro } from '../../utils/logger.js';
|
|
26
|
+
import { addWaitLockOption, pickDefined, resolveWaitLockSeconds } from '../../utils/options.js';
|
|
27
|
+
import { parsePositiveIntegerFlag } from '../../utils/validation.js';
|
|
28
|
+
/**
|
|
29
|
+
* Refuses a reservation the manifest or the policy cannot hold: an order a
|
|
30
|
+
* patch, a retired slot or another reservation already takes, or one
|
|
31
|
+
* outside the category's configured ranges.
|
|
32
|
+
*/
|
|
33
|
+
function assertReservable(manifest, config, order, category) {
|
|
34
|
+
const label = formatPatchOrder(order);
|
|
35
|
+
const patch = manifest.patches.find((entry) => entry.order === order);
|
|
36
|
+
if (patch !== undefined) {
|
|
37
|
+
throw new InvalidArgumentError(`Order ${label} is taken by ${patch.filename}.`, '<order>');
|
|
38
|
+
}
|
|
39
|
+
if ((manifest.retired ?? []).some((slot) => slot.order === order)) {
|
|
40
|
+
throw new InvalidArgumentError(`Order ${label} is a retired slot; "fireforge export --order ${label}" fills it without a reservation.`, '<order>');
|
|
41
|
+
}
|
|
42
|
+
const held = manifest.reserved?.find((slot) => slot.order === order);
|
|
43
|
+
if (held !== undefined) {
|
|
44
|
+
throw new InvalidArgumentError(`Order ${label} is already reserved by ${held.owner} (since ${held.reservedAt}).`, '<order>');
|
|
45
|
+
}
|
|
46
|
+
const policy = config.patchPolicy;
|
|
47
|
+
if (policy !== undefined && categoryRangeForOrder(policy, category, order) === null) {
|
|
48
|
+
throw new InvalidArgumentError(`Order ${label} is outside the ${category} range (${categoryRangeLabel(policy.ranges, category)}).`, '<order>');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Runs the `patch reserve` command.
|
|
53
|
+
*
|
|
54
|
+
* @param projectRoot - Project root directory
|
|
55
|
+
* @param order - Order number to reserve or release
|
|
56
|
+
* @param options - Command options
|
|
57
|
+
*/
|
|
58
|
+
export async function patchReserveCommand(projectRoot, order, options = {}) {
|
|
59
|
+
const releasing = options.release === true;
|
|
60
|
+
const verb = releasing ? 'release' : 'reserve';
|
|
61
|
+
intro(options.dryRun ? `FireForge patch ${verb} (dry run)` : `FireForge patch ${verb}`);
|
|
62
|
+
const config = await loadConfig(projectRoot);
|
|
63
|
+
const label = formatPatchOrder(order);
|
|
64
|
+
let category;
|
|
65
|
+
let owner;
|
|
66
|
+
if (!releasing) {
|
|
67
|
+
category = options.category;
|
|
68
|
+
owner = options.owner?.trim();
|
|
69
|
+
if (category === undefined || owner === undefined || owner === '') {
|
|
70
|
+
throw new InvalidArgumentError('patch reserve requires --category and --owner (or --release to drop a reservation).', [category === undefined ? '--category' : '', owner ? '' : '--owner']
|
|
71
|
+
.filter(Boolean)
|
|
72
|
+
.join(', '));
|
|
73
|
+
}
|
|
74
|
+
if (!isCategoryAllowedByConfig(config, category)) {
|
|
75
|
+
throw new InvalidArgumentError(`Unknown patch category "${category}".`, '--category');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (options.category !== undefined || options.owner !== undefined) {
|
|
79
|
+
throw new InvalidArgumentError('--release takes only the order; --category and --owner apply when reserving.', '--release');
|
|
80
|
+
}
|
|
81
|
+
const { patches: patchesDir } = getProjectPaths(projectRoot);
|
|
82
|
+
await withPatchDirectoryLock(patchesDir, async () => {
|
|
83
|
+
const manifest = (await loadPatchesManifestForWrite(patchesDir)) ?? {
|
|
84
|
+
version: 1,
|
|
85
|
+
patches: [],
|
|
86
|
+
};
|
|
87
|
+
const reserved = manifest.reserved ?? [];
|
|
88
|
+
let slot;
|
|
89
|
+
let next;
|
|
90
|
+
if (releasing) {
|
|
91
|
+
const held = reserved.find((entry) => entry.order === order);
|
|
92
|
+
if (held === undefined) {
|
|
93
|
+
throw new InvalidArgumentError(`Order ${label} is not reserved.`, '<order>');
|
|
94
|
+
}
|
|
95
|
+
slot = held;
|
|
96
|
+
next = reserved.filter((entry) => entry.order !== order);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
assertReservable(manifest, config, order, category);
|
|
100
|
+
slot = {
|
|
101
|
+
order,
|
|
102
|
+
category: category,
|
|
103
|
+
owner: owner,
|
|
104
|
+
reservedAt: new Date().toISOString(),
|
|
105
|
+
...(options.reason !== undefined ? { reason: options.reason } : {}),
|
|
106
|
+
};
|
|
107
|
+
next = [...reserved, slot].sort((a, b) => a.order - b.order);
|
|
108
|
+
}
|
|
109
|
+
const summary = releasing
|
|
110
|
+
? `release order ${label} (reserved by ${slot.owner})`
|
|
111
|
+
: `reserve order ${label} for ${slot.owner} (category: ${slot.category})`;
|
|
112
|
+
if (options.dryRun === true) {
|
|
113
|
+
info(`[dry-run] ${summary}`);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (next.length > 0)
|
|
117
|
+
manifest.reserved = next;
|
|
118
|
+
else
|
|
119
|
+
delete manifest.reserved;
|
|
120
|
+
await savePatchesManifest(patchesDir, manifest);
|
|
121
|
+
await appendHistoryBestEffort(patchesDir, {
|
|
122
|
+
operation: releasing ? 'patch-reserve-release' : 'patch-reserve',
|
|
123
|
+
args: { ...slot },
|
|
124
|
+
result: 'ok',
|
|
125
|
+
}, `patch ${verb} committed (${label})`);
|
|
126
|
+
info(releasing
|
|
127
|
+
? `Released order ${label}.`
|
|
128
|
+
: `Reserved order ${label} for ${slot.owner}; "fireforge export --order ${label}" fills it.`);
|
|
129
|
+
}, { waitLockSeconds: resolveWaitLockSeconds(options.waitLock), command: `patch ${verb}` });
|
|
130
|
+
outro(options.dryRun ? 'Dry run complete — no changes made' : `Patch ${verb} complete`);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Registers the `patch reserve` subcommand on the `patch` parent.
|
|
134
|
+
*
|
|
135
|
+
* @param parent - Parent Commander command
|
|
136
|
+
* @param context - Shared CLI registration context
|
|
137
|
+
*/
|
|
138
|
+
export function registerPatchReserve(parent, context) {
|
|
139
|
+
const { getProjectRoot, withErrorHandling } = context;
|
|
140
|
+
const command = parent
|
|
141
|
+
.command('reserve <order>')
|
|
142
|
+
.description('Hold an order number for a patch not exported yet, so patchPolicy.allowGaps: false ' +
|
|
143
|
+
'sees no gap; the export that takes the order fills it (metadata only)')
|
|
144
|
+
.option('--category <category>', 'Category the reserved patch will carry')
|
|
145
|
+
.option('--owner <label>', 'Who holds the reservation (a session label or planned filename)')
|
|
146
|
+
.option('--reason <text>', 'Rationale recorded on the reserved slot')
|
|
147
|
+
.option('--release', 'Drop the reservation at <order> instead of creating one')
|
|
148
|
+
.option('--dry-run', 'Show what would change without writing');
|
|
149
|
+
addWaitLockOption(command).action(withErrorHandling(async (rawOrder, options) => {
|
|
150
|
+
await patchReserveCommand(getProjectRoot(), parsePositiveIntegerFlag('<order>', rawOrder), pickDefined(options));
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fireforge patch retire <name>`: removes a patch from the queue and keeps
|
|
3
|
+
* its order number spent as a retired slot in `patches.json#retired`.
|
|
4
|
+
*
|
|
5
|
+
* The case it exists for: a rename leaves a patch owning only files that no
|
|
6
|
+
* longer exist. `patch delete` opens a numeric gap that
|
|
7
|
+
* `patchPolicy.allowGaps: false` refuses, and `patch compact` renumbers
|
|
8
|
+
* every later patch, including ones a peer session has in flight. Retiring
|
|
9
|
+
* removes the patch file and row like a delete, and records the order as a
|
|
10
|
+
* placeholder the gap check counts as occupied. Nothing else reads the
|
|
11
|
+
* placeholder (see `core/patch-retired.ts`), and a later
|
|
12
|
+
* `export --order <n>` that takes the number fills it.
|
|
13
|
+
*
|
|
14
|
+
* Shares the delete's dependency refusal, dry-run, confirmation and history
|
|
15
|
+
* contract, so the two differ only in what happens to the order number.
|
|
16
|
+
*/
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import type { CommandContext } from '../../types/cli.js';
|
|
19
|
+
import type { PatchRetireOptions } from '../../types/commands/index.js';
|
|
20
|
+
/**
|
|
21
|
+
* Runs the `patch retire` command.
|
|
22
|
+
*
|
|
23
|
+
* @param projectRoot - Project root directory
|
|
24
|
+
* @param identifier - Patch filename, ordinal, or manifest name to retire
|
|
25
|
+
* @param options - Command options
|
|
26
|
+
*/
|
|
27
|
+
export declare function patchRetireCommand(projectRoot: string, identifier: string, options?: PatchRetireOptions): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Registers the `patch retire` subcommand on the `patch` parent.
|
|
30
|
+
*
|
|
31
|
+
* @param parent - Parent Commander command
|
|
32
|
+
* @param context - Shared CLI registration context
|
|
33
|
+
*/
|
|
34
|
+
export declare function registerPatchRetire(parent: Command, context: CommandContext): void;
|