@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
|
@@ -6,11 +6,13 @@ import { getFurnacePaths, loadFurnaceConfig } from '../../core/furnace-config.js
|
|
|
6
6
|
import { assertFurnaceReady } from '../../core/furnace-precondition.js';
|
|
7
7
|
import { addCustomElementRegistration, addJarMnEntries, pruneStaleJarMnEntries, } from '../../core/furnace-registration.js';
|
|
8
8
|
import { validateAllComponents, validateComponent } from '../../core/furnace-validate.js';
|
|
9
|
+
import { applyValidationExceptions, } from '../../core/furnace-validate-exceptions.js';
|
|
10
|
+
import { createIssue } from '../../core/furnace-validate-helpers.js';
|
|
9
11
|
import { FurnaceError } from '../../errors/furnace.js';
|
|
10
12
|
import { toError } from '../../utils/errors.js';
|
|
11
13
|
import { pathExists } from '../../utils/fs.js';
|
|
12
|
-
import { info, intro, note, outro,
|
|
13
|
-
import {
|
|
14
|
+
import { info, intro, isMachineOutputMode, note, outro, setMachineOutputMode, warn, } from '../../utils/logger.js';
|
|
15
|
+
import { judgeValidateReport, renderValidateHuman, renderValidateJson } from './validate-report.js';
|
|
14
16
|
/** Checks that auto-fix can correct. */
|
|
15
17
|
const FIXABLE_CHECKS = new Set([
|
|
16
18
|
'missing-jar-mn-mjs',
|
|
@@ -114,133 +116,176 @@ async function autoFixIssues(projectRoot, issues) {
|
|
|
114
116
|
}
|
|
115
117
|
return fixed;
|
|
116
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolves the named component's type and directory.
|
|
121
|
+
* @returns undefined for a stock component (nothing local to validate)
|
|
122
|
+
*/
|
|
123
|
+
function resolveNamedComponent(name, config, furnacePaths) {
|
|
124
|
+
if (name in config.overrides) {
|
|
125
|
+
return { type: 'override', componentDir: join(furnacePaths.overridesDir, name) };
|
|
126
|
+
}
|
|
127
|
+
if (name in config.custom) {
|
|
128
|
+
return { type: 'custom', componentDir: join(furnacePaths.customDir, name) };
|
|
129
|
+
}
|
|
130
|
+
if (config.stock.includes(name))
|
|
131
|
+
return undefined;
|
|
132
|
+
throw new FurnaceError(`Component "${name}" not found in furnace.json.`, name);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Runs the first validation pass: one named component, or every override
|
|
136
|
+
* and custom component. Returns undefined when there is nothing local to
|
|
137
|
+
* validate (a stock component, or no components at all), after saying so.
|
|
138
|
+
*/
|
|
139
|
+
async function runValidationPass(projectRoot, name, config, furnacePaths) {
|
|
140
|
+
if (name) {
|
|
141
|
+
const target = resolveNamedComponent(name, config, furnacePaths);
|
|
142
|
+
if (target === undefined) {
|
|
143
|
+
info(`"${name}" is a stock component. Stock components are not validated locally.`);
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
if (!(await pathExists(target.componentDir))) {
|
|
147
|
+
throw new FurnaceError(`Component directory not found for "${name}".`, name);
|
|
148
|
+
}
|
|
149
|
+
const issues = await validateComponent(target.componentDir, name, target.type, config, projectRoot);
|
|
150
|
+
return { results: new Map([[name, issues]]), judged: (component) => component === name };
|
|
151
|
+
}
|
|
152
|
+
if (Object.keys(config.overrides).length === 0 && Object.keys(config.custom).length === 0) {
|
|
153
|
+
info('No components to validate.');
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
if (config.stock.length > 0) {
|
|
157
|
+
info(`Skipping ${config.stock.length} stock component(s) (no local files to validate).`);
|
|
158
|
+
}
|
|
159
|
+
return { results: await validateAllComponents(projectRoot), judged: () => true };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Runs `--fix` over the fixable findings, then re-validates the affected
|
|
163
|
+
* components and swaps their findings in, so the summary and the JSON
|
|
164
|
+
* report what auto-fix actually accomplished. The counter `autoFixIssues`
|
|
165
|
+
* returns only counts calls that did not throw, so a write that succeeded
|
|
166
|
+
* but did not resolve the finding would otherwise still be reported fixed.
|
|
167
|
+
*/
|
|
168
|
+
async function applyAutoFix(projectRoot, config, furnacePaths, run, countedIssues) {
|
|
169
|
+
const fixableIssues = countedIssues.filter((issue) => FIXABLE_CHECKS.has(issue.check));
|
|
170
|
+
if (fixableIssues.length === 0) {
|
|
171
|
+
info('\nNo auto-fixable issues found. Remaining issues require manual resolution.');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
await autoFixIssues(projectRoot, fixableIssues);
|
|
175
|
+
const reValidated = await reValidateComponents(projectRoot, config, furnacePaths, new Set(fixableIssues.map((issue) => issue.component)));
|
|
176
|
+
for (const [component, issues] of reValidated)
|
|
177
|
+
run.results.set(component, issues);
|
|
178
|
+
const fixableAfter = [...reValidated.values()]
|
|
179
|
+
.flat()
|
|
180
|
+
.filter((issue) => FIXABLE_CHECKS.has(issue.check)).length;
|
|
181
|
+
const actuallyFixed = Math.max(0, fixableIssues.length - fixableAfter);
|
|
182
|
+
if (actuallyFixed > 0) {
|
|
183
|
+
info(`\nAuto-fixed ${actuallyFixed} issue(s).`);
|
|
184
|
+
}
|
|
185
|
+
if (fixableAfter > 0) {
|
|
186
|
+
warn(`${fixableAfter} fixable issue(s) remain after auto-fix — investigate manually.`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** Applies `validationExceptions` to every finding of the run. */
|
|
190
|
+
function exceptRun(run, config) {
|
|
191
|
+
return applyValidationExceptions([...run.results.values()].flat(), config.validationExceptions, run.judged);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The `furnace.json#minComponents` floor of a full run: an error when it
|
|
195
|
+
* judged fewer components, so a run over an emptied or moved components
|
|
196
|
+
* tree cannot pass by validating nothing. A run naming one component is
|
|
197
|
+
* not judged against it.
|
|
198
|
+
*/
|
|
199
|
+
function componentFloorIssue(config, name, judged) {
|
|
200
|
+
if (name !== undefined || config.minComponents === undefined)
|
|
201
|
+
return undefined;
|
|
202
|
+
if (judged >= config.minComponents)
|
|
203
|
+
return undefined;
|
|
204
|
+
return createIssue('furnace.json', 'error', 'component-floor', `Validated ${judged} component(s), under furnace.json minComponents ${config.minComponents}. ` +
|
|
205
|
+
'Check that the components tree is where furnace.json says, or lower the floor.');
|
|
206
|
+
}
|
|
117
207
|
/**
|
|
118
208
|
* Runs the furnace validate command to perform static analysis on components.
|
|
119
209
|
* @param projectRoot - Root directory of the project
|
|
120
210
|
* @param name - Optional component name to validate (validates all if omitted)
|
|
121
|
-
* @param options - Optional command options (
|
|
211
|
+
* @param options - Optional command options (`--fix`, `--json`, `--max-warnings`)
|
|
122
212
|
*/
|
|
123
213
|
export async function furnaceValidateCommand(projectRoot, name, options = {}) {
|
|
214
|
+
const previousMachineOutputMode = isMachineOutputMode();
|
|
215
|
+
// `--json` owns stdout: every diagnostic routes to stderr from here on.
|
|
216
|
+
// On a throw the mode stays engaged and withErrorHandling resets it, so
|
|
217
|
+
// the refusal cannot land on stdout after the payload.
|
|
218
|
+
if (options.json === true)
|
|
219
|
+
setMachineOutputMode(true);
|
|
220
|
+
await runFurnaceValidate(projectRoot, name, options);
|
|
221
|
+
if (isMachineOutputMode() !== previousMachineOutputMode) {
|
|
222
|
+
setMachineOutputMode(previousMachineOutputMode);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async function runFurnaceValidate(projectRoot, name, options) {
|
|
124
226
|
intro('Furnace Validate');
|
|
125
227
|
// Gains the engine rung here: this was the one site in the family that
|
|
126
228
|
// checked furnace.json alone, so a validate against a missing engine
|
|
127
229
|
// produced downstream noise instead of the shared precondition refusal.
|
|
128
230
|
const { config } = await assertFurnaceReady(projectRoot);
|
|
129
231
|
const furnacePaths = getFurnacePaths(projectRoot);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
type = 'override';
|
|
140
|
-
componentDir = join(furnacePaths.overridesDir, name);
|
|
141
|
-
}
|
|
142
|
-
else if (name in config.custom) {
|
|
143
|
-
type = 'custom';
|
|
144
|
-
componentDir = join(furnacePaths.customDir, name);
|
|
145
|
-
}
|
|
146
|
-
else if (config.stock.includes(name)) {
|
|
147
|
-
info(`"${name}" is a stock component. Stock components are not validated locally.`);
|
|
148
|
-
outro('Validation complete');
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
throw new FurnaceError(`Component "${name}" not found in furnace.json.`, name);
|
|
153
|
-
}
|
|
154
|
-
if (!(await pathExists(componentDir))) {
|
|
155
|
-
throw new FurnaceError(`Component directory not found for "${name}".`, name);
|
|
156
|
-
}
|
|
157
|
-
const issues = await validateComponent(componentDir, name, type, config, projectRoot);
|
|
158
|
-
componentCount = 1;
|
|
159
|
-
allIssues = issues;
|
|
160
|
-
if (issues.length === 0) {
|
|
161
|
-
success(`${name} — all checks passed`);
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
const [e, w] = displayValidationIssues(issues);
|
|
165
|
-
totalErrors += e;
|
|
166
|
-
totalWarnings += w;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
// --- Validate all components ---
|
|
171
|
-
const overrideCount = Object.keys(config.overrides).length;
|
|
172
|
-
const customCount = Object.keys(config.custom).length;
|
|
173
|
-
if (overrideCount === 0 && customCount === 0) {
|
|
174
|
-
info('No components to validate.');
|
|
175
|
-
outro('Done');
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
if (config.stock.length > 0) {
|
|
179
|
-
info(`Skipping ${config.stock.length} stock component(s) (no local files to validate).`);
|
|
232
|
+
const run = await runValidationPass(projectRoot, name, config, furnacePaths);
|
|
233
|
+
if (run === undefined) {
|
|
234
|
+
const floor = componentFloorIssue(config, name, 0);
|
|
235
|
+
if (options.json === true) {
|
|
236
|
+
renderValidateJson({
|
|
237
|
+
components: [],
|
|
238
|
+
outcome: { issues: floor ? [floor] : [], excepted: [], stale: [] },
|
|
239
|
+
maxWarnings: options.maxWarnings,
|
|
240
|
+
});
|
|
180
241
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
allIssues.push(...issues);
|
|
185
|
-
if (issues.length === 0) {
|
|
186
|
-
success(`${componentName} — all checks passed`);
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
const [e, w] = displayValidationIssues(issues);
|
|
190
|
-
totalErrors += e;
|
|
191
|
-
totalWarnings += w;
|
|
192
|
-
}
|
|
242
|
+
if (floor !== undefined) {
|
|
243
|
+
renderValidateHuman([], [floor]);
|
|
244
|
+
throw new FurnaceError('Validation failed with 1 error(s).');
|
|
193
245
|
}
|
|
246
|
+
outro(name ? 'Validation complete' : 'Done');
|
|
247
|
+
return;
|
|
194
248
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (options.fix &&
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const fixableBefore = fixableIssues.length;
|
|
207
|
-
const fixableAfter = reValidated.issues.filter((issue) => FIXABLE_CHECKS.has(issue.check)).length;
|
|
208
|
-
const actuallyFixed = Math.max(0, fixableBefore - fixableAfter);
|
|
209
|
-
// Replace the pre-fix issue totals with the post-fix view so the
|
|
210
|
-
// summary reflects current reality. Issues that auto-fix could not
|
|
211
|
-
// address still count toward totalErrors / totalWarnings.
|
|
212
|
-
totalErrors = reValidated.totalErrors;
|
|
213
|
-
totalWarnings = reValidated.totalWarnings;
|
|
214
|
-
if (actuallyFixed > 0) {
|
|
215
|
-
info(`\nAuto-fixed ${actuallyFixed} issue(s).`);
|
|
216
|
-
}
|
|
217
|
-
if (fixableAfter > 0) {
|
|
218
|
-
warn(`${fixableAfter} fixable issue(s) remain after auto-fix — investigate manually.`);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
info('\nNo auto-fixable issues found. Remaining issues require manual resolution.');
|
|
223
|
-
}
|
|
249
|
+
const floor = componentFloorIssue(config, name, run.results.size);
|
|
250
|
+
const firstPass = exceptRun(run, config);
|
|
251
|
+
if (floor !== undefined)
|
|
252
|
+
firstPass.issues.push(floor);
|
|
253
|
+
renderValidateHuman([...run.results.keys()], firstPass.issues);
|
|
254
|
+
let outcome = firstPass;
|
|
255
|
+
if (options.fix && firstPass.issues.length > 0) {
|
|
256
|
+
await applyAutoFix(projectRoot, config, furnacePaths, run, firstPass.issues);
|
|
257
|
+
outcome = exceptRun(run, config);
|
|
258
|
+
if (floor !== undefined)
|
|
259
|
+
outcome.issues.push(floor);
|
|
224
260
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
261
|
+
const report = {
|
|
262
|
+
components: [...run.results.keys()],
|
|
263
|
+
outcome,
|
|
264
|
+
maxWarnings: options.maxWarnings,
|
|
265
|
+
};
|
|
266
|
+
const verdict = judgeValidateReport(report);
|
|
267
|
+
const excepted = outcome.excepted.length > 0 ? `, ${outcome.excepted.length} excepted` : '';
|
|
268
|
+
note(`${verdict.errors} error(s), ${verdict.warnings} warning(s)${excepted} across ${report.components.length} component(s)`, 'Validation Summary');
|
|
269
|
+
if (options.json === true)
|
|
270
|
+
renderValidateJson(report);
|
|
271
|
+
if (verdict.errors > 0) {
|
|
228
272
|
const fixHint = options.fix ? '' : ' Use --fix to auto-correct registration issues.';
|
|
229
273
|
info(`Fix the errors above and run "fireforge furnace validate" again.${fixHint}`);
|
|
230
|
-
throw new FurnaceError(`Validation failed with ${
|
|
274
|
+
throw new FurnaceError(`Validation failed with ${verdict.errors} error(s).`);
|
|
275
|
+
}
|
|
276
|
+
if (verdict.warningBudgetExceeded) {
|
|
277
|
+
throw new FurnaceError(`Validation failed: ${verdict.warnings} warning(s) exceed --max-warnings ${String(options.maxWarnings)}. ` +
|
|
278
|
+
'Fix them, or record a reviewed finding in furnace.json validationExceptions.');
|
|
231
279
|
}
|
|
232
280
|
outro('Validation passed');
|
|
233
281
|
}
|
|
234
282
|
/**
|
|
235
283
|
* Re-validates a specific set of components after an auto-fix pass and
|
|
236
|
-
* returns
|
|
237
|
-
*
|
|
238
|
-
* actually accomplished.
|
|
284
|
+
* returns each one's post-fix findings. Used by the `--fix` path to
|
|
285
|
+
* honestly report what auto-fix actually accomplished.
|
|
239
286
|
*/
|
|
240
287
|
async function reValidateComponents(projectRoot, config, furnacePaths, componentNames) {
|
|
241
|
-
const
|
|
242
|
-
let totalErrors = 0;
|
|
243
|
-
let totalWarnings = 0;
|
|
288
|
+
const results = new Map();
|
|
244
289
|
for (const componentName of componentNames) {
|
|
245
290
|
let type;
|
|
246
291
|
let componentDir;
|
|
@@ -258,14 +303,7 @@ async function reValidateComponents(projectRoot, config, furnacePaths, component
|
|
|
258
303
|
}
|
|
259
304
|
if (!(await pathExists(componentDir)))
|
|
260
305
|
continue;
|
|
261
|
-
|
|
262
|
-
issues.push(...componentIssues);
|
|
263
|
-
for (const issue of componentIssues) {
|
|
264
|
-
if (issue.severity === 'error')
|
|
265
|
-
totalErrors += 1;
|
|
266
|
-
else
|
|
267
|
-
totalWarnings += 1;
|
|
268
|
-
}
|
|
306
|
+
results.set(componentName, await validateComponent(componentDir, componentName, type, config, projectRoot));
|
|
269
307
|
}
|
|
270
|
-
return
|
|
308
|
+
return results;
|
|
271
309
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { validateAllComponents, validateComponent } from '../../core/furnace-validate.js';
|
|
4
|
+
import { applyValidationExceptions } from '../../core/furnace-validate-exceptions.js';
|
|
4
5
|
import { FurnaceError } from '../../errors/furnace.js';
|
|
5
6
|
import { pathExists } from '../../utils/fs.js';
|
|
6
7
|
import { error, info, outro, success, warn } from '../../utils/logger.js';
|
|
@@ -36,6 +37,14 @@ function filterProjectedDryRunIssues(issues, actions) {
|
|
|
36
37
|
return true;
|
|
37
38
|
});
|
|
38
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Drops the findings `furnace.json#validationExceptions` records as
|
|
42
|
+
* reviewed, so a deploy does not fail on a finding `furnace validate`
|
|
43
|
+
* accepts. Staleness is `furnace validate`'s report, not a deploy's.
|
|
44
|
+
*/
|
|
45
|
+
function withoutExceptedIssues(issues, config) {
|
|
46
|
+
return applyValidationExceptions(issues, config.validationExceptions, () => false).issues;
|
|
47
|
+
}
|
|
39
48
|
function resolveNamedValidationTarget(name, config, furnacePaths) {
|
|
40
49
|
if (name in config.overrides) {
|
|
41
50
|
return {
|
|
@@ -67,7 +76,8 @@ async function validateNamedTarget(args) {
|
|
|
67
76
|
throw new FurnaceError(`Component directory not found for "${name}".`, name);
|
|
68
77
|
}
|
|
69
78
|
const rawIssues = await validateComponent(target.componentDir, name, target.type, config, projectRoot);
|
|
70
|
-
const
|
|
79
|
+
const counted = withoutExceptedIssues(rawIssues, config);
|
|
80
|
+
const issues = isDryRun ? filterProjectedDryRunIssues(counted, dryRunActions) : counted;
|
|
71
81
|
if (issues.length === 0) {
|
|
72
82
|
success(`${name} — all checks passed`);
|
|
73
83
|
return [0, 0];
|
|
@@ -130,9 +140,10 @@ export async function runDeployValidation(options) {
|
|
|
130
140
|
continue;
|
|
131
141
|
}
|
|
132
142
|
componentCount++;
|
|
143
|
+
const counted = withoutExceptedIssues(issues, config);
|
|
133
144
|
const projectedIssues = isDryRun
|
|
134
|
-
? filterProjectedDryRunIssues(
|
|
135
|
-
:
|
|
145
|
+
? filterProjectedDryRunIssues(counted, dryRunActions)
|
|
146
|
+
: counted;
|
|
136
147
|
if (projectedIssues.length === 0) {
|
|
137
148
|
success(`${componentName} — all checks passed`);
|
|
138
149
|
}
|
|
@@ -3,10 +3,13 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { loadConfig } from '../core/config.js';
|
|
4
4
|
import { getDiffForFilesAgainstHead } from '../core/git-diff.js';
|
|
5
5
|
import { withPrivateGitIndex } from '../core/git-readonly-index.js';
|
|
6
|
-
import { buildPatchQueueContext,
|
|
6
|
+
import { buildPatchQueueContext, formatPatchLintIssue, lintExportedPatch, lintPatchQueue, measurePatchSize, resolvePatchSizeTier, } from '../core/patch-lint.js';
|
|
7
7
|
import { buildPerPatchLintCacheKey, getCachedPerPatchLintIssues, getPerPatchLintCacheHeadSha, loadPerPatchLintCache, savePerPatchLintCache, setCachedPerPatchLintIssues, } from '../core/patch-lint-cache.js';
|
|
8
|
+
import { buildLintIgnoreChecks, evaluateLintIgnoreCeilings, formatLintIgnoreEntry, isLintIgnoreCeiling, lintIgnoreCacheTokens, removableCeilingIssues, } from '../core/patch-lint-ignore.js';
|
|
8
9
|
import { loadPatchesManifest } from '../core/patch-manifest.js';
|
|
9
10
|
import { evaluatePatchPolicy } from '../core/patch-policy.js';
|
|
11
|
+
import { evaluatePatchPolicyContent } from '../core/patch-policy-content.js';
|
|
12
|
+
import { selectPatchSubset } from '../core/patch-subset.js';
|
|
10
13
|
import { GeneralError } from '../errors/base.js';
|
|
11
14
|
import { mapWithConcurrency } from '../utils/concurrency.js';
|
|
12
15
|
import { pathExists } from '../utils/fs.js';
|
|
@@ -47,7 +50,7 @@ async function lintQueuedPatch(patch, lintCtx) {
|
|
|
47
50
|
usedCheckJs: false,
|
|
48
51
|
};
|
|
49
52
|
}
|
|
50
|
-
const ignore =
|
|
53
|
+
const ignore = buildLintIgnoreChecks(patch.lintIgnore);
|
|
51
54
|
let cacheKey;
|
|
52
55
|
if (cache) {
|
|
53
56
|
cacheKey = await buildPerPatchLintCacheKey({
|
|
@@ -60,7 +63,7 @@ async function lintQueuedPatch(patch, lintCtx) {
|
|
|
60
63
|
queueContext: ctx,
|
|
61
64
|
...(engineHeadSha === undefined ? {} : { engineHeadSha }),
|
|
62
65
|
});
|
|
63
|
-
const cached = getCachedPerPatchLintIssues(cache, patch.filename, cacheKey, ignore);
|
|
66
|
+
const cached = getCachedPerPatchLintIssues(cache, patch.filename, cacheKey, lintIgnoreCacheTokens(ignore));
|
|
64
67
|
if (cached) {
|
|
65
68
|
// Returning before the empty-diff probe below is safe: the cache key
|
|
66
69
|
// hashes every affected engine file's content plus engineHeadSha, so
|
|
@@ -106,8 +109,13 @@ async function lintQueuedPatch(patch, lintCtx) {
|
|
|
106
109
|
}
|
|
107
110
|
lintOptions.precomputedCheckJs = precomputedCheckJs;
|
|
108
111
|
}
|
|
112
|
+
// Measured from the working tree, like every other rule here: the files
|
|
113
|
+
// on disk are the would-be export, so a size tier fires mid-edit rather
|
|
114
|
+
// than first at the re-export.
|
|
115
|
+
const patchSize = await measurePatchSize(paths.engine, diff, existing, config);
|
|
109
116
|
const patchIssues = await lintExportedPatch(paths.engine, existing, diff, config, {
|
|
110
117
|
...lintOptions,
|
|
118
|
+
patchSizeMeasurement: patchSize,
|
|
111
119
|
patchQueueCtx: ctx,
|
|
112
120
|
...(ignore ? { ignoreChecks: ignore } : {}),
|
|
113
121
|
...(patch.tier ? { patchTier: patch.tier } : {}),
|
|
@@ -117,11 +125,11 @@ async function lintQueuedPatch(patch, lintCtx) {
|
|
|
117
125
|
existingFiles: existing,
|
|
118
126
|
rawIssues: patchIssues,
|
|
119
127
|
suppressedIssues,
|
|
120
|
-
lineCount:
|
|
128
|
+
lineCount: patchSize.lines,
|
|
121
129
|
usedCheckJs,
|
|
122
130
|
};
|
|
123
131
|
if (cache && cacheKey) {
|
|
124
|
-
result.cacheWrite = { key: cacheKey, lintIgnore:
|
|
132
|
+
result.cacheWrite = { key: cacheKey, lintIgnore: lintIgnoreCacheTokens(ignore) };
|
|
125
133
|
}
|
|
126
134
|
return result;
|
|
127
135
|
}
|
|
@@ -183,6 +191,9 @@ async function applyPerPatchResults(subset, results, issues, checkJs, cache, hel
|
|
|
183
191
|
for (const issue of result.rawIssues) {
|
|
184
192
|
issues.push({ ...issue, file: `${patch.filename} :: ${issue.file}` });
|
|
185
193
|
}
|
|
194
|
+
// A ceiling the patch no longer needs is reported as a notice naming
|
|
195
|
+
// the command that drops it.
|
|
196
|
+
issues.push(...removableCeilingIssues(patch.filename, evaluateLintIgnoreCeilings(patch.lintIgnore, [...result.rawIssues, ...result.suppressedIssues], { lines: result.lineCount, files: patch.filesAffected.length })));
|
|
186
197
|
// A waived size finding still reports its measurement: the finding stays
|
|
187
198
|
// suppressed (no exit-code / --max-warnings effect) but the current count
|
|
188
199
|
// is readable from the tool that enforces it, so a waiver's cited size
|
|
@@ -194,7 +205,10 @@ async function applyPerPatchResults(subset, results, issues, checkJs, cache, hel
|
|
|
194
205
|
heldNotices.push({ check: suppressedIssue.check, patch: patch.filename, suppressed: true });
|
|
195
206
|
continue;
|
|
196
207
|
}
|
|
197
|
-
|
|
208
|
+
const ceilings = (patch.lintIgnore ?? []).filter((entry) => isLintIgnoreCeiling(entry) && entry.rule === suppressedIssue.check);
|
|
209
|
+
const ceiling = ceilings.find((entry) => isLintIgnoreCeiling(entry) && entry.share === suppressedIssue.share) ?? ceilings[0];
|
|
210
|
+
const waiver = ceiling === undefined ? 'lintIgnore' : `lintIgnore (${formatLintIgnoreEntry(ceiling)})`;
|
|
211
|
+
info(`NOTICE [${suppressedIssue.check}] ${patch.filename}: suppressed by ${waiver} — ${suppressedIssue.message}`);
|
|
198
212
|
}
|
|
199
213
|
totals.suppressed += result.suppressedIssues.length;
|
|
200
214
|
if (result.status === 'cached')
|
|
@@ -309,57 +323,9 @@ function reportNoticeSummary(heldNotices, issues) {
|
|
|
309
323
|
info(`NOTICE [${check}]: ${entry.count} across ${entry.patches.size} patch(es)${suppressed} (see --report)`);
|
|
310
324
|
}
|
|
311
325
|
}
|
|
312
|
-
/**
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
* `.patch`, the bare order number, or the manifest `name` field). Throws listing the available
|
|
316
|
-
* patches when a requested name matches none, so a typo fails loud rather
|
|
317
|
-
* than silently linting nothing.
|
|
318
|
-
*/
|
|
319
|
-
function selectPatchSubset(manifest, requested) {
|
|
320
|
-
const normalizedRequests = requested
|
|
321
|
-
.flatMap((entry) => entry.split(','))
|
|
322
|
-
.map((entry) => entry.trim())
|
|
323
|
-
.filter((entry) => entry.length > 0);
|
|
324
|
-
const patchAliases = (p) => {
|
|
325
|
-
const aliases = new Set([p.filename, p.filename.replace(/\.patch$/, ''), p.name]);
|
|
326
|
-
// The bare order number, both as the manifest records it and as the
|
|
327
|
-
// filename spells it (`2` and `002`). The refusal message offers stems,
|
|
328
|
-
// and an operator reading `102-ui-canvas-tiles.patch` in a lint finding
|
|
329
|
-
// reaches for `102` first.
|
|
330
|
-
aliases.add(String(p.order));
|
|
331
|
-
const match = /^(\d+)-([a-z]+)-(.+)\.patch$/.exec(p.filename);
|
|
332
|
-
if (match?.[1])
|
|
333
|
-
aliases.add(match[1]);
|
|
334
|
-
if (match?.[2] && match[3]) {
|
|
335
|
-
aliases.add(`${match[2]}-${match[3]}`);
|
|
336
|
-
aliases.add(match[3]);
|
|
337
|
-
}
|
|
338
|
-
return aliases;
|
|
339
|
-
};
|
|
340
|
-
const matches = (p, name) => {
|
|
341
|
-
const stem = name.replace(/\.patch$/, '');
|
|
342
|
-
return (patchAliases(p).has(name) || patchAliases(p).has(stem) || patchAliases(p).has(`${stem}.patch`));
|
|
343
|
-
};
|
|
344
|
-
const selected = [];
|
|
345
|
-
const seen = new Set();
|
|
346
|
-
for (const name of normalizedRequests) {
|
|
347
|
-
const found = manifest.patches.filter((p) => matches(p, name));
|
|
348
|
-
if (found.length === 0) {
|
|
349
|
-
const available = manifest.patches.map((p) => p.filename).join(', ');
|
|
350
|
-
throw new GeneralError(`No patch in the queue matches "${name}". In --per-patch mode, positional arguments ` +
|
|
351
|
-
`and --patches select patches (filename, stem, order number, manifest name, or slug) — not engine ` +
|
|
352
|
-
`files; drop --per-patch to lint engine paths. Available patches: ${available}`);
|
|
353
|
-
}
|
|
354
|
-
for (const p of found) {
|
|
355
|
-
if (!seen.has(p.filename)) {
|
|
356
|
-
seen.add(p.filename);
|
|
357
|
-
selected.push(p);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
return selected;
|
|
362
|
-
}
|
|
326
|
+
/** The refusal sentence `selectPatchSubset` adds for a `lint --per-patch` miss. */
|
|
327
|
+
const PER_PATCH_SUBSET_HINT = 'In --per-patch mode, positional arguments and --patches select patches (filename, stem, ' +
|
|
328
|
+
'order number, manifest name, or slug) — not engine files; drop --per-patch to lint engine paths.';
|
|
363
329
|
/**
|
|
364
330
|
* Lints each patch in the queue as its own isolated diff, honouring
|
|
365
331
|
* per-patch `lintIgnore` entries. Cross-patch rules still run once over
|
|
@@ -382,7 +348,7 @@ async function lintPerPatchInner(projectRoot, paths, options = {}) {
|
|
|
382
348
|
return;
|
|
383
349
|
}
|
|
384
350
|
const subset = options.patches && options.patches.length > 0
|
|
385
|
-
? selectPatchSubset(manifest, options.patches)
|
|
351
|
+
? selectPatchSubset(manifest, options.patches, PER_PATCH_SUBSET_HINT)
|
|
386
352
|
: manifest.patches;
|
|
387
353
|
const subsetNames = new Set(subset.map((p) => p.filename));
|
|
388
354
|
const isSubset = subset.length !== manifest.patches.length;
|
|
@@ -405,7 +371,11 @@ async function lintPerPatchInner(projectRoot, paths, options = {}) {
|
|
|
405
371
|
const cache = options.noCache === true ? undefined : await loadPerPatchLintCache(projectRoot);
|
|
406
372
|
const engineHeadSha = cache ? await getPerPatchLintCacheHeadSha(paths.engine) : undefined;
|
|
407
373
|
const issues = [];
|
|
408
|
-
|
|
374
|
+
const policyIssues = [
|
|
375
|
+
...evaluatePatchPolicy(config, manifest),
|
|
376
|
+
...(await evaluatePatchPolicyContent(config, projectRoot, manifest, ctx.entries)),
|
|
377
|
+
];
|
|
378
|
+
for (const issue of policyIssues) {
|
|
409
379
|
if (isSubset && !subsetNames.has(issue.filename))
|
|
410
380
|
continue;
|
|
411
381
|
issues.push({
|
|
@@ -433,10 +403,15 @@ async function lintPerPatchInner(projectRoot, paths, options = {}) {
|
|
|
433
403
|
});
|
|
434
404
|
const heldNotices = options.notices === 'summary' ? [] : undefined;
|
|
435
405
|
const { linted, skipped, cacheDirty, reusedCacheEntries, suppressed } = await applyPerPatchResults(subset, results, issues, checkJs, cache, heldNotices);
|
|
406
|
+
const queueIssues = [];
|
|
436
407
|
for (const issue of lintPatchQueue(ctx)) {
|
|
437
|
-
|
|
408
|
+
// A finding that names a subset patch stays in scope even when its file
|
|
409
|
+
// is one the patch no longer touches (a stale staged declaration).
|
|
410
|
+
const namesSubset = issue.patches?.some((name) => subsetNames.has(name)) === true;
|
|
411
|
+
if (isSubset && !namesSubset && !subsetTouchedFiles.has(issue.file))
|
|
438
412
|
continue;
|
|
439
413
|
issues.push(issue);
|
|
414
|
+
queueIssues.push(issue);
|
|
440
415
|
}
|
|
441
416
|
if (cache && cacheDirty)
|
|
442
417
|
await savePerPatchLintCache(projectRoot, cache);
|
|
@@ -447,7 +422,7 @@ async function lintPerPatchInner(projectRoot, paths, options = {}) {
|
|
|
447
422
|
info(`Suppressed ${suppressed} issue(s) via per-patch lintIgnore.`);
|
|
448
423
|
}
|
|
449
424
|
if (options.report !== undefined) {
|
|
450
|
-
await writePerPatchLintReport(options.report, subset, results);
|
|
425
|
+
await writePerPatchLintReport(options.report, subset, results, config, queueIssues);
|
|
451
426
|
}
|
|
452
427
|
reportPerPatchOutcome(issues, linted, skipped, options, heldNotices);
|
|
453
428
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* cold" via `probeCheckJsGlobalIssues` instead of building anything.
|
|
22
22
|
*/
|
|
23
23
|
import { invokePatchLintCheckJsGrouped, probeCheckJsGlobalIssues, runCheckJsTestFilesGrouped, } from '../core/patch-lint-checkjs.js';
|
|
24
|
-
import { isTestScriptFile, resolvePatchOwnedSysMjs, resolvePatchOwnedTestScripts, } from '../core/patch-lint-ownership.js';
|
|
24
|
+
import { isTestScriptFile, resolvableModulesForTests, resolvePatchOwnedSysMjs, resolvePatchOwnedTestScripts, } from '../core/patch-lint-ownership.js';
|
|
25
25
|
/**
|
|
26
26
|
* Builds the per-run checkJs program controller when `patchLint.checkJs` is
|
|
27
27
|
* enabled, or returns undefined. With `rootScopePatches` set (the
|
|
@@ -69,7 +69,7 @@ export function buildPerRunCheckJs(projectRoot, paths, config, ctx, rootScopePat
|
|
|
69
69
|
const sys = await invokePatchLintCheckJsGrouped(paths.engine, resolvePatchOwnedSysMjs(new Set(), ctx), patchLint, projectRoot, scopedRoots);
|
|
70
70
|
if (!testFilesEnabled)
|
|
71
71
|
return sys;
|
|
72
|
-
const tests = await runCheckJsTestFilesGrouped(paths.engine, resolvePatchOwnedTestScripts(new Set(), ctx), patchLint, projectRoot, scopedRoots);
|
|
72
|
+
const tests = await runCheckJsTestFilesGrouped(paths.engine, resolvePatchOwnedTestScripts(new Set(), ctx), patchLint, projectRoot, scopedRoots, resolvableModulesForTests(ctx));
|
|
73
73
|
const byFile = new Map(sys.byFile);
|
|
74
74
|
for (const [rel, list] of tests.byFile) {
|
|
75
75
|
byFile.set(rel, [...(byFile.get(rel) ?? []), ...list]);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PatchLintIssue, PatchMetadata } from '../types/commands/index.js';
|
|
2
|
+
import type { FireForgeConfig } from '../types/config.js';
|
|
2
3
|
/**
|
|
3
4
|
* Structural view of one per-patch lint outcome. Mirrors the relevant
|
|
4
5
|
* fields of lint-per-patch's QueuedPatchResult without importing it:
|
|
@@ -14,8 +15,11 @@ export interface PerPatchReportResult {
|
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
17
|
* Writes the per-patch lint report JSON. Called after the per-patch
|
|
17
|
-
* results are assembled in patch order.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* results are assembled in patch order. Policy findings are
|
|
19
|
+
* patch-attributed by their `filename` field upstream and are not
|
|
20
|
+
* duplicated here. Cross-patch findings (`forward-import`,
|
|
21
|
+
* `forward-registration`, …) go in `queueIssues`, with the
|
|
22
|
+
* `stagedDeclarations` that discharge them, so the report can be handed to
|
|
23
|
+
* `patch staged-dependency --from` as is.
|
|
20
24
|
*/
|
|
21
|
-
export declare function writePerPatchLintReport(reportPath: string, subset: readonly PatchMetadata[], results: readonly PerPatchReportResult[]): Promise<void>;
|
|
25
|
+
export declare function writePerPatchLintReport(reportPath: string, subset: readonly PatchMetadata[], results: readonly PerPatchReportResult[], config: Pick<FireForgeConfig, 'patchLint'>, queueIssues?: readonly PatchLintIssue[]): Promise<void>;
|
|
@@ -10,16 +10,21 @@
|
|
|
10
10
|
* none is wanted.
|
|
11
11
|
*/
|
|
12
12
|
import { getPatchSizeThresholds, resolvePatchSizeTier, } from '../core/patch-lint.js';
|
|
13
|
+
import { evaluateLintIgnoreCeilings, evaluateLintIgnoreWaivers, } from '../core/patch-lint-ignore.js';
|
|
13
14
|
import { writeJson } from '../utils/fs.js';
|
|
14
15
|
import { info } from '../utils/logger.js';
|
|
15
16
|
const LINT_REPORT_SCHEMA_VERSION = 1;
|
|
16
17
|
/**
|
|
17
18
|
* Writes the per-patch lint report JSON. Called after the per-patch
|
|
18
|
-
* results are assembled in patch order.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* results are assembled in patch order. Policy findings are
|
|
20
|
+
* patch-attributed by their `filename` field upstream and are not
|
|
21
|
+
* duplicated here. Cross-patch findings (`forward-import`,
|
|
22
|
+
* `forward-registration`, …) go in `queueIssues`, with the
|
|
23
|
+
* `stagedDeclarations` that discharge them, so the report can be handed to
|
|
24
|
+
* `patch staged-dependency --from` as is.
|
|
21
25
|
*/
|
|
22
|
-
export async function writePerPatchLintReport(reportPath, subset, results) {
|
|
26
|
+
export async function writePerPatchLintReport(reportPath, subset, results, config, queueIssues = []) {
|
|
27
|
+
const metric = config.patchLint?.patchSizeMetric ?? 'diff';
|
|
23
28
|
const patches = [];
|
|
24
29
|
let errors = 0;
|
|
25
30
|
let warnings = 0;
|
|
@@ -32,16 +37,25 @@ export async function writePerPatchLintReport(reportPath, subset, results) {
|
|
|
32
37
|
if (!patch || !result)
|
|
33
38
|
continue;
|
|
34
39
|
const tier = resolvePatchSizeTier(result.existingFiles, patch.tier);
|
|
40
|
+
const findings = [...result.rawIssues, ...result.suppressedIssues];
|
|
41
|
+
const measurements = { lines: result.lineCount, files: patch.filesAffected.length };
|
|
35
42
|
patches.push({
|
|
36
43
|
filename: patch.filename,
|
|
37
44
|
name: patch.name,
|
|
38
45
|
status: result.status,
|
|
46
|
+
metric,
|
|
39
47
|
lineCount: result.lineCount,
|
|
40
48
|
filesAffected: patch.filesAffected.length,
|
|
41
49
|
tier,
|
|
42
|
-
thresholds: getPatchSizeThresholds(tier.tier),
|
|
50
|
+
thresholds: getPatchSizeThresholds(tier.tier, config),
|
|
43
51
|
issues: result.rawIssues,
|
|
44
52
|
suppressedIssues: result.suppressedIssues,
|
|
53
|
+
lintIgnoreCeilings: result.status === 'skipped'
|
|
54
|
+
? []
|
|
55
|
+
: evaluateLintIgnoreCeilings(patch.lintIgnore, findings, measurements),
|
|
56
|
+
lintIgnoreWaivers: result.status === 'skipped'
|
|
57
|
+
? []
|
|
58
|
+
: evaluateLintIgnoreWaivers(patch.lintIgnore, findings, measurements),
|
|
45
59
|
});
|
|
46
60
|
if (result.status === 'skipped')
|
|
47
61
|
skipped++;
|
|
@@ -55,6 +69,7 @@ export async function writePerPatchLintReport(reportPath, subset, results) {
|
|
|
55
69
|
schemaVersion: LINT_REPORT_SCHEMA_VERSION,
|
|
56
70
|
generatedAt: new Date().toISOString(),
|
|
57
71
|
patches,
|
|
72
|
+
queueIssues,
|
|
58
73
|
totals: { linted, skipped, errors, warnings, suppressed },
|
|
59
74
|
});
|
|
60
75
|
info(`Per-patch lint report written to ${reportPath}.`);
|