@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
|
@@ -10,9 +10,10 @@ import { getStatusWithCodes, hasChanges } from '../core/git.js';
|
|
|
10
10
|
import { getAllDiff, getDiffForFilesAgainstHead } from '../core/git-diff.js';
|
|
11
11
|
import { expandUntrackedDirectoryEntries, getModifiedFilesInDir, getUntrackedFiles, getUntrackedFilesInDir, getWorkingTreeStatus, } from '../core/git-status.js';
|
|
12
12
|
import { extractAffectedFiles } from '../core/patch-apply.js';
|
|
13
|
-
import { buildPatchQueueContext,
|
|
13
|
+
import { buildPatchQueueContext, formatPatchLintIssue, lintExportedPatch, lintPatchQueue, lintPatchSize, measurePatchSize, } from '../core/patch-lint.js';
|
|
14
14
|
import { clearPerPatchLintCache } from '../core/patch-lint-cache.js';
|
|
15
15
|
import { collectDiffFilePaths, tagLintIssues } from '../core/patch-lint-diff-tag.js';
|
|
16
|
+
import { buildLintIgnoreChecks, filterLintIgnoredByOwners, partitionLintIgnoredIssues, } from '../core/patch-lint-ignore.js';
|
|
16
17
|
import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
17
18
|
import { pathExists } from '../utils/fs.js';
|
|
18
19
|
import { info, intro, outro, success, warn } from '../utils/logger.js';
|
|
@@ -186,25 +187,7 @@ function buildMaxWarningsMessage(count, maxWarnings, scope) {
|
|
|
186
187
|
* @returns Filtered issue list and the count of dropped findings.
|
|
187
188
|
*/
|
|
188
189
|
export function applyAggregateLintIgnoreSuppression(issues, ctx) {
|
|
189
|
-
const
|
|
190
|
-
for (const entry of ctx.entries) {
|
|
191
|
-
const ignoreList = entry.metadata?.lintIgnore;
|
|
192
|
-
if (!ignoreList || ignoreList.length === 0)
|
|
193
|
-
continue;
|
|
194
|
-
for (const f of entry.metadata?.filesAffected ?? []) {
|
|
195
|
-
let bucket = suppressionsByFile.get(f);
|
|
196
|
-
if (!bucket) {
|
|
197
|
-
bucket = new Set();
|
|
198
|
-
suppressionsByFile.set(f, bucket);
|
|
199
|
-
}
|
|
200
|
-
for (const id of ignoreList)
|
|
201
|
-
bucket.add(id);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (suppressionsByFile.size === 0) {
|
|
205
|
-
return { issues, dropped: 0 };
|
|
206
|
-
}
|
|
207
|
-
const filtered = issues.filter((issue) => !suppressionsByFile.get(issue.file)?.has(issue.check));
|
|
190
|
+
const filtered = filterLintIgnoredByOwners(issues, ctx.entries.map((entry) => entry.metadata));
|
|
208
191
|
return { issues: filtered, dropped: issues.length - filtered.length };
|
|
209
192
|
}
|
|
210
193
|
/**
|
|
@@ -283,7 +266,7 @@ function downgradeAggregateSizeRules(issues, files, ctx) {
|
|
|
283
266
|
* @param ctx - Patch queue context used to attribute file → owning patch
|
|
284
267
|
* @returns Size issues, each attributed to its owning patch by message prefix
|
|
285
268
|
*/
|
|
286
|
-
async function lintOwningPatchSizes(engineDir, filesAffected, ctx) {
|
|
269
|
+
async function lintOwningPatchSizes(engineDir, filesAffected, ctx, config) {
|
|
287
270
|
const listed = new Set(filesAffected);
|
|
288
271
|
const owners = new Map();
|
|
289
272
|
const ownedListed = new Set();
|
|
@@ -302,26 +285,24 @@ async function lintOwningPatchSizes(engineDir, filesAffected, ctx) {
|
|
|
302
285
|
owners.set(entry.filename, entry);
|
|
303
286
|
}
|
|
304
287
|
const issues = [];
|
|
305
|
-
const
|
|
288
|
+
const sizeForFiles = async (relPaths) => {
|
|
306
289
|
const existing = [];
|
|
307
290
|
for (const f of relPaths) {
|
|
308
291
|
if (await pathExists(join(engineDir, f)))
|
|
309
292
|
existing.push(f);
|
|
310
293
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return countNonBinaryDiffLines(diff).textLines;
|
|
294
|
+
const diff = existing.length > 0 ? await getDiffForFilesAgainstHead(engineDir, existing) : '';
|
|
295
|
+
// The file count stays the owner's whole `filesAffected`, as before.
|
|
296
|
+
return measurePatchSize(engineDir, diff, relPaths, config);
|
|
315
297
|
};
|
|
316
298
|
for (const entry of owners.values()) {
|
|
317
299
|
const md = entry.metadata;
|
|
318
300
|
if (!md)
|
|
319
301
|
continue;
|
|
320
|
-
const
|
|
321
|
-
const ignore =
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
continue;
|
|
302
|
+
const size = await sizeForFiles(md.filesAffected);
|
|
303
|
+
const ignore = buildLintIgnoreChecks(md.lintIgnore);
|
|
304
|
+
const sized = lintPatchSize(md.filesAffected, size, md.tier, config);
|
|
305
|
+
for (const issue of partitionLintIgnoredIssues(sized, ignore).kept) {
|
|
325
306
|
issues.push({ ...issue, message: `${entry.filename}: ${issue.message}` });
|
|
326
307
|
}
|
|
327
308
|
}
|
|
@@ -329,8 +310,8 @@ async function lintOwningPatchSizes(engineDir, filesAffected, ctx) {
|
|
|
329
310
|
// unit so a genuinely oversized fresh change still surfaces.
|
|
330
311
|
const unowned = filesAffected.filter((f) => !ownedListed.has(f));
|
|
331
312
|
if (unowned.length > 0) {
|
|
332
|
-
const
|
|
333
|
-
issues.push(...lintPatchSize(unowned,
|
|
313
|
+
const size = await sizeForFiles(unowned);
|
|
314
|
+
issues.push(...lintPatchSize(unowned, size, undefined, config));
|
|
334
315
|
}
|
|
335
316
|
return issues;
|
|
336
317
|
}
|
|
@@ -460,7 +441,7 @@ export async function lintCommand(projectRoot, files, options = {}) {
|
|
|
460
441
|
})),
|
|
461
442
|
];
|
|
462
443
|
if (files.length > 0 && ctx) {
|
|
463
|
-
issues.push(...(await lintOwningPatchSizes(paths.engine, filesAffected, ctx)));
|
|
444
|
+
issues.push(...(await lintOwningPatchSizes(paths.engine, filesAffected, ctx, config)));
|
|
464
445
|
}
|
|
465
446
|
// Cross-patch rules operate over the whole queue, so run them whenever a
|
|
466
447
|
// patches directory exists. They surface duplicate /dev/null creations
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { registerBootstrap } from './bootstrap.js';
|
|
2
2
|
import { registerBuild } from './build.js';
|
|
3
|
+
import { registerCheck } from './check.js';
|
|
3
4
|
import { registerConfig } from './config.js';
|
|
4
5
|
import { registerDiscard } from './discard.js';
|
|
5
6
|
import { registerDoctor } from './doctor.js';
|
|
@@ -10,6 +11,7 @@ import { registerFurnace } from './furnace/index.js';
|
|
|
10
11
|
import { registerImport } from './import.js';
|
|
11
12
|
import { registerLint } from './lint.js';
|
|
12
13
|
import { registerPackage } from './package.js';
|
|
14
|
+
import { registerPark } from './park.js';
|
|
13
15
|
import { registerPatch } from './patch/index.js';
|
|
14
16
|
import { registerReExport } from './re-export-register.js';
|
|
15
17
|
import { registerRebase } from './rebase/index.js';
|
|
@@ -46,6 +48,7 @@ export const COMMAND_MANIFEST = [
|
|
|
46
48
|
{ name: 'export', group: 'workflow', register: registerExport },
|
|
47
49
|
{ name: 'export-all', group: 'workflow', register: registerExportAll },
|
|
48
50
|
{ name: 're-export', group: 'workflow', register: registerReExport },
|
|
51
|
+
{ name: 'park', group: 'workflow', register: registerPark },
|
|
49
52
|
{ name: 'patch', group: 'workflow', register: registerPatch },
|
|
50
53
|
{ name: 'rebase', group: 'workflow', register: registerRebase },
|
|
51
54
|
{ name: 'package', group: 'workflow', register: registerPackage },
|
|
@@ -60,5 +63,6 @@ export const COMMAND_MANIFEST = [
|
|
|
60
63
|
{ name: 'lint', group: 'diagnostics', register: registerLint },
|
|
61
64
|
{ name: 'typecheck', group: 'diagnostics', register: registerTypecheck },
|
|
62
65
|
{ name: 'verify', group: 'diagnostics', register: registerVerify },
|
|
66
|
+
{ name: 'check', group: 'diagnostics', register: registerCheck },
|
|
63
67
|
{ name: 'furnace', group: 'components', register: registerFurnace },
|
|
64
68
|
];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { CommandContext } from '../types/cli.js';
|
|
3
|
+
/** Options for `fireforge park`. */
|
|
4
|
+
export interface ParkOptions {
|
|
5
|
+
label?: string;
|
|
6
|
+
hunks?: string;
|
|
7
|
+
restore?: string;
|
|
8
|
+
list?: boolean;
|
|
9
|
+
drop?: string;
|
|
10
|
+
dryRun?: boolean;
|
|
11
|
+
waitLock?: number | boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Runs `fireforge park`.
|
|
15
|
+
*
|
|
16
|
+
* @param projectRoot - Project root
|
|
17
|
+
* @param identifier - The patch to park (omitted for --restore/--list/--drop)
|
|
18
|
+
* @param options - See {@link ParkOptions}
|
|
19
|
+
*/
|
|
20
|
+
export declare function parkCommand(projectRoot: string, identifier: string | undefined, options: ParkOptions): Promise<void>;
|
|
21
|
+
/** Registers `fireforge park`. */
|
|
22
|
+
export declare function registerPark(program: Command, { getProjectRoot, withErrorHandling }: CommandContext): void;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `fireforge park`: set one session's edits in a patch's files aside and
|
|
4
|
+
* put them back after the owner commits.
|
|
5
|
+
*
|
|
6
|
+
* park <patch> [--label <l>] [--hunks <diff>] park and restore baseline
|
|
7
|
+
* park --restore <label> three-way merge them back
|
|
8
|
+
* park --list | park --drop <label>
|
|
9
|
+
*
|
|
10
|
+
* Without `--hunks` every drifted file of the patch goes back to its
|
|
11
|
+
* queue-applied baseline. With `--hunks` only the named hunks are taken out
|
|
12
|
+
* of the working tree. Either way the park warns when a hunk it takes out
|
|
13
|
+
* removes a Fluent message id, or a whole file, that files outside the
|
|
14
|
+
* patch still reference: a parked string still used by markup another
|
|
15
|
+
* patch owns makes `document.l10n.ready` reject in every new window.
|
|
16
|
+
*/
|
|
17
|
+
import { basename, join } from 'node:path';
|
|
18
|
+
import { getProjectPaths } from '../core/config.js';
|
|
19
|
+
import { withEngineSessionLock } from '../core/engine-session-lock.js';
|
|
20
|
+
import { appendHistoryBestEffort } from '../core/history-log.js';
|
|
21
|
+
import { dropPark, fluentIds, listParks, planRestore, readEngineFile, readPark, removedLines, writeEngineFile, writePark, } from '../core/park-store.js';
|
|
22
|
+
import { createPatchedContentContext } from '../core/patch-apply.js';
|
|
23
|
+
import { formatPatchNotFoundError } from '../core/patch-identifier-suggest.js';
|
|
24
|
+
import { loadPatchesManifest, resolvePatchIdentifier } from '../core/patch-manifest.js';
|
|
25
|
+
import { parseDiffSections } from '../core/patch-parse.js';
|
|
26
|
+
import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
27
|
+
import { pathExists, readText } from '../utils/fs.js';
|
|
28
|
+
import { info, intro, outro, success, warn } from '../utils/logger.js';
|
|
29
|
+
import { addWaitLockOption, resolveWaitLockSeconds } from '../utils/options.js';
|
|
30
|
+
import { applyHunksByContext } from './re-export-hunks.js';
|
|
31
|
+
import { normalizeEngineRelativeInput } from './re-export-scan.js';
|
|
32
|
+
/** The patch-applied baseline of each of `patch`'s files (null = absent). */
|
|
33
|
+
async function baselinesOf(paths, patch) {
|
|
34
|
+
const { computePatched } = await createPatchedContentContext(paths.patches, paths.engine);
|
|
35
|
+
const deleted = new Set(parseDiffSections(await readText(join(paths.patches, patch.filename)))
|
|
36
|
+
.filter((section) => section.isDeletedFile)
|
|
37
|
+
.map((section) => section.targetPath));
|
|
38
|
+
const baselines = new Map();
|
|
39
|
+
for (const file of patch.filesAffected) {
|
|
40
|
+
baselines.set(file, deleted.has(file) ? null : await computePatched(file));
|
|
41
|
+
}
|
|
42
|
+
return baselines;
|
|
43
|
+
}
|
|
44
|
+
/** The files a park would change, with both sides. */
|
|
45
|
+
async function planPark(paths, patch, hunksPath) {
|
|
46
|
+
if (hunksPath === undefined) {
|
|
47
|
+
const baselines = await baselinesOf(paths, patch);
|
|
48
|
+
const files = [];
|
|
49
|
+
for (const [path, after] of baselines) {
|
|
50
|
+
const before = await readEngineFile(paths.engine, path);
|
|
51
|
+
if (before !== after)
|
|
52
|
+
files.push({ path, before, after });
|
|
53
|
+
}
|
|
54
|
+
return files;
|
|
55
|
+
}
|
|
56
|
+
if (!(await pathExists(hunksPath))) {
|
|
57
|
+
throw new InvalidArgumentError(`--hunks ${hunksPath} does not exist.`, '--hunks');
|
|
58
|
+
}
|
|
59
|
+
const owned = new Set(patch.filesAffected);
|
|
60
|
+
const files = [];
|
|
61
|
+
for (const section of parseDiffSections(await readText(hunksPath))) {
|
|
62
|
+
const path = normalizeEngineRelativeInput(section.targetPath, '--hunks');
|
|
63
|
+
if (!owned.has(path)) {
|
|
64
|
+
throw new InvalidArgumentError(`--hunks names ${path}, which ${patch.filename} does not own.`, '--hunks');
|
|
65
|
+
}
|
|
66
|
+
const before = await readEngineFile(paths.engine, path);
|
|
67
|
+
const after = applyHunksByContext(before ?? '', section.hunks, true, path);
|
|
68
|
+
if (before !== after)
|
|
69
|
+
files.push({ path, before, after });
|
|
70
|
+
}
|
|
71
|
+
return files;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Referrers outside the patch of what the park takes away: Fluent ids its
|
|
75
|
+
* removed lines declare, and files it removes outright.
|
|
76
|
+
*/
|
|
77
|
+
async function findOutsideReferrers(paths, manifest, patch, files) {
|
|
78
|
+
const needles = new Map();
|
|
79
|
+
for (const file of files) {
|
|
80
|
+
if (file.path.endsWith('.ftl')) {
|
|
81
|
+
for (const id of fluentIds(removedLines(file.before, file.after))) {
|
|
82
|
+
needles.set(id, `Fluent id "${id}" (${file.path})`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (file.before !== null && file.after === null) {
|
|
86
|
+
needles.set(basename(file.path), `file ${file.path}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (needles.size === 0)
|
|
90
|
+
return [];
|
|
91
|
+
const own = new Set(patch.filesAffected);
|
|
92
|
+
const outside = [...new Set(manifest.patches.flatMap((entry) => entry.filesAffected))].filter((file) => !own.has(file));
|
|
93
|
+
const findings = [];
|
|
94
|
+
for (const file of outside) {
|
|
95
|
+
const content = await readEngineFile(paths.engine, file);
|
|
96
|
+
if (content === null)
|
|
97
|
+
continue;
|
|
98
|
+
for (const [needle, label] of needles) {
|
|
99
|
+
if (content.includes(needle))
|
|
100
|
+
findings.push(`${label} is still referenced by ${file}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return findings;
|
|
104
|
+
}
|
|
105
|
+
async function runPark(projectRoot, identifier, options) {
|
|
106
|
+
const paths = getProjectPaths(projectRoot);
|
|
107
|
+
const manifest = await loadPatchesManifest(paths.patches);
|
|
108
|
+
if (!manifest)
|
|
109
|
+
throw new GeneralError('No patches/patches.json; nothing to park against.');
|
|
110
|
+
const patch = resolvePatchIdentifier(identifier, manifest.patches);
|
|
111
|
+
if (!patch) {
|
|
112
|
+
throw new InvalidArgumentError(formatPatchNotFoundError(identifier, manifest.patches), 'park');
|
|
113
|
+
}
|
|
114
|
+
const files = await planPark(paths, patch, options.hunks);
|
|
115
|
+
if (files.length === 0) {
|
|
116
|
+
info(`${patch.filename}: nothing to park; its files match ${options.hunks ?? 'the baseline'}.`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const finding of await findOutsideReferrers(paths, manifest, patch, files)) {
|
|
120
|
+
warn(`Parking removes what is still in use: ${finding}. Move it somewhere you own first.`);
|
|
121
|
+
}
|
|
122
|
+
const label = options.label ?? `${patch.filename.replace(/\.patch$/, '')}-${Date.now().toString(36)}`;
|
|
123
|
+
for (const file of files)
|
|
124
|
+
info(` ${file.path}`);
|
|
125
|
+
if (options.dryRun === true) {
|
|
126
|
+
info(`[dry-run] would park ${files.length} file(s) of ${patch.filename} as "${label}".`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const dir = await writePark(projectRoot, {
|
|
130
|
+
label,
|
|
131
|
+
patch: patch.filename,
|
|
132
|
+
createdAt: new Date().toISOString(),
|
|
133
|
+
mode: options.hunks === undefined ? 'baseline' : 'hunks',
|
|
134
|
+
files,
|
|
135
|
+
});
|
|
136
|
+
for (const file of files)
|
|
137
|
+
await writeEngineFile(paths.engine, file.path, file.after);
|
|
138
|
+
await appendHistoryBestEffort(paths.patches, {
|
|
139
|
+
operation: 'park',
|
|
140
|
+
args: { label, patch: patch.filename, files: files.map((file) => file.path) },
|
|
141
|
+
result: 'ok',
|
|
142
|
+
}, `park ${label}`);
|
|
143
|
+
success(`Parked ${files.length} file(s) of ${patch.filename} as "${label}" (${dir}).`);
|
|
144
|
+
info(`Restore with: fireforge park --restore ${label}`);
|
|
145
|
+
}
|
|
146
|
+
async function runRestore(projectRoot, label, dryRun) {
|
|
147
|
+
const paths = getProjectPaths(projectRoot);
|
|
148
|
+
const park = await readPark(projectRoot, label);
|
|
149
|
+
const conflicts = [];
|
|
150
|
+
const plans = [];
|
|
151
|
+
for (const file of park.files) {
|
|
152
|
+
const now = await readEngineFile(paths.engine, file.path);
|
|
153
|
+
const plan = await planRestore(file, now);
|
|
154
|
+
plans.push({ file, ...plan });
|
|
155
|
+
if (plan.outcome === 'conflict')
|
|
156
|
+
conflicts.push(file.path);
|
|
157
|
+
}
|
|
158
|
+
for (const { file, outcome } of plans)
|
|
159
|
+
info(` ${file.path}: ${outcome}`);
|
|
160
|
+
if (dryRun) {
|
|
161
|
+
info(`[dry-run] restoring "${label}" would leave ${conflicts.length} conflict(s).`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
for (const { file, content, outcome } of plans) {
|
|
165
|
+
if (outcome !== 'unchanged')
|
|
166
|
+
await writeEngineFile(paths.engine, file.path, content);
|
|
167
|
+
}
|
|
168
|
+
await appendHistoryBestEffort(paths.patches, {
|
|
169
|
+
operation: 'park-restore',
|
|
170
|
+
args: { label, patch: park.patch, conflicts },
|
|
171
|
+
result: conflicts.length === 0 ? 'ok' : 'conflict',
|
|
172
|
+
}, `park --restore ${label}`);
|
|
173
|
+
if (conflicts.length > 0) {
|
|
174
|
+
throw new GeneralError(`Restoring "${label}" left conflict markers in ${conflicts.join(', ')}. Resolve them, ` +
|
|
175
|
+
`then drop the park with "fireforge park --drop ${label}".`);
|
|
176
|
+
}
|
|
177
|
+
await dropPark(projectRoot, label);
|
|
178
|
+
success(`Restored "${label}" into ${park.files.length} file(s) of ${park.patch}.`);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Runs `fireforge park`.
|
|
182
|
+
*
|
|
183
|
+
* @param projectRoot - Project root
|
|
184
|
+
* @param identifier - The patch to park (omitted for --restore/--list/--drop)
|
|
185
|
+
* @param options - See {@link ParkOptions}
|
|
186
|
+
*/
|
|
187
|
+
export async function parkCommand(projectRoot, identifier, options) {
|
|
188
|
+
intro('FireForge park');
|
|
189
|
+
const modes = [identifier !== undefined, options.restore, options.list, options.drop].filter((mode) => mode !== undefined && mode !== false);
|
|
190
|
+
if (modes.length !== 1) {
|
|
191
|
+
throw new InvalidArgumentError('Pass exactly one of: a patch to park, --restore <label>, --list or --drop <label>.', 'park');
|
|
192
|
+
}
|
|
193
|
+
if (options.list === true) {
|
|
194
|
+
const parks = await listParks(projectRoot);
|
|
195
|
+
if (parks.length === 0)
|
|
196
|
+
info('No parks.');
|
|
197
|
+
for (const park of parks) {
|
|
198
|
+
info(`${park.label} ${park.patch} ${park.files.length} file(s) ${park.createdAt}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else if (options.drop !== undefined) {
|
|
202
|
+
await dropPark(projectRoot, options.drop);
|
|
203
|
+
success(`Dropped park "${options.drop}".`);
|
|
204
|
+
}
|
|
205
|
+
else if (options.restore !== undefined) {
|
|
206
|
+
await runRestore(projectRoot, options.restore, options.dryRun === true);
|
|
207
|
+
}
|
|
208
|
+
else if (identifier !== undefined) {
|
|
209
|
+
await runPark(projectRoot, identifier, options);
|
|
210
|
+
}
|
|
211
|
+
outro('Park complete');
|
|
212
|
+
}
|
|
213
|
+
/** Registers `fireforge park`. */
|
|
214
|
+
export function registerPark(program, { getProjectRoot, withErrorHandling }) {
|
|
215
|
+
const command = program
|
|
216
|
+
.command('park [patch]')
|
|
217
|
+
.description("Set a session's edits in a patch's files aside (back to the queue-applied baseline, or minus the hunks a diff names) and three-way merge them back later")
|
|
218
|
+
.option('--label <label>', 'Name for the park (default: <patch>-<time>)')
|
|
219
|
+
.option('--hunks <diff>', 'Park only the hunks this diff names, not every drifted line')
|
|
220
|
+
.option('--restore <label>', 'Three-way merge a park back into the engine, then drop it')
|
|
221
|
+
.option('--list', 'List the parks')
|
|
222
|
+
.option('--drop <label>', 'Delete a park without restoring it')
|
|
223
|
+
.option('--dry-run', 'Show what would be parked or restored');
|
|
224
|
+
addWaitLockOption(command).action(withErrorHandling(async (patch, options) => {
|
|
225
|
+
const projectRoot = getProjectRoot();
|
|
226
|
+
if (options.list === true || options.drop !== undefined || options.dryRun === true) {
|
|
227
|
+
await parkCommand(projectRoot, patch, options);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
await withEngineSessionLock(projectRoot, 'park', () => parkCommand(projectRoot, patch, options), { waitLockSeconds: resolveWaitLockSeconds(options.waitLock) });
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
@@ -42,5 +42,7 @@ function formatGapNotice(targetFilename, message) {
|
|
|
42
42
|
const where = orders ? `a gap at ${orders}` : 'a numeric gap';
|
|
43
43
|
return (`deleting ${targetFilename} leaves ${range} with ${where}; the queue will fail ` +
|
|
44
44
|
'numeric-gap (patchPolicy.allowGaps is false) until ' +
|
|
45
|
-
`"fireforge export --order ${fill}" fills it or "fireforge patch compact" renumbers
|
|
45
|
+
`"fireforge export --order ${fill}" fills it or "fireforge patch compact" renumbers. ` +
|
|
46
|
+
`"fireforge patch retire ${targetFilename}" removes the patch and keeps its order as a ` +
|
|
47
|
+
'retired slot instead, so no gap opens.');
|
|
46
48
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Destructive: refuses when a later patch imports a module owned by the
|
|
5
5
|
* target (that would leave a dangling forward import), prompts for
|
|
6
6
|
* confirmation interactively, requires `--yes` for non-TTY, supports
|
|
7
|
-
* `--dry-run`, and appends to
|
|
7
|
+
* `--dry-run`, and appends to the patch history log on success.
|
|
8
8
|
*/
|
|
9
9
|
import { Command } from 'commander';
|
|
10
10
|
import type { CommandContext } from '../../types/cli.js';
|
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
* Destructive: refuses when a later patch imports a module owned by the
|
|
6
6
|
* target (that would leave a dangling forward import), prompts for
|
|
7
7
|
* confirmation interactively, requires `--yes` for non-TTY, supports
|
|
8
|
-
* `--dry-run`, and appends to
|
|
8
|
+
* `--dry-run`, and appends to the patch history log on success.
|
|
9
9
|
*/
|
|
10
|
-
import { basename } from 'node:path';
|
|
11
10
|
import { Command } from 'commander';
|
|
12
11
|
import { loadConfig } from '../../core/config.js';
|
|
13
12
|
import { confirmDestructive } from '../../core/destructive.js';
|
|
14
13
|
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
15
|
-
import { buildPatchQueueContext
|
|
14
|
+
import { buildPatchQueueContext } from '../../core/patch-lint.js';
|
|
16
15
|
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
17
16
|
import { removePatchFileAndManifest } from '../../core/patch-manifest.js';
|
|
18
17
|
import { GeneralError } from '../../errors/base.js';
|
|
@@ -21,6 +20,8 @@ import { addWaitLockOption, pickDefined, resolveWaitLockSeconds } from '../../ut
|
|
|
21
20
|
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
22
21
|
import { describeProjectedGaps } from './delete-gap-notice.js';
|
|
23
22
|
import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
|
|
23
|
+
import { deleteCreatedFiles, planClaimedFiles, withCreatedFileLock, } from './removal-created-files.js';
|
|
24
|
+
import { collectRemovalDependents, queueSnapshot, removalConflict, warnDanglingStagedOwners, } from './removal-dependents.js';
|
|
24
25
|
/**
|
|
25
26
|
* Runs the `patch delete` command: removes a patch file and its manifest
|
|
26
27
|
* row atomically, refusing when a later patch imports a leaf owned by the
|
|
@@ -37,81 +38,15 @@ export async function patchDeleteCommand(projectRoot, identifier, options = {})
|
|
|
37
38
|
});
|
|
38
39
|
const target = requirePatchTarget(identifier, manifest.patches);
|
|
39
40
|
// Build the full queue context once so we can scan each patch's newFiles
|
|
40
|
-
// without re-parsing for the dependency check below.
|
|
41
|
+
// without re-parsing for the dependency check below. Hard refusal: an
|
|
42
|
+
// import owned by a later patch that points at any of the target's
|
|
43
|
+
// newly-created files is a dependency on the target.
|
|
41
44
|
const baseCtx = await buildPatchQueueContext(paths.patches);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (!targetEntry || JSON.stringify(targetEntry.metadata) !== JSON.stringify(target)) {
|
|
48
|
-
throw new GeneralError('Patch queue changed while preparing deletion. Re-run patch delete to review the current target.');
|
|
49
|
-
}
|
|
50
|
-
const targetNewFileLeaves = new Set();
|
|
51
|
-
for (const fullPath of targetEntry.newFiles.keys()) {
|
|
52
|
-
targetNewFileLeaves.add(basename(fullPath));
|
|
53
|
-
}
|
|
54
|
-
// Scan every later patch's new files and its added lines on pre-existing
|
|
55
|
-
// files for import specifiers that resolve to a leaf owned by the target.
|
|
56
|
-
// Uses the shared specifier extractor so dynamic `import()` and
|
|
57
|
-
// `ChromeUtils.defineESModuleGetters` are picked up: the forward-import
|
|
58
|
-
// lint rule already covers those forms, and delete safety must match the
|
|
59
|
-
// same set or it silently drops dependencies.
|
|
60
|
-
//
|
|
61
|
-
// Both source-site maps are covered: `newFiles` (files the later patch
|
|
62
|
-
// creates) and `modifiedFileAdditions` (added lines against files that
|
|
63
|
-
// already exist). Scanning only newFiles misses a later patch adding
|
|
64
|
-
// `import "./TargetHelper.sys.mjs"` to an existing file.
|
|
65
|
-
const dependents = [];
|
|
66
|
-
const scanSite = (entryFilename, sitePath, content) => {
|
|
67
|
-
if (!isForwardImportableFile(sitePath))
|
|
68
|
-
return false;
|
|
69
|
-
const ignoredLines = findForwardImportIgnoreLines(content);
|
|
70
|
-
const specifiers = extractImportSpecifiersWithLines(content);
|
|
71
|
-
for (const { specifier, line } of specifiers) {
|
|
72
|
-
if (ignoredLines.has(line))
|
|
73
|
-
continue;
|
|
74
|
-
const cleaned = specifier.split(/[?#]/)[0] ?? specifier;
|
|
75
|
-
const leaf = basename(cleaned);
|
|
76
|
-
if (!leaf || !isForwardImportableFile(leaf))
|
|
77
|
-
continue;
|
|
78
|
-
if (targetNewFileLeaves.has(leaf)) {
|
|
79
|
-
dependents.push(`${entryFilename} (${sitePath}) imports "${specifier}" which would be deleted`);
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return false;
|
|
84
|
-
};
|
|
85
|
-
for (const entry of baseCtx.entries) {
|
|
86
|
-
if (entry.filename === target.filename)
|
|
87
|
-
continue;
|
|
88
|
-
if (entry.order < target.order)
|
|
89
|
-
continue;
|
|
90
|
-
let matched = false;
|
|
91
|
-
for (const [newFile, content] of entry.newFiles) {
|
|
92
|
-
if (scanSite(entry.filename, newFile, content)) {
|
|
93
|
-
matched = true;
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (matched)
|
|
98
|
-
continue;
|
|
99
|
-
for (const [modifiedPath, addedContent] of entry.modifiedFileAdditions) {
|
|
100
|
-
if (scanSite(entry.filename, modifiedPath, addedContent))
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
// Staged-dependency declarations on other patches may name the deleted
|
|
105
|
-
// patch as their forward-import owner. The dangling reference also
|
|
106
|
-
// surfaces via cross-patch lint later, but warning here puts the exact
|
|
107
|
-
// cleanup command in front of the operator at decision time.
|
|
108
|
-
const danglingOwnerHolders = baseCtx.entries.filter((entry) => entry.filename !== target.filename &&
|
|
109
|
-
(entry.metadata?.stagedDependencies?.forwardImports ?? []).some((fi) => fi.owner === target.filename));
|
|
110
|
-
for (const holder of danglingOwnerHolders) {
|
|
111
|
-
warn(`${holder.filename} declares a staged dependency with owner ${target.filename}; ` +
|
|
112
|
-
`after the delete, update it via "fireforge patch staged-dependency ${holder.filename} --remove ..." ` +
|
|
113
|
-
'or re-point the owner at the patch that will create the file.');
|
|
114
|
-
}
|
|
45
|
+
const dependents = collectRemovalDependents(baseCtx, target, {
|
|
46
|
+
noun: 'deletion',
|
|
47
|
+
verb: 'delete',
|
|
48
|
+
});
|
|
49
|
+
warnDanglingStagedOwners(baseCtx, target, 'delete');
|
|
115
50
|
// Under `patchPolicy.allowGaps: false` the removal can open a numeric gap
|
|
116
51
|
// that every later lint and re-export refuses on. Naming it here, in
|
|
117
52
|
// dry-run and at the prompt, puts the refill/compact choice in front of
|
|
@@ -120,28 +55,13 @@ export async function patchDeleteCommand(projectRoot, identifier, options = {})
|
|
|
120
55
|
for (const line of gapNotices) {
|
|
121
56
|
warn(line);
|
|
122
57
|
}
|
|
123
|
-
const conflicts = dependents
|
|
124
|
-
|
|
125
|
-
// The wording spells out the runtime impact: `git apply`
|
|
126
|
-
// does not resolve imports and succeeds even when a later patch
|
|
127
|
-
// imports a file the target created, so the queue re-imports
|
|
128
|
-
// cleanly. The breakage surfaces at browser startup when
|
|
129
|
-
// `ChromeUtils.importESModule` cannot locate the deleted module.
|
|
130
|
-
// Operators who plan to re-introduce the imported files (rename,
|
|
131
|
-
// refactor) need to know this is the impact model rather than a
|
|
132
|
-
// patch-application failure.
|
|
133
|
-
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.`,
|
|
134
|
-
details: dependents,
|
|
135
|
-
}
|
|
136
|
-
: null;
|
|
58
|
+
const conflicts = removalConflict(dependents, target);
|
|
59
|
+
const claimed = await planClaimedFiles(baseCtx, target, paths.engine, options.deleteCreated === true);
|
|
137
60
|
const summary = [
|
|
138
61
|
`delete ${target.filename} (category: ${target.category}, order: ${target.order})`,
|
|
139
62
|
`description: ${target.description || '(none)'}`,
|
|
140
|
-
|
|
63
|
+
...claimed.summary,
|
|
141
64
|
];
|
|
142
|
-
for (const file of target.filesAffected) {
|
|
143
|
-
summary.push(` ${file} → will become unmanaged`);
|
|
144
|
-
}
|
|
145
65
|
summary.push(...gapNotices);
|
|
146
66
|
const decision = await confirmDestructive({
|
|
147
67
|
operation: 'patch-delete',
|
|
@@ -162,32 +82,31 @@ export async function patchDeleteCommand(projectRoot, identifier, options = {})
|
|
|
162
82
|
// mutation with no audit trail. A history append failure is warned but not
|
|
163
83
|
// re-thrown: by that point the mutation has committed, and reporting
|
|
164
84
|
// failure to the caller would mislead.
|
|
165
|
-
|
|
85
|
+
const waitLockSeconds = resolveWaitLockSeconds(options.waitLock);
|
|
86
|
+
await withCreatedFileLock(projectRoot, 'patch delete', claimed, waitLockSeconds, () => withPatchDirectoryLock(paths.patches, async () => {
|
|
166
87
|
const current = await buildPatchQueueContext(paths.patches);
|
|
167
|
-
const snapshot = (context) => JSON.stringify(context.entries.map(({ filename, order, diff, metadata }) => ({
|
|
168
|
-
filename,
|
|
169
|
-
order,
|
|
170
|
-
diff,
|
|
171
|
-
metadata,
|
|
172
|
-
})));
|
|
173
88
|
// A changed body or manifest invalidates the dependency scan approved
|
|
174
89
|
// before the prompt. Recheck all of its inputs while holding the lock.
|
|
175
|
-
if (
|
|
90
|
+
if (queueSnapshot(current) !== queueSnapshot(baseCtx)) {
|
|
176
91
|
throw new GeneralError('Patch queue changed while waiting for confirmation or the lock. Re-run patch delete to review the current dependencies.');
|
|
177
92
|
}
|
|
178
93
|
await removePatchFileAndManifest(paths.patches, target.filename);
|
|
94
|
+
await deleteCreatedFiles(paths.engine, claimed.deleteOnCommit);
|
|
179
95
|
await appendHistoryBestEffort(paths.patches, {
|
|
180
96
|
operation: 'patch-delete',
|
|
181
97
|
args: {
|
|
182
98
|
filename: target.filename,
|
|
183
99
|
order: target.order,
|
|
184
100
|
filesAffected: target.filesAffected,
|
|
101
|
+
...(claimed.deleteOnCommit.length > 0
|
|
102
|
+
? { deletedCreatedFiles: claimed.deleteOnCommit }
|
|
103
|
+
: {}),
|
|
185
104
|
},
|
|
186
105
|
...(options.yes === true ? { yes: true } : {}),
|
|
187
106
|
...(options.forceUnsafe === true ? { unsafeOverride: true } : {}),
|
|
188
107
|
result: 'ok',
|
|
189
108
|
}, `patch delete committed (${target.filename})`);
|
|
190
|
-
}, { waitLockSeconds
|
|
109
|
+
}, { waitLockSeconds, command: 'patch delete' }));
|
|
191
110
|
info(`Deleted ${target.filename}.`);
|
|
192
111
|
outro('Delete complete');
|
|
193
112
|
}
|
|
@@ -204,6 +123,7 @@ export function registerPatchDelete(parent, context) {
|
|
|
204
123
|
.description('Delete a patch from the queue (destructive)')
|
|
205
124
|
.option('--dry-run', 'Show what would happen without writing')
|
|
206
125
|
.option('-y, --yes', 'Skip confirmation prompt (required for non-TTY)')
|
|
126
|
+
.option('--delete-created', 'Also delete the engine files this patch creates that are still on disk, instead of leaving them unmanaged')
|
|
207
127
|
.option('--force-unsafe', 'Bypass the refusal when a later patch depends on this patch (last resort)');
|
|
208
128
|
addWaitLockOption(command).action(withErrorHandling(async (name, options) => {
|
|
209
129
|
await patchDeleteCommand(getProjectRoot(), name, pickDefined(options));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `fireforge patch <verb>` parent command. Groups single-patch mutations and
|
|
3
3
|
* planners (`compact`, `delete`, `lint-ignore`, `move-files`, `rename`,
|
|
4
|
-
* `reorder`, `
|
|
5
|
-
* top-level command list. Queue-level
|
|
6
|
-
* and `status` stay flat.
|
|
4
|
+
* `reorder`, `reserve`, `retire`, `split`, `stage`, `staged-dependency`,
|
|
5
|
+
* `tier`) so they do not clutter the top-level command list. Queue-level
|
|
6
|
+
* verbs like `lint`, `export`, `verify`, and `status` stay flat.
|
|
7
7
|
*/
|
|
8
8
|
import { Command } from 'commander';
|
|
9
9
|
import type { CommandContext } from '../../types/cli.js';
|