@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
|
@@ -22,19 +22,47 @@ import { assertConfirmationAvailable, confirmDestructive } from '../../core/dest
|
|
|
22
22
|
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
23
23
|
import { formatPatchNotFoundError } from '../../core/patch-identifier-suggest.js';
|
|
24
24
|
import { buildPatchQueueContext } from '../../core/patch-lint.js';
|
|
25
|
+
import { buildLintIgnoreChecks } from '../../core/patch-lint-ignore.js';
|
|
25
26
|
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
26
27
|
import { loadPatchesManifest, renumberPatchesInManifest, resolvePatchIdentifier, savePatchesManifest, validatePatchesManifest, } from '../../core/patch-manifest.js';
|
|
27
28
|
import { enforcePatchPolicy } from '../../core/patch-policy.js';
|
|
29
|
+
import { manifestWithRetiredSlots } from '../../core/patch-retired.js';
|
|
28
30
|
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
29
31
|
import { toError } from '../../utils/errors.js';
|
|
30
32
|
import { readText, removeFile, writeText } from '../../utils/fs.js';
|
|
31
33
|
import { info, intro, outro, success, warn } from '../../utils/logger.js';
|
|
32
34
|
import { addWaitLockOption, commanderArgParser, pickDefined, resolveWaitLockSeconds, } from '../../utils/options.js';
|
|
33
35
|
import { normalizePatchDisplayName, parsePositiveIntegerFlag } from '../../utils/validation.js';
|
|
36
|
+
import { withDeclarationsReport } from '../declarations-report.js';
|
|
34
37
|
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
35
38
|
import { placementPlansEqual, resolvePlacementPlan } from '../export-flow.js';
|
|
36
39
|
import { runPatchLint } from '../export-shared.js';
|
|
37
|
-
import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary,
|
|
40
|
+
import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary, mergeStagedForwardImports, projectSplitManifest, runProjectedSplitLint, splitDeclarationRenamer, } from './split-plan.js';
|
|
41
|
+
import { describeStagedMaintenance, emptyStagedMaintenance, findOwnerRewriteHolders, rewriteSplitOwners, withoutDroppedDeclarations, withoutRelocatedDeclarations, } from './split-staged.js';
|
|
42
|
+
/**
|
|
43
|
+
* History payload for the declarations a split re-homed or dropped, omitted
|
|
44
|
+
* when it touched none so unaffected records keep their old shape.
|
|
45
|
+
*/
|
|
46
|
+
function stagedMaintenanceHistory(plan) {
|
|
47
|
+
const { relocated, dropped } = plan.stagedMaintenance;
|
|
48
|
+
const relocatedCount = (relocated.forwardImports?.length ?? 0) + (relocated.registrations?.length ?? 0);
|
|
49
|
+
return {
|
|
50
|
+
...(relocatedCount > 0 ? { relocatedDeclarations: relocated } : {}),
|
|
51
|
+
...(dropped.length > 0 ? { droppedDeclarations: dropped } : {}),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Prints the owner rewrites and the declarations the move re-homed or
|
|
56
|
+
* dropped, after a committed split or `move-files --create`.
|
|
57
|
+
*/
|
|
58
|
+
export function reportSplitStagedChanges(plan) {
|
|
59
|
+
if (plan.ownerRewrites.length > 0) {
|
|
60
|
+
info(`Re-pointed staged-dependency owners in: ${plan.ownerRewrites.join(', ')}`);
|
|
61
|
+
}
|
|
62
|
+
for (const line of describeStagedMaintenance(plan.stagedMaintenance, plan.source.filename, plan.placement.newFilename)) {
|
|
63
|
+
info(line);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
38
66
|
/**
|
|
39
67
|
* Commits a confirmed split under the patch directory lock: renumber →
|
|
40
68
|
* write new patch body → write shrunken source body → single manifest
|
|
@@ -84,13 +112,17 @@ export async function commitPatchSplit(patchesDir, plan, newMetadata, options, c
|
|
|
84
112
|
if (decls?.length) {
|
|
85
113
|
withOwners = mergeStagedForwardImports(withOwners, decls);
|
|
86
114
|
}
|
|
115
|
+
withOwners = withoutDroppedDeclarations(withOwners, plan.stagedMaintenance.dropped);
|
|
87
116
|
if (patch.filename !== effectiveSourceFilename)
|
|
88
117
|
return withOwners;
|
|
89
|
-
return {
|
|
118
|
+
return {
|
|
119
|
+
...withoutRelocatedDeclarations(withOwners, movedSet),
|
|
120
|
+
filesAffected: plan.remainingFiles,
|
|
121
|
+
};
|
|
90
122
|
});
|
|
91
123
|
updatedPatches.push(newMetadata);
|
|
92
124
|
updatedPatches.sort((a, b) => a.order - b.order || a.filename.localeCompare(b.filename));
|
|
93
|
-
const updated = validatePatchesManifest(
|
|
125
|
+
const updated = validatePatchesManifest(manifestWithRetiredSlots(fresh, updatedPatches));
|
|
94
126
|
await savePatchesManifest(patchesDir, updated);
|
|
95
127
|
await appendHistoryBestEffort(patchesDir, {
|
|
96
128
|
operation: 'patch-split',
|
|
@@ -100,6 +132,7 @@ export async function commitPatchSplit(patchesDir, plan, newMetadata, options, c
|
|
|
100
132
|
order: plan.placement.insertionOrder,
|
|
101
133
|
files: plan.movedFiles,
|
|
102
134
|
ownerRewrites: plan.ownerRewrites,
|
|
135
|
+
...stagedMaintenanceHistory(plan),
|
|
103
136
|
renames: [...plan.placement.renameMap.entries()].map(([from, entry]) => ({
|
|
104
137
|
from,
|
|
105
138
|
to: entry.newFilename,
|
|
@@ -212,16 +245,27 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
212
245
|
name: displayName,
|
|
213
246
|
description: options.description ?? '',
|
|
214
247
|
ownerRewrites: findOwnerRewriteHolders(manifest.patches, source.filename, movedSet),
|
|
215
|
-
// Populated by runProjectedSplitLint below (forward edges into the new
|
|
248
|
+
// Populated by runProjectedSplitLint below (forward edges into the new
|
|
249
|
+
// patch, and the declarations the move re-homes or makes unused).
|
|
216
250
|
stagedDependencyAdditions: new Map(),
|
|
251
|
+
stagedMaintenance: emptyStagedMaintenance(),
|
|
217
252
|
};
|
|
253
|
+
// Policy first: the projected manifest (orders, ranges, the gap rule)
|
|
254
|
+
// does not depend on the lint below, so a refused --order fails before
|
|
255
|
+
// the per-patch and projected lint passes instead of after them.
|
|
256
|
+
enforcePatchPolicy({
|
|
257
|
+
config,
|
|
258
|
+
manifest: projectSplitManifest(manifest, plan, buildNewPatchMetadata(plan, config)),
|
|
259
|
+
command: 'patch split',
|
|
260
|
+
forceUnsafe: options.forceUnsafe === true,
|
|
261
|
+
});
|
|
218
262
|
// Per-patch lint both projected bodies, threading the source patch's
|
|
219
263
|
// tier/lintIgnore so an intentional-advisory patch can still split.
|
|
220
264
|
// The whole-queue context (built once, with the config for committed-gate
|
|
221
265
|
// parity) resolves cross-patch imports and sibling head.js harness roots
|
|
222
266
|
// instead of linting each projected body blind.
|
|
223
267
|
const patchQueueCtx = await buildPatchQueueContext(paths.patches, config);
|
|
224
|
-
const ignoreChecks =
|
|
268
|
+
const ignoreChecks = buildLintIgnoreChecks(source.lintIgnore);
|
|
225
269
|
await runPatchLint({
|
|
226
270
|
engineDir: paths.engine,
|
|
227
271
|
filesAffected: remainingFiles,
|
|
@@ -242,15 +286,16 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
242
286
|
ignoreChecks,
|
|
243
287
|
patchTier: source.tier,
|
|
244
288
|
});
|
|
245
|
-
const
|
|
246
|
-
plan.stagedDependencyAdditions = stagedDependencyAdditions;
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
289
|
+
const projected = runProjectedSplitLint(plan, patchQueueCtx);
|
|
290
|
+
plan.stagedDependencyAdditions = projected.stagedDependencyAdditions;
|
|
291
|
+
plan.stagedMaintenance = projected.stagedMaintenance;
|
|
292
|
+
const conflicts = await withDeclarationsReport(projected.conflicts, {
|
|
293
|
+
projectRoot,
|
|
294
|
+
regressions: projected.regressions,
|
|
295
|
+
out: options.declarationsOut,
|
|
296
|
+
rename: splitDeclarationRenamer(plan),
|
|
253
297
|
});
|
|
298
|
+
const newMetadata = buildNewPatchMetadata(plan, config);
|
|
254
299
|
const decision = await confirmDestructive({
|
|
255
300
|
operation: 'patch-split',
|
|
256
301
|
title: `Split ${plan.movedFiles.length} file(s) out of ${source.filename} into ${placement.newFilename}`,
|
|
@@ -264,9 +309,7 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
264
309
|
return;
|
|
265
310
|
await commitPatchSplit(paths.patches, plan, newMetadata, options, config);
|
|
266
311
|
success(`Split ${source.filename}: ${placement.newFilename} now owns ${plan.movedFiles.length} file(s)`);
|
|
267
|
-
|
|
268
|
-
info(`Re-pointed staged-dependency owners in: ${plan.ownerRewrites.join(', ')}`);
|
|
269
|
-
}
|
|
312
|
+
reportSplitStagedChanges(plan);
|
|
270
313
|
outro('Split complete');
|
|
271
314
|
}
|
|
272
315
|
/**
|
|
@@ -287,7 +330,8 @@ export function registerPatchSplit(parent, context) {
|
|
|
287
330
|
.option('--dry-run', 'Show what would happen without writing')
|
|
288
331
|
.option('-y, --yes', 'Skip confirmation prompt (required for non-TTY)')
|
|
289
332
|
.option('--force-unsafe', 'Bypass projected-lint refusals')
|
|
290
|
-
.option('--skip-lint', 'Skip per-patch lint of the projected bodies')
|
|
333
|
+
.option('--skip-lint', 'Skip per-patch lint of the projected bodies')
|
|
334
|
+
.option('--declarations-out <path>', 'Where a refusal writes its one --from-ready staged-declaration report (default: .fireforge/declarations/<timestamp>.json)');
|
|
291
335
|
addWaitLockOption(command).action(withErrorHandling(async (sourceId, options) => {
|
|
292
336
|
await patchSplitCommand(getProjectRoot(), sourceId, {
|
|
293
337
|
files: options.files,
|
|
@@ -302,6 +346,7 @@ export function registerPatchSplit(parent, context) {
|
|
|
302
346
|
yes: options.yes,
|
|
303
347
|
forceUnsafe: options.forceUnsafe,
|
|
304
348
|
skipLint: options.skipLint,
|
|
349
|
+
declarationsOut: options.declarationsOut,
|
|
305
350
|
waitLock: options.waitLock,
|
|
306
351
|
}),
|
|
307
352
|
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fireforge patch stage <name>...`: stages the named patches' `.patch`
|
|
3
|
+
* files and only their rows of `patches/patches.json` in the project
|
|
4
|
+
* repository's git index.
|
|
5
|
+
*
|
|
6
|
+
* The case it exists for: several sessions edit one checkout's queue, and
|
|
7
|
+
* each must commit only its own patches. Staging the whole manifest takes
|
|
8
|
+
* every session's rows; staging one owner's hunks of it with
|
|
9
|
+
* `git apply --cached --unidiff-zero` placed a kept insertion at its
|
|
10
|
+
* new-side line number once a dropped hunk above had shifted the file, and
|
|
11
|
+
* an invalid manifest was committed. This command builds the staged
|
|
12
|
+
* manifest from HEAD's rows plus the named working rows (see
|
|
13
|
+
* `core/patch-manifest-stage.ts`), validates it, and writes it straight to
|
|
14
|
+
* the index with `git hash-object` + `git update-index`. The working copy
|
|
15
|
+
* is never touched, and nothing is committed.
|
|
16
|
+
*/
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import type { CommandContext } from '../../types/cli.js';
|
|
19
|
+
import type { PatchStageOptions } from '../../types/commands/index.js';
|
|
20
|
+
/**
|
|
21
|
+
* Runs the `patch stage` command.
|
|
22
|
+
*
|
|
23
|
+
* @param projectRoot - Project root directory (a git working tree)
|
|
24
|
+
* @param identifiers - Patch filenames, ordinals or names to stage
|
|
25
|
+
* @param options - Command options
|
|
26
|
+
*/
|
|
27
|
+
export declare function patchStageCommand(projectRoot: string, identifiers: readonly string[], options?: PatchStageOptions): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Registers the `patch stage` subcommand on the `patch` parent.
|
|
30
|
+
*
|
|
31
|
+
* @param parent - Parent Commander command
|
|
32
|
+
* @param context - Shared CLI registration context
|
|
33
|
+
*/
|
|
34
|
+
export declare function registerPatchStage(parent: Command, context: CommandContext): void;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `fireforge patch stage <name>...`: stages the named patches' `.patch`
|
|
4
|
+
* files and only their rows of `patches/patches.json` in the project
|
|
5
|
+
* repository's git index.
|
|
6
|
+
*
|
|
7
|
+
* The case it exists for: several sessions edit one checkout's queue, and
|
|
8
|
+
* each must commit only its own patches. Staging the whole manifest takes
|
|
9
|
+
* every session's rows; staging one owner's hunks of it with
|
|
10
|
+
* `git apply --cached --unidiff-zero` placed a kept insertion at its
|
|
11
|
+
* new-side line number once a dropped hunk above had shifted the file, and
|
|
12
|
+
* an invalid manifest was committed. This command builds the staged
|
|
13
|
+
* manifest from HEAD's rows plus the named working rows (see
|
|
14
|
+
* `core/patch-manifest-stage.ts`), validates it, and writes it straight to
|
|
15
|
+
* the index with `git hash-object` + `git update-index`. The working copy
|
|
16
|
+
* is never touched, and nothing is committed.
|
|
17
|
+
*/
|
|
18
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
19
|
+
import { tmpdir } from 'node:os';
|
|
20
|
+
import { join, relative, sep } from 'node:path';
|
|
21
|
+
import { Command } from 'commander';
|
|
22
|
+
import { getProjectPaths } from '../../core/config.js';
|
|
23
|
+
import { git } from '../../core/git-base.js';
|
|
24
|
+
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
25
|
+
import { loadPatchesManifestForWrite, resolvePatchIdentifier, validatePatchesManifest, } from '../../core/patch-manifest.js';
|
|
26
|
+
import { mergeStagedManifest, serializeStagedManifest, } from '../../core/patch-manifest-stage.js';
|
|
27
|
+
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
28
|
+
import { toError } from '../../utils/errors.js';
|
|
29
|
+
import { pathExists } from '../../utils/fs.js';
|
|
30
|
+
import { info, intro, outro } from '../../utils/logger.js';
|
|
31
|
+
import { addWaitLockOption, pickDefined, resolveWaitLockSeconds } from '../../utils/options.js';
|
|
32
|
+
import { exec } from '../../utils/process.js';
|
|
33
|
+
/** A project-relative path in git's separator. */
|
|
34
|
+
function gitPath(projectRoot, path) {
|
|
35
|
+
return relative(projectRoot, path).split(sep).join('/');
|
|
36
|
+
}
|
|
37
|
+
/** HEAD's manifest, or null when HEAD has none (or there is no HEAD yet). */
|
|
38
|
+
async function readHeadManifest(projectRoot, manifestPath) {
|
|
39
|
+
const shown = await exec('git', ['show', `HEAD:./${manifestPath}`], { cwd: projectRoot });
|
|
40
|
+
if (shown.exitCode !== 0)
|
|
41
|
+
return null;
|
|
42
|
+
try {
|
|
43
|
+
return validatePatchesManifest(JSON.parse(shown.stdout));
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new GeneralError(`HEAD's ${manifestPath} is not a valid manifest (${toError(error).message}); stage it by hand.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Resolves each identifier to a filename, against the working rows then HEAD's. */
|
|
50
|
+
function resolveFilenames(identifiers, working, head) {
|
|
51
|
+
const filenames = new Set();
|
|
52
|
+
for (const identifier of identifiers) {
|
|
53
|
+
const live = resolvePatchIdentifier(identifier, working.patches);
|
|
54
|
+
// A patch deleted (or renamed away) in the working copy is named by its
|
|
55
|
+
// HEAD filename, so its row can be staged as removed.
|
|
56
|
+
const gone = (head?.patches ?? []).find((row) => row.filename === identifier || row.filename === `${identifier}.patch`);
|
|
57
|
+
const filename = live?.filename ?? gone?.filename;
|
|
58
|
+
if (filename === undefined) {
|
|
59
|
+
throw new InvalidArgumentError(`No patch "${identifier}" in the working manifest or at HEAD.`, identifier);
|
|
60
|
+
}
|
|
61
|
+
filenames.add(filename);
|
|
62
|
+
}
|
|
63
|
+
return filenames;
|
|
64
|
+
}
|
|
65
|
+
/** Stages one `.patch` file: its content, or its removal when it is gone. */
|
|
66
|
+
async function stagePatchFile(projectRoot, patchPath) {
|
|
67
|
+
if (await pathExists(join(projectRoot, patchPath))) {
|
|
68
|
+
await git(['add', '--', patchPath], projectRoot);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const tracked = await exec('git', ['ls-files', '--error-unmatch', '--', patchPath], {
|
|
72
|
+
cwd: projectRoot,
|
|
73
|
+
});
|
|
74
|
+
if (tracked.exitCode === 0) {
|
|
75
|
+
await git(['rm', '--cached', '--quiet', '--', patchPath], projectRoot);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Writes `content` as a blob and points the index entry at it. */
|
|
79
|
+
async function stageBlob(projectRoot, path, content) {
|
|
80
|
+
const scratch = await mkdtemp(join(tmpdir(), 'fireforge-stage-'));
|
|
81
|
+
try {
|
|
82
|
+
const file = join(scratch, 'patches.json');
|
|
83
|
+
await writeFile(file, content);
|
|
84
|
+
const sha = (await git(['hash-object', '-w', '--no-filters', file], projectRoot)).trim();
|
|
85
|
+
await git(['update-index', '--add', '--cacheinfo', `100644,${sha},${path}`], projectRoot);
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
await rm(scratch, { recursive: true, force: true });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Runs the `patch stage` command.
|
|
93
|
+
*
|
|
94
|
+
* @param projectRoot - Project root directory (a git working tree)
|
|
95
|
+
* @param identifiers - Patch filenames, ordinals or names to stage
|
|
96
|
+
* @param options - Command options
|
|
97
|
+
*/
|
|
98
|
+
export async function patchStageCommand(projectRoot, identifiers, options = {}) {
|
|
99
|
+
intro(options.dryRun ? 'FireForge patch stage (dry run)' : 'FireForge patch stage');
|
|
100
|
+
const probe = await exec('git', ['rev-parse', '--is-inside-work-tree'], { cwd: projectRoot });
|
|
101
|
+
if (probe.exitCode !== 0 || probe.stdout.trim() !== 'true') {
|
|
102
|
+
throw new GeneralError('patch stage needs the project to be a git working tree.');
|
|
103
|
+
}
|
|
104
|
+
const { patches: patchesDir } = getProjectPaths(projectRoot);
|
|
105
|
+
const manifestPath = gitPath(projectRoot, join(patchesDir, 'patches.json'));
|
|
106
|
+
// Under the patch lock, so the rows and `.patch` files staged are one
|
|
107
|
+
// consistent snapshot rather than a peer's half-finished write.
|
|
108
|
+
await withPatchDirectoryLock(patchesDir, async () => {
|
|
109
|
+
const working = await loadPatchesManifestForWrite(patchesDir);
|
|
110
|
+
if (working === null) {
|
|
111
|
+
throw new GeneralError(`No ${manifestPath} to stage.`);
|
|
112
|
+
}
|
|
113
|
+
const head = await readHeadManifest(projectRoot, manifestPath);
|
|
114
|
+
const filenames = resolveFilenames(identifiers, working, head);
|
|
115
|
+
let staged;
|
|
116
|
+
try {
|
|
117
|
+
staged = mergeStagedManifest(head, working, filenames);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
throw new GeneralError(`Refusing to stage: ${toError(error).message}.`);
|
|
121
|
+
}
|
|
122
|
+
const prefix = options.dryRun === true ? '[dry-run] ' : '';
|
|
123
|
+
for (const { filename, change } of staged.changes) {
|
|
124
|
+
info(`${prefix}${filename}: row ${change}; .patch file staged`);
|
|
125
|
+
}
|
|
126
|
+
if (options.dryRun === true)
|
|
127
|
+
return;
|
|
128
|
+
await stageBlob(projectRoot, manifestPath, serializeStagedManifest(staged.manifest));
|
|
129
|
+
for (const filename of filenames) {
|
|
130
|
+
await stagePatchFile(projectRoot, gitPath(projectRoot, join(patchesDir, filename)));
|
|
131
|
+
}
|
|
132
|
+
info(`Staged ${filenames.size} patch(es) and their ${manifestPath} rows. Other rows stay as at HEAD; ` +
|
|
133
|
+
'nothing was committed.');
|
|
134
|
+
}, { waitLockSeconds: resolveWaitLockSeconds(options.waitLock), command: 'patch stage' });
|
|
135
|
+
outro(options.dryRun ? 'Dry run complete — nothing staged' : 'Patch stage complete');
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Registers the `patch stage` subcommand on the `patch` parent.
|
|
139
|
+
*
|
|
140
|
+
* @param parent - Parent Commander command
|
|
141
|
+
* @param context - Shared CLI registration context
|
|
142
|
+
*/
|
|
143
|
+
export function registerPatchStage(parent, context) {
|
|
144
|
+
const { getProjectRoot, withErrorHandling } = context;
|
|
145
|
+
const command = parent
|
|
146
|
+
.command('stage <names...>')
|
|
147
|
+
.description("Stage the named patches' .patch files and only their patches.json rows in the git index " +
|
|
148
|
+
"(HEAD's rows for every other patch); validates the staged manifest, commits nothing")
|
|
149
|
+
.option('--dry-run', 'Show which rows would be staged without touching the index');
|
|
150
|
+
addWaitLockOption(command).action(withErrorHandling(async (names, options) => {
|
|
151
|
+
await patchStageCommand(getProjectRoot(), names, pickDefined(options));
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch input and history delta for `patch staged-dependency`.
|
|
3
|
+
*
|
|
4
|
+
* `--from <json-file>` lets one `--add` call declare many edges under one
|
|
5
|
+
* lock hold. Turning on `patchLint.forwardRegistration: "extended"` on a real
|
|
6
|
+
* queue surfaced hundreds of undeclared registrations, each printed with its
|
|
7
|
+
* own command, and one call per edge was the only way to declare them. The
|
|
8
|
+
* file is either a `lint --per-patch --report` JSON (its findings carry the
|
|
9
|
+
* `stagedDeclarations` that discharge them) or a plain array of
|
|
10
|
+
* declarations.
|
|
11
|
+
*
|
|
12
|
+
* The history record of every call holds only the delta (`added` /
|
|
13
|
+
* `removed`). Recording the patch's whole list before and after grew the
|
|
14
|
+
* log quadratically with the list: 849 single-edge calls took one consumer's
|
|
15
|
+
* log from 36 MB to 184 MB.
|
|
16
|
+
*/
|
|
17
|
+
import type { PatchStagedDependencies, PatchStagedForwardImport, PatchStagedRegistration } from '../../types/commands/index.js';
|
|
18
|
+
/** Declarations read from a `--from` file for one target patch. */
|
|
19
|
+
export interface StagedDependencyBatch {
|
|
20
|
+
forwardImports: PatchStagedForwardImport[];
|
|
21
|
+
registrations: PatchStagedRegistration[];
|
|
22
|
+
/** Entries naming a different patch, left out. */
|
|
23
|
+
skippedForOtherPatches: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reads the declarations a `--from` file supplies for `targetFilename`.
|
|
27
|
+
*
|
|
28
|
+
* Every entry takes `file`, `creates`, optional `owner` and `reason`, and
|
|
29
|
+
* either `specifier` (an import) or `line` (a registration); `kind` may
|
|
30
|
+
* name the shape explicitly. An entry with a `patch` field naming another
|
|
31
|
+
* patch is skipped and counted, so one report can be applied patch by
|
|
32
|
+
* patch. `defaultReason` (the `--reason` flag) fills entries without one.
|
|
33
|
+
*
|
|
34
|
+
* @param path - The JSON file
|
|
35
|
+
* @param targetFilename - Filename of the patch being edited
|
|
36
|
+
* @param defaultReason - Reason for entries that carry none
|
|
37
|
+
*/
|
|
38
|
+
export declare function readStagedDependencyBatch(path: string, targetFilename: string, defaultReason?: string): Promise<StagedDependencyBatch>;
|
|
39
|
+
/**
|
|
40
|
+
* The history delta between two committed `stagedDependencies` values:
|
|
41
|
+
* the declarations the call added and the ones it removed. Each side is
|
|
42
|
+
* omitted when empty. Older log records carry `before`/`after` instead;
|
|
43
|
+
* a reader should accept either.
|
|
44
|
+
*/
|
|
45
|
+
export declare function stagedDependencyDelta(before: PatchStagedDependencies | undefined, after: PatchStagedDependencies | undefined): {
|
|
46
|
+
added?: PatchStagedDependencies;
|
|
47
|
+
removed?: PatchStagedDependencies;
|
|
48
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Batch input and history delta for `patch staged-dependency`.
|
|
4
|
+
*
|
|
5
|
+
* `--from <json-file>` lets one `--add` call declare many edges under one
|
|
6
|
+
* lock hold. Turning on `patchLint.forwardRegistration: "extended"` on a real
|
|
7
|
+
* queue surfaced hundreds of undeclared registrations, each printed with its
|
|
8
|
+
* own command, and one call per edge was the only way to declare them. The
|
|
9
|
+
* file is either a `lint --per-patch --report` JSON (its findings carry the
|
|
10
|
+
* `stagedDeclarations` that discharge them) or a plain array of
|
|
11
|
+
* declarations.
|
|
12
|
+
*
|
|
13
|
+
* The history record of every call holds only the delta (`added` /
|
|
14
|
+
* `removed`). Recording the patch's whole list before and after grew the
|
|
15
|
+
* log quadratically with the list: 849 single-edge calls took one consumer's
|
|
16
|
+
* log from 36 MB to 184 MB.
|
|
17
|
+
*/
|
|
18
|
+
import { InvalidArgumentError } from '../../errors/base.js';
|
|
19
|
+
import { toError } from '../../utils/errors.js';
|
|
20
|
+
import { readJson } from '../../utils/fs.js';
|
|
21
|
+
import { isArray, isObject } from '../../utils/validation.js';
|
|
22
|
+
function optionalString(record, key, label) {
|
|
23
|
+
const value = record[key];
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
27
|
+
throw new InvalidArgumentError(`${label}.${key} must be a non-empty string.`, '--from');
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function requiredString(record, key, label) {
|
|
32
|
+
const value = optionalString(record, key, label);
|
|
33
|
+
if (value === undefined) {
|
|
34
|
+
throw new InvalidArgumentError(`${label} is missing "${key}".`, '--from');
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Collects the declaration records a parsed file holds: a lint report's
|
|
40
|
+
* `stagedDeclarations` (from `queueIssues` and every patch's `issues`), or
|
|
41
|
+
* the elements of a plain array.
|
|
42
|
+
*/
|
|
43
|
+
function collectRecords(data) {
|
|
44
|
+
if (isArray(data)) {
|
|
45
|
+
return data.map((entry, index) => {
|
|
46
|
+
if (!isObject(entry)) {
|
|
47
|
+
throw new InvalidArgumentError(`--from entry [${index}] must be an object.`, '--from');
|
|
48
|
+
}
|
|
49
|
+
return { record: entry, label: `--from entry [${index}]` };
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (!isObject(data)) {
|
|
53
|
+
throw new InvalidArgumentError('--from must hold a JSON array of declarations or a "lint --per-patch --report" document.', '--from');
|
|
54
|
+
}
|
|
55
|
+
const issues = [];
|
|
56
|
+
if (isArray(data['queueIssues']))
|
|
57
|
+
issues.push(...data['queueIssues']);
|
|
58
|
+
if (isArray(data['patches'])) {
|
|
59
|
+
for (const patch of data['patches']) {
|
|
60
|
+
if (isObject(patch) && isArray(patch['issues']))
|
|
61
|
+
issues.push(...patch['issues']);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const records = [];
|
|
65
|
+
for (const issue of issues) {
|
|
66
|
+
if (!isObject(issue) || !isArray(issue['stagedDeclarations']))
|
|
67
|
+
continue;
|
|
68
|
+
for (const declaration of issue['stagedDeclarations']) {
|
|
69
|
+
if (isObject(declaration)) {
|
|
70
|
+
records.push({
|
|
71
|
+
record: declaration,
|
|
72
|
+
label: `stagedDeclarations of [${String(issue['check'])}] ${String(issue['file'])}`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return records;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reads the declarations a `--from` file supplies for `targetFilename`.
|
|
81
|
+
*
|
|
82
|
+
* Every entry takes `file`, `creates`, optional `owner` and `reason`, and
|
|
83
|
+
* either `specifier` (an import) or `line` (a registration); `kind` may
|
|
84
|
+
* name the shape explicitly. An entry with a `patch` field naming another
|
|
85
|
+
* patch is skipped and counted, so one report can be applied patch by
|
|
86
|
+
* patch. `defaultReason` (the `--reason` flag) fills entries without one.
|
|
87
|
+
*
|
|
88
|
+
* @param path - The JSON file
|
|
89
|
+
* @param targetFilename - Filename of the patch being edited
|
|
90
|
+
* @param defaultReason - Reason for entries that carry none
|
|
91
|
+
*/
|
|
92
|
+
export async function readStagedDependencyBatch(path, targetFilename, defaultReason) {
|
|
93
|
+
let data;
|
|
94
|
+
try {
|
|
95
|
+
data = await readJson(path);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
throw new InvalidArgumentError(`--from ${path} could not be read as JSON: ${toError(error).message}`, '--from');
|
|
99
|
+
}
|
|
100
|
+
const batch = {
|
|
101
|
+
forwardImports: [],
|
|
102
|
+
registrations: [],
|
|
103
|
+
skippedForOtherPatches: 0,
|
|
104
|
+
};
|
|
105
|
+
for (const { record, label } of collectRecords(data)) {
|
|
106
|
+
const patch = optionalString(record, 'patch', label);
|
|
107
|
+
if (patch !== undefined && patch !== targetFilename) {
|
|
108
|
+
batch.skippedForOtherPatches += 1;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const specifier = optionalString(record, 'specifier', label);
|
|
112
|
+
const line = optionalString(record, 'line', label);
|
|
113
|
+
const kind = record['kind'] ?? (line !== undefined ? 'registration' : 'import');
|
|
114
|
+
if (kind !== 'import' && kind !== 'registration') {
|
|
115
|
+
throw new InvalidArgumentError(`${label}.kind must be "import" or "registration".`, '--from');
|
|
116
|
+
}
|
|
117
|
+
const file = requiredString(record, 'file', label);
|
|
118
|
+
const creates = requiredString(record, 'creates', label);
|
|
119
|
+
const owner = optionalString(record, 'owner', label);
|
|
120
|
+
const reason = optionalString(record, 'reason', label) ?? defaultReason;
|
|
121
|
+
const extras = {
|
|
122
|
+
...(owner !== undefined ? { owner } : {}),
|
|
123
|
+
...(reason !== undefined ? { reason } : {}),
|
|
124
|
+
};
|
|
125
|
+
if (kind === 'import') {
|
|
126
|
+
if (specifier === undefined || line !== undefined) {
|
|
127
|
+
throw new InvalidArgumentError(`${label} is an import declaration and needs "specifier" (not "line").`, '--from');
|
|
128
|
+
}
|
|
129
|
+
batch.forwardImports.push({ file, specifier, creates, ...extras });
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
if (line === undefined || specifier !== undefined) {
|
|
133
|
+
throw new InvalidArgumentError(`${label} is a registration declaration and needs "line" (not "specifier").`, '--from');
|
|
134
|
+
}
|
|
135
|
+
batch.registrations.push({ file, line, creates, ...extras });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return batch;
|
|
139
|
+
}
|
|
140
|
+
function entryKey(entry) {
|
|
141
|
+
return JSON.stringify(entry);
|
|
142
|
+
}
|
|
143
|
+
function difference(left, right) {
|
|
144
|
+
const keys = new Set((right ?? []).map(entryKey));
|
|
145
|
+
return (left ?? []).filter((entry) => !keys.has(entryKey(entry)));
|
|
146
|
+
}
|
|
147
|
+
function nonEmpty(staged) {
|
|
148
|
+
const forwardImports = staged.forwardImports ?? [];
|
|
149
|
+
const registrations = staged.registrations ?? [];
|
|
150
|
+
if (forwardImports.length === 0 && registrations.length === 0)
|
|
151
|
+
return undefined;
|
|
152
|
+
return {
|
|
153
|
+
...(forwardImports.length > 0 ? { forwardImports } : {}),
|
|
154
|
+
...(registrations.length > 0 ? { registrations } : {}),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The history delta between two committed `stagedDependencies` values:
|
|
159
|
+
* the declarations the call added and the ones it removed. Each side is
|
|
160
|
+
* omitted when empty. Older log records carry `before`/`after` instead;
|
|
161
|
+
* a reader should accept either.
|
|
162
|
+
*/
|
|
163
|
+
export function stagedDependencyDelta(before, after) {
|
|
164
|
+
const added = nonEmpty({
|
|
165
|
+
forwardImports: difference(after?.forwardImports, before?.forwardImports),
|
|
166
|
+
registrations: difference(after?.registrations, before?.registrations),
|
|
167
|
+
});
|
|
168
|
+
const removed = nonEmpty({
|
|
169
|
+
forwardImports: difference(before?.forwardImports, after?.forwardImports),
|
|
170
|
+
registrations: difference(before?.registrations, after?.registrations),
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
...(added !== undefined ? { added } : {}),
|
|
174
|
+
...(removed !== undefined ? { removed } : {}),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kind-neutral views and list operations over staged-dependency
|
|
3
|
+
* declarations, shared by `patch staged-dependency` and its `--from` batch
|
|
4
|
+
* path. Split out of `staged-dependency.ts` for the per-file line budget.
|
|
5
|
+
*/
|
|
6
|
+
import type { PatchStagedDependencies, PatchStagedForwardImport, PatchStagedRegistration } from '../../types/commands/index.js';
|
|
7
|
+
/** The command's mutation mode. */
|
|
8
|
+
export type StagedDependencyMode = 'add' | 'remove' | 'clear';
|
|
9
|
+
/** The declaration shape (`--kind`). */
|
|
10
|
+
export type StagedDependencyKind = 'import' | 'registration';
|
|
11
|
+
/**
|
|
12
|
+
* One staged-dependency entry of either kind. The `matcher` field is the
|
|
13
|
+
* kind-specific middle term: the import specifier for import entries, the
|
|
14
|
+
* registration line for registration entries. Key/removal/label logic is
|
|
15
|
+
* shared across kinds through this shape.
|
|
16
|
+
*/
|
|
17
|
+
export interface StagedEntryView {
|
|
18
|
+
file: string;
|
|
19
|
+
matcher: string;
|
|
20
|
+
creates: string;
|
|
21
|
+
owner?: string;
|
|
22
|
+
reason?: string;
|
|
23
|
+
}
|
|
24
|
+
/** View of an import declaration. */
|
|
25
|
+
export declare function importView(dependency: PatchStagedForwardImport): StagedEntryView;
|
|
26
|
+
/** View of a registration declaration. */
|
|
27
|
+
export declare function registrationView(dependency: PatchStagedRegistration): StagedEntryView;
|
|
28
|
+
/** One-line operator label for a declaration. */
|
|
29
|
+
export declare function dependencyLabel(kind: StagedDependencyKind, view: StagedEntryView): string;
|
|
30
|
+
/**
|
|
31
|
+
* Applies one add/remove/clear to a declaration list: `add` appends unless
|
|
32
|
+
* an entry with the same file, matcher, creates and owner exists.
|
|
33
|
+
*/
|
|
34
|
+
export declare function applyMode<T>(existing: readonly T[], mode: StagedDependencyMode, target: StagedEntryView | undefined, toView: (entry: T) => StagedEntryView, addition: T | undefined): T[];
|
|
35
|
+
/** Number of declarations of both kinds. */
|
|
36
|
+
export declare function countStagedEntries(staged: PatchStagedDependencies | undefined): number;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Kind-neutral views and list operations over staged-dependency
|
|
4
|
+
* declarations, shared by `patch staged-dependency` and its `--from` batch
|
|
5
|
+
* path. Split out of `staged-dependency.ts` for the per-file line budget.
|
|
6
|
+
*/
|
|
7
|
+
/** View of an import declaration. */
|
|
8
|
+
export function importView(dependency) {
|
|
9
|
+
return { ...dependency, matcher: dependency.specifier };
|
|
10
|
+
}
|
|
11
|
+
/** View of a registration declaration. */
|
|
12
|
+
export function registrationView(dependency) {
|
|
13
|
+
return { ...dependency, matcher: dependency.line };
|
|
14
|
+
}
|
|
15
|
+
function dependencyKey(view) {
|
|
16
|
+
return [view.file, view.matcher, view.creates, view.owner ?? ''].join('\0');
|
|
17
|
+
}
|
|
18
|
+
/** One-line operator label for a declaration. */
|
|
19
|
+
export function dependencyLabel(kind, view) {
|
|
20
|
+
const owner = view.owner ? ` owner=${view.owner}` : '';
|
|
21
|
+
const reason = view.reason ? ` reason="${view.reason}"` : '';
|
|
22
|
+
if (kind === 'import') {
|
|
23
|
+
return `${view.file} imports "${view.matcher}" from ${view.creates}${owner}${reason}`;
|
|
24
|
+
}
|
|
25
|
+
return `${view.file} registers "${view.matcher}" for ${view.creates}${owner}${reason}`;
|
|
26
|
+
}
|
|
27
|
+
function matchesTarget(view, target) {
|
|
28
|
+
return (view.file === target.file &&
|
|
29
|
+
view.matcher === target.matcher &&
|
|
30
|
+
view.creates === target.creates &&
|
|
31
|
+
(target.owner === undefined || view.owner === target.owner));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Applies one add/remove/clear to a declaration list: `add` appends unless
|
|
35
|
+
* an entry with the same file, matcher, creates and owner exists.
|
|
36
|
+
*/
|
|
37
|
+
export function applyMode(existing, mode, target, toView, addition) {
|
|
38
|
+
if (mode === 'clear')
|
|
39
|
+
return [];
|
|
40
|
+
if (!target)
|
|
41
|
+
return [...existing];
|
|
42
|
+
if (mode === 'remove')
|
|
43
|
+
return existing.filter((entry) => !matchesTarget(toView(entry), target));
|
|
44
|
+
const seen = new Set(existing.map((entry) => dependencyKey(toView(entry))));
|
|
45
|
+
if (seen.has(dependencyKey(target)) || addition === undefined)
|
|
46
|
+
return [...existing];
|
|
47
|
+
return [...existing, addition];
|
|
48
|
+
}
|
|
49
|
+
/** Number of declarations of both kinds. */
|
|
50
|
+
export function countStagedEntries(staged) {
|
|
51
|
+
return (staged?.forwardImports?.length ?? 0) + (staged?.registrations?.length ?? 0);
|
|
52
|
+
}
|