@automaton-labs/aib 0.0.7 → 0.0.8
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/README.md +36 -257
- package/config/agent-dx-policy.default.json +31 -0
- package/config/control-plane-client.default.json +3 -0
- package/dist/agent-dx/agent-dx.js +805 -0
- package/dist/agent-dx/presentation.js +32 -0
- package/dist/alias-advisor/analyze.js +982 -0
- package/dist/alias-advisor/exact-text-cost.js +74 -0
- package/dist/alias-advisor/lexical.js +240 -0
- package/dist/alias-advisor/model.js +2 -0
- package/dist/alias-advisor/path-identity.js +49 -0
- package/dist/alias-advisor/ranking.js +171 -0
- package/dist/alias-advisor/token-estimator.js +85 -0
- package/dist/bin/aib.js +5 -1
- package/dist/bin/cli.js +811 -4
- package/dist/commands/add-missing-imports-preview-cache.js +99 -2
- package/dist/commands/add-missing-imports.js +241 -2
- package/dist/commands/apply-module-plan.js +1319 -3
- package/dist/commands/background-command.js +378 -2
- package/dist/commands/captured-input.js +74 -1
- package/dist/commands/config-command.js +314 -2
- package/dist/commands/config-output.js +357 -10
- package/dist/commands/control-plane-command.js +183 -1
- package/dist/commands/declaration-command.js +1209 -7
- package/dist/commands/declaration-execution-cache.js +79 -1
- package/dist/commands/declaration-format.js +221 -1
- package/dist/commands/declaration-preview-cache.js +114 -2
- package/dist/commands/diagnostics-command.js +70 -3
- package/dist/commands/doctor.js +137 -3
- package/dist/commands/execution-command.js +117 -1
- package/dist/commands/exports-star-collapse-command.js +178 -0
- package/dist/commands/exports-star-command.js +252 -0
- package/dist/commands/exports-star-preview-cache.js +84 -0
- package/dist/commands/feedback-command.js +176 -3
- package/dist/commands/file-move-to-dir-execution-cache.js +92 -1
- package/dist/commands/file-move-to-dir-preview-cache.js +125 -2
- package/dist/commands/file-move-to-dir-worker.js +54 -0
- package/dist/commands/file-move-to-dir.js +1073 -5
- package/dist/commands/file-mutation-runtime.js +102 -1
- package/dist/commands/file-mutation-worker-apply.js +180 -0
- package/dist/commands/file-refactor-batch-execution-cache.js +90 -1
- package/dist/commands/file-refactor-batch-preview-cache.js +125 -2
- package/dist/commands/file-refactor-batch-worker.js +54 -0
- package/dist/commands/file-refactor-batch.js +1154 -5
- package/dist/commands/file-rename-execution-cache.js +92 -1
- package/dist/commands/file-rename-preview-cache.js +125 -2
- package/dist/commands/file-rename-worker.js +54 -0
- package/dist/commands/file-rename.js +1053 -5
- package/dist/commands/file-text-edit-executor.js +205 -0
- package/dist/commands/find-importers.js +52 -1
- package/dist/commands/find-unused-imports.js +55 -1
- package/dist/commands/generate-docs-command.js +244 -6
- package/dist/commands/help-command.js +460 -7
- package/dist/commands/import-specifier-policy.js +391 -0
- package/dist/commands/imports-command.js +68 -1
- package/dist/commands/imports-rebase.js +1138 -0
- package/dist/commands/init-skill-usage.js +92 -3
- package/dist/commands/init-workspace.js +30 -1
- package/dist/commands/inspect-alias-candidates.js +413 -0
- package/dist/commands/inspect-cycles.js +811 -5
- package/dist/commands/inspect-direct-usages.js +98 -0
- package/dist/commands/inspect-duplicates-format.js +229 -0
- package/dist/commands/inspect-format-primitives.js +127 -0
- package/dist/commands/inspect-format.js +1590 -18
- package/dist/commands/inspect-graph.js +1553 -9
- package/dist/commands/inspect-imports.js +339 -2
- package/dist/commands/inspect-mixed-format.js +524 -0
- package/dist/commands/inspect-module-plan-format.js +216 -0
- package/dist/commands/inspect-module-plan-serialization.js +402 -0
- package/dist/commands/inspect-reference-path-format.js +157 -0
- package/dist/commands/inspect-request-values.js +599 -0
- package/dist/commands/inspect-selector-kind.js +21 -0
- package/dist/commands/inspect-selector.js +50 -0
- package/dist/commands/inspect-symbol-format.js +90 -0
- package/dist/commands/inspect-topology.js +435 -0
- package/dist/commands/inspect-tree.js +827 -5
- package/dist/commands/inspect-usages-detail.js +793 -0
- package/dist/commands/inspect-usages-evidence.js +31 -0
- package/dist/commands/inspect-usages-format.js +367 -0
- package/dist/commands/inspect.js +1450 -16
- package/dist/commands/internal-command.js +64 -0
- package/dist/commands/json-file-input.js +60 -1
- package/dist/commands/module-plan-cache.js +156 -2
- package/dist/commands/module-plan-standalone.js +208 -0
- package/dist/commands/module-plan-timeout.js +77 -1
- package/dist/commands/move-preview-cache.js +101 -2
- package/dist/commands/mutate-aggregate-output.js +996 -0
- package/dist/commands/mutate-child-execution.js +374 -0
- package/dist/commands/mutate-command.js +1394 -0
- package/dist/commands/mutate-declaration-output.js +313 -0
- package/dist/commands/mutate-domain-adapters.js +638 -0
- package/dist/commands/mutate-execution-cache.js +114 -0
- package/dist/commands/mutate-execution-status.js +598 -0
- package/dist/commands/mutate-failure-output.js +531 -0
- package/dist/commands/mutate-file-view.js +151 -0
- package/dist/commands/mutate-object-output.js +91 -0
- package/dist/commands/mutate-output-values.js +97 -0
- package/dist/commands/mutate-preview-cache.js +86 -0
- package/dist/commands/mutate-response-replay.js +153 -0
- package/dist/commands/mutate-result-projection.js +818 -0
- package/dist/commands/mutation-eta.js +28 -0
- package/dist/commands/mutation-execution-cache.js +140 -3
- package/dist/commands/mutation-recovery-journal.js +195 -0
- package/dist/commands/normalize-imports.js +1120 -4
- package/dist/commands/object-pack-command.js +546 -0
- package/dist/commands/object-pack-preview-cache.js +88 -0
- package/dist/commands/observability-command.js +47 -3
- package/dist/commands/ota-command.js +403 -2
- package/dist/commands/print-config.js +10 -1
- package/dist/commands/quick-read.js +1113 -11
- package/dist/commands/refactor-batch-builder.js +265 -1
- package/dist/commands/refactor-batch-execution-cache.js +85 -1
- package/dist/commands/refactor-batch-preview-cache.js +96 -2
- package/dist/commands/refactor-batch.js +461 -4
- package/dist/commands/rename-entities.js +794 -4
- package/dist/commands/rename-execution-cache.js +49 -1
- package/dist/commands/rename-input.js +22 -1
- package/dist/commands/rename-polling-policy.js +68 -0
- package/dist/commands/rename-preview-cache.js +96 -2
- package/dist/commands/result-view.js +993 -7
- package/dist/commands/reveal-command.js +445 -5
- package/dist/commands/selector-suggestions.js +93 -0
- package/dist/commands/session-workspace.js +1836 -31
- package/dist/commands/shared.js +2 -1
- package/dist/commands/skills-command.js +315 -0
- package/dist/commands/sync-command.js +59 -2
- package/dist/commands/worker-command.js +173 -1
- package/dist/commands/workspace-cache.js +55 -1
- package/dist/config/defaults.js +26 -1
- package/dist/config/env-vars.js +77 -1
- package/dist/config/glob-match.js +44 -1
- package/dist/config/import-rules.js +165 -1
- package/dist/config/local-config.js +293 -3
- package/dist/config/mutation-comments.js +51 -1
- package/dist/config/path-aliases.js +165 -1
- package/dist/config/path-display.js +84 -1
- package/dist/config/product-storage.js +37 -1
- package/dist/config/request-env.js +21 -0
- package/dist/config/request-routing-context.js +23 -0
- package/dist/config/resolve-command-alias.js +74 -1
- package/dist/config/resolve.js +24 -1
- package/dist/config/tsconfig-discovery.js +113 -1
- package/dist/config/typescript-preferences.js +131 -0
- package/dist/config/workspace-root.js +310 -3
- package/dist/config/workspace-state.js +254 -1
- package/dist/content/content-bundle.js +88 -1
- package/dist/diagnostics/module-plan-timeline.js +75 -2
- package/dist/diagnostics/readiness-text.js +45 -8
- package/dist/dsl/aib-dsl.js +838 -1
- package/dist/dsl/declaration-dsl.js +264 -1
- package/dist/dsl/file-move-to-dir-dsl.js +123 -1
- package/dist/dsl/file-refactor-batch-dsl.js +203 -1
- package/dist/dsl/file-rename-dsl.js +135 -1
- package/dist/dsl/mutate-dsl.js +746 -0
- package/dist/help/bootstrap.bash.md +176 -341
- package/dist/help/bootstrap.md +175 -340
- package/dist/help/bootstrap.pwsh.md +176 -341
- package/dist/help/docs/basics.md +1 -1
- package/dist/help/docs/config-setup.import-normalize.md +65 -65
- package/dist/help/docs/config-setup.import-rules.md +6 -5
- package/dist/help/docs/first-setup.md +6 -5
- package/dist/help/docs/help-protocol.md +4 -3
- package/dist/help/docs/import.base.md +38 -0
- package/dist/help/docs/import.rebase.md +85 -0
- package/dist/help/docs/inspect.aliasCandidates.md +39 -0
- package/dist/help/docs/inspect.code.md +1 -7
- package/dist/help/docs/inspect.file.md +10 -19
- package/dist/help/docs/inspect.graph.md +3 -17
- package/dist/help/{dsl/docs/inspect.md → docs/inspect.shared.md} +13 -12
- package/dist/help/docs/modulePlan.md +24 -28
- package/dist/help/docs/move.md +19 -22
- package/dist/help/{dsl/topics/file.move.md → docs/moveToDir.md} +36 -34
- package/dist/help/docs/mutate.mixed.md +34 -0
- package/dist/help/docs/mutation.shared.md +55 -0
- package/dist/help/docs/patterns.md +3 -12
- package/dist/help/docs/qr.md +12 -6
- package/dist/help/docs/quick-map.md +36 -24
- package/dist/help/docs/recipe.file-layout.md +10 -10
- package/dist/help/docs/recipe.import-boundary.md +92 -0
- package/dist/help/docs/recipe.safe-artifact-probing.md +68 -0
- package/dist/help/docs/recipe.unfamiliar-area.md +64 -0
- package/dist/help/docs/recipes.md +20 -0
- package/dist/help/docs/rename.md +19 -27
- package/dist/help/docs/renameFile.md +36 -0
- package/dist/help/docs/rules.md +26 -0
- package/dist/help/dsl/bootstrap.bash.md +176 -341
- package/dist/help/dsl/bootstrap.md +175 -340
- package/dist/help/dsl/bootstrap.pwsh.md +176 -341
- package/dist/help/dsl/docs/basics.md +1 -1
- package/dist/help/dsl/docs/config-setup.import-normalize.md +65 -65
- package/dist/help/dsl/docs/config-setup.import-rules.md +6 -5
- package/dist/help/dsl/docs/first-setup.md +6 -5
- package/dist/help/dsl/docs/help-protocol.md +4 -3
- package/dist/help/dsl/docs/import.base.md +38 -0
- package/dist/help/dsl/docs/import.rebase.md +85 -0
- package/dist/help/dsl/docs/inspect.aliasCandidates.md +39 -0
- package/dist/help/dsl/docs/inspect.code.md +1 -7
- package/dist/help/dsl/docs/inspect.file.md +10 -19
- package/dist/help/dsl/docs/inspect.graph.md +3 -17
- package/dist/help/{docs/inspect.md → dsl/docs/inspect.shared.md} +13 -12
- package/dist/help/dsl/docs/modulePlan.md +24 -28
- package/dist/help/dsl/docs/move.md +19 -22
- package/dist/help/{topics/file.move.md → dsl/docs/moveToDir.md} +36 -34
- package/dist/help/dsl/docs/mutate.mixed.md +34 -0
- package/dist/help/dsl/docs/mutation.shared.md +55 -0
- package/dist/help/dsl/docs/patterns.md +3 -12
- package/dist/help/dsl/docs/qr.md +12 -6
- package/dist/help/dsl/docs/quick-map.md +36 -24
- package/dist/help/dsl/docs/recipe.file-layout.md +10 -10
- package/dist/help/dsl/docs/recipe.import-boundary.md +92 -0
- package/dist/help/dsl/docs/recipe.safe-artifact-probing.md +68 -0
- package/dist/help/dsl/docs/recipe.unfamiliar-area.md +64 -0
- package/dist/help/dsl/docs/recipes.md +20 -0
- package/dist/help/dsl/docs/rename.md +19 -27
- package/dist/help/dsl/docs/renameFile.md +36 -0
- package/dist/help/dsl/docs/rules.md +26 -0
- package/dist/help/dsl/full.md +175 -340
- package/dist/help/dsl/help-index.md +48 -43
- package/dist/help/dsl/snippets/agents.md +2 -2
- package/dist/help/dsl/tokens/qr.inline.examples.md +1 -1
- package/dist/help/dsl/topics/basics.md +1 -1
- package/dist/help/dsl/topics/config-setup.import-normalize.md +65 -65
- package/dist/help/dsl/topics/config-setup.import-rules.md +6 -5
- package/dist/help/dsl/topics/first-setup.md +6 -5
- package/dist/help/dsl/topics/help-protocol.md +4 -3
- package/dist/help/dsl/topics/import.base.md +36 -0
- package/dist/help/dsl/topics/import.rebase.md +83 -0
- package/dist/help/dsl/topics/inspect.aliasCandidates.md +37 -0
- package/dist/help/dsl/topics/inspect.code.md +1 -7
- package/dist/help/dsl/topics/inspect.file.md +10 -19
- package/dist/help/dsl/topics/inspect.graph.md +3 -17
- package/dist/help/dsl/topics/{inspect.md → inspect.shared.md} +13 -12
- package/dist/help/dsl/topics/modulePlan.md +21 -26
- package/dist/help/dsl/topics/move.md +17 -18
- package/dist/help/{docs/file.move.md → dsl/topics/moveToDir.md} +33 -35
- package/dist/help/dsl/topics/mutate.mixed.md +32 -0
- package/dist/help/dsl/topics/mutation.shared.md +53 -0
- package/dist/help/dsl/topics/patterns.md +3 -12
- package/dist/help/dsl/topics/qr.md +12 -6
- package/dist/help/dsl/topics/quick-map.md +36 -24
- package/dist/help/dsl/topics/recipe.file-layout.md +10 -10
- package/dist/help/dsl/topics/recipe.import-boundary.md +90 -0
- package/dist/help/dsl/topics/recipe.safe-artifact-probing.md +66 -0
- package/dist/help/dsl/topics/recipe.unfamiliar-area.md +62 -0
- package/dist/help/dsl/topics/recipes.md +18 -0
- package/dist/help/dsl/topics/rename.md +15 -15
- package/dist/help/dsl/topics/renameFile.md +34 -0
- package/dist/help/dsl/topics/rules.md +24 -0
- package/dist/help/full.md +175 -340
- package/dist/help/help-index.md +48 -43
- package/dist/help/help-meta.json +98 -48
- package/dist/help/index.md +52 -20
- package/dist/help/snippets/agents.md +2 -2
- package/dist/help/tokens/qr.inline.examples.md +1 -1
- package/dist/help/topics/basics.md +1 -1
- package/dist/help/topics/config-setup.import-normalize.md +65 -65
- package/dist/help/topics/config-setup.import-rules.md +6 -5
- package/dist/help/topics/first-setup.md +6 -5
- package/dist/help/topics/help-protocol.md +4 -3
- package/dist/help/topics/import.base.md +36 -0
- package/dist/help/topics/import.rebase.md +83 -0
- package/dist/help/topics/inspect.aliasCandidates.md +37 -0
- package/dist/help/topics/inspect.code.md +1 -7
- package/dist/help/topics/inspect.file.md +10 -19
- package/dist/help/topics/inspect.graph.md +3 -17
- package/dist/help/topics/{inspect.md → inspect.shared.md} +13 -12
- package/dist/help/topics/modulePlan.md +21 -26
- package/dist/help/topics/move.md +17 -18
- package/dist/help/{dsl/docs/file.move.md → topics/moveToDir.md} +33 -35
- package/dist/help/topics/mutate.mixed.md +32 -0
- package/dist/help/topics/mutation.shared.md +53 -0
- package/dist/help/topics/patterns.md +3 -12
- package/dist/help/topics/qr.md +12 -6
- package/dist/help/topics/quick-map.md +36 -24
- package/dist/help/topics/recipe.file-layout.md +10 -10
- package/dist/help/topics/recipe.import-boundary.md +90 -0
- package/dist/help/topics/recipe.safe-artifact-probing.md +66 -0
- package/dist/help/topics/recipe.unfamiliar-area.md +62 -0
- package/dist/help/topics/recipes.md +18 -0
- package/dist/help/topics/rename.md +15 -15
- package/dist/help/topics/renameFile.md +34 -0
- package/dist/help/topics/rules.md +24 -0
- package/dist/host/capabilities.js +56 -1
- package/dist/imports/standalone-import-analysis.js +602 -0
- package/dist/imports/standalone-import-model.js +2 -0
- package/dist/imports/standalone-import-process.js +66 -0
- package/dist/imports/standalone-import-worker.js +27 -0
- package/dist/imports/standalone-ts-project.js +147 -0
- package/dist/inspect/inspect-duplicates-service.js +526 -0
- package/dist/inspect/inspect-reverse-module-closure.js +338 -0
- package/dist/inspect/inspect-source-ranges.js +82 -0
- package/dist/inspect/inspect-source-shape.js +649 -0
- package/dist/inspect/standalone-inspect-analysis.js +319 -0
- package/dist/inspect/standalone-inspect-model.js +9 -0
- package/dist/inspect/standalone-inspect-process.js +55 -0
- package/dist/inspect/standalone-inspect-usages.js +274 -0
- package/dist/inspect/standalone-inspect-worker.js +13 -0
- package/dist/metrics/central-metrics.js +101 -2
- package/dist/mutation/concrete-edit-plan.js +157 -0
- package/dist/mutation/exports-star-collapse-preview.js +361 -0
- package/dist/mutation/exports-star-expand-apply.js +373 -0
- package/dist/mutation/exports-star-expand-preview.js +807 -0
- package/dist/mutation/file-impact-scan.js +182 -0
- package/dist/mutation/file-mutation-discovery.js +674 -0
- package/dist/mutation/file-mutation-eta.js +99 -0
- package/dist/mutation/file-mutation-standalone-apply.js +584 -0
- package/dist/mutation/file-mutation-standalone-planner.js +500 -0
- package/dist/mutation/file-mutation-tsserver-session.js +473 -0
- package/dist/mutation/mutation-estimate.js +45 -0
- package/dist/mutation/mutation-plan.js +467 -0
- package/dist/mutation/object-mutation-foundation.js +2 -0
- package/dist/mutation/object-mutation-partition.js +148 -0
- package/dist/mutation/object-pack-semantic-apply.js +507 -0
- package/dist/mutation/object-pack-semantic-preview.js +984 -0
- package/dist/mutation/object-unpack-semantic-preview.js +993 -0
- package/dist/mutation/package-manifest-impact.js +176 -0
- package/dist/mutation/rename-owner-worker-child.js +37 -0
- package/dist/mutation/rename-owner-worker.js +122 -0
- package/dist/mutation/runtime-cycle-query.js +66 -0
- package/dist/mutation/semantic-execution-progress.js +63 -0
- package/dist/mutation/semantic-export-index.js +474 -0
- package/dist/mutation/semantic-impact-scan.js +434 -0
- package/dist/mutation/semantic-model.js +2 -0
- package/dist/mutation/semantic-move-best-effort-planner.js +209 -0
- package/dist/mutation/semantic-move-config-worker-child.js +61 -0
- package/dist/mutation/semantic-move-config-worker.js +449 -0
- package/dist/mutation/semantic-move-declarations.js +94 -0
- package/dist/mutation/semantic-move-metrics.js +119 -0
- package/dist/mutation/semantic-move-model.js +2 -0
- package/dist/mutation/semantic-move-owner-authority.js +122 -0
- package/dist/mutation/semantic-move-owner-candidates.js +300 -0
- package/dist/mutation/semantic-move-owner-divergence-harness.js +474 -0
- package/dist/mutation/semantic-move-owner-divergence-runner.js +162 -0
- package/dist/mutation/semantic-move-plan-hash.js +56 -0
- package/dist/mutation/semantic-move-policy.js +360 -0
- package/dist/mutation/semantic-move-process.js +218 -0
- package/dist/mutation/semantic-move-provider-diagnostics.js +27 -0
- package/dist/mutation/semantic-move-standalone-engine.js +919 -0
- package/dist/mutation/semantic-move-standalone-planner.js +1553 -0
- package/dist/mutation/semantic-move-transaction.js +639 -0
- package/dist/mutation/semantic-mutation-engine.js +478 -0
- package/dist/mutation/semantic-mutation-runtime.js +521 -0
- package/dist/mutation/semantic-planner-retry.js +26 -0
- package/dist/mutation/semantic-program-roots.js +69 -0
- package/dist/mutation/semantic-rename-commonjs.js +229 -0
- package/dist/mutation/semantic-rename-config-worker-child.js +46 -0
- package/dist/mutation/semantic-rename-config-worker.js +498 -0
- package/dist/mutation/semantic-rename-locations.js +192 -0
- package/dist/mutation/semantic-rename-model.js +2 -0
- package/dist/mutation/semantic-rename-process.js +210 -0
- package/dist/mutation/semantic-rename-standalone-engine.js +302 -0
- package/dist/mutation/semantic-rename-standalone-planner.js +567 -0
- package/dist/mutation/semantic-rename-transaction.js +333 -0
- package/dist/mutation/semantic-unused-import-cleanup.js +248 -0
- package/dist/mutation/semantic-worker-retry.js +40 -0
- package/dist/mutation/semantic-workspace.js +804 -0
- package/dist/mutation/targeted-import-canonicalizer.js +359 -0
- package/dist/observability/config.js +206 -1
- package/dist/observability/context.js +59 -1
- package/dist/observability/failure-snapshot.js +124 -2
- package/dist/observability/recent.js +63 -2
- package/dist/payloads/read-stdin-json.js +212 -1
- package/dist/rg-wrapper/core.js +714 -6
- package/dist/rg-wrapper/options.js +125 -1
- package/dist/rtk-wrapper/core.js +144 -2
- package/dist/rtk-wrapper/git-safety.js +203 -0
- package/dist/rtk-wrapper/output-adapter.js +106 -0
- package/dist/runtime/buffered-process.js +114 -0
- package/dist/runtime/bundled-node.js +97 -1
- package/dist/runtime/command-lifecycle.js +2 -0
- package/dist/runtime/input-source.js +238 -1
- package/dist/runtime/node-cli-identity.js +37 -0
- package/dist/runtime/run-command.js +191 -1
- package/dist/selectors/parse-entities.js +84 -1
- package/dist/semantic/semantic-context-pool.js +830 -0
- package/dist/semantic/semantic-context-provider.js +416 -0
- package/dist/semantic/semantic-resident-admission.js +38 -0
- package/dist/semantic/semantic-worker-child.js +40 -0
- package/dist/semantic/semantic-worker-manager.js +460 -0
- package/dist/semantic/semantic-worker-protocol.js +2 -0
- package/dist/semantic/semantic-worker-runtime.js +38 -0
- package/dist/semantic/workspace-semantic-context-pools.js +136 -0
- package/dist/session/client.js +359 -1
- package/dist/session/daemon-state.js +185 -2
- package/dist/session/external-tool-coordinator.js +88 -0
- package/dist/session/external-tool-request.js +52 -0
- package/dist/session/lane-owner.js +116 -0
- package/dist/session/paths.js +71 -1
- package/dist/session/runtime-lifecycle.js +225 -0
- package/dist/session/runtime-shutdown.js +95 -0
- package/dist/session/server.js +1194 -7
- package/dist/session/session-health.js +27 -0
- package/dist/shared/agent-text.js +29 -1
- package/dist/shared/errors.js +496 -30
- package/dist/shared/event-loop.js +6 -1
- package/dist/shared/hints.js +37 -1
- package/dist/shared/metrics.js +43 -1
- package/dist/shared/presentation.js +69 -4
- package/dist/shared/stdin-examples.js +77 -1
- package/dist/shared/stdout.js +6 -2
- package/dist/shared/types.js +23 -1
- package/dist/tool-runtime/runtime-tools.js +120 -1
- package/dist/topology/availability.js +663 -0
- package/dist/topology/cache.js +130 -0
- package/dist/topology/candidates.js +372 -0
- package/dist/topology/core.js +903 -0
- package/dist/topology/dependency-snapshot.js +189 -0
- package/dist/topology/model.js +2 -0
- package/dist/topology/scope.js +100 -0
- package/dist/topology/ts-oracle-batch.js +529 -0
- package/dist/topology/ts-oracle-host.js +69 -0
- package/dist/topology/ts-oracle-model.js +2 -0
- package/dist/topology/ts-oracle.js +224 -0
- package/dist/topology/tsconfig-loader.js +105 -0
- package/dist/topology/workspace-project-index.js +542 -0
- package/dist/tracing/config.js +192 -2
- package/dist/tracing/failure-summary.js +100 -0
- package/dist/tracing/trace.js +318 -5
- package/dist/tsc-wrapper/core.js +697 -0
- package/dist/workers/control-plane/aib-control-plane-worker.cjs +32 -32
- package/dist/workers/control-plane/client-config.default.json +3 -3
- package/dist/workers/lexical-worker-child.js +27 -0
- package/dist/workers/lexical-worker-manager.js +394 -0
- package/dist/workers/lexical-worker-protocol.js +2 -0
- package/dist/workers/lexical-worker-runtime.js +25 -0
- package/dist/workers/owned-one-shot-process.js +115 -0
- package/dist/workers/product-storage.js +23 -1
- package/dist/workers/registry.js +241 -1
- package/package.json +8 -13
- package/scripts/postinstall.cjs +29 -29
- package/scripts/provision-runtime.cjs +411 -459
- package/dist/client/http-client.js +0 -1
- package/dist/client/transport-options.js +0 -1
- package/dist/client/websocket-client.js +0 -1
- package/dist/commands/entity-resolution.js +0 -1
- package/dist/commands/file-command.js +0 -1
- package/dist/commands/host-command.js +0 -1
- package/dist/commands/inspect-symbol-query.js +0 -1
- package/dist/commands/list-instances.js +0 -1
- package/dist/commands/module-plan-command.js +0 -1
- package/dist/commands/move-command.js +0 -1
- package/dist/commands/move-to-file.js +0 -2
- package/dist/commands/ping.js +0 -1
- package/dist/commands/refactor-command.js +0 -1
- package/dist/commands/rename-command.js +0 -1
- package/dist/commands/runtime-command.js +0 -4
- package/dist/commands/runtime-info.js +0 -1
- package/dist/commands/validate-module-plan.js +0 -2
- package/dist/compatibility/policy.js +0 -1
- package/dist/config/auto-start-ide.js +0 -1
- package/dist/discovery/registry.js +0 -1
- package/dist/discovery/select-instance.js +0 -1
- package/dist/help/docs/file.rename.md +0 -35
- package/dist/help/docs/mutation.md +0 -60
- package/dist/help/docs/refactor.batch.md +0 -44
- package/dist/help/dsl/docs/file.rename.md +0 -35
- package/dist/help/dsl/docs/mutation.md +0 -60
- package/dist/help/dsl/docs/refactor.batch.md +0 -44
- package/dist/help/dsl/topics/file.rename.md +0 -33
- package/dist/help/dsl/topics/mutation.md +0 -58
- package/dist/help/dsl/topics/refactor.batch.md +0 -33
- package/dist/help/topics/file.rename.md +0 -33
- package/dist/help/topics/mutation.md +0 -58
- package/dist/help/topics/refactor.batch.md +0 -33
- package/dist/ide-launch/common.cjs +0 -162
- package/dist/managed-host/extension-vsix-resolver.js +0 -1
- package/dist/managed-host/manage-serve-web-host.cjs +0 -141
- package/dist/managed-host/serve-web-autostart.js +0 -1
- package/dist/managed-host/serve-web-host.cjs +0 -2115
- package/dist/runtime/managed-runtime-provisioning.js +0 -1
- package/dist/shared/diagnostic-catalog.js +0 -1
- package/dist/shared/diagnostics.js +0 -1
- package/dist/shared/operations.js +0 -1
- package/dist/shared/protocol.js +0 -1
- package/dist/shared/routes.js +0 -1
- package/extension/vscode-refactor-bridge-extension.vsix +0 -0
- package/runtimes/launcher/linux-x64/aib +0 -0
- package/runtimes/launcher/win-x64/aib.exe +0 -0
|
@@ -0,0 +1,1553 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.planStandaloneSemanticMove = planStandaloneSemanticMove;
|
|
40
|
+
const crypto = __importStar(require("node:crypto"));
|
|
41
|
+
const fs = __importStar(require("node:fs"));
|
|
42
|
+
const path = __importStar(require("node:path"));
|
|
43
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
44
|
+
const file_mutation_discovery_1 = require("./file-mutation-discovery");
|
|
45
|
+
const semantic_move_plan_hash_1 = require("./semantic-move-plan-hash");
|
|
46
|
+
const package_manifest_impact_1 = require("./package-manifest-impact");
|
|
47
|
+
const semantic_move_process_1 = require("./semantic-move-process");
|
|
48
|
+
const semantic_move_declarations_1 = require("./semantic-move-declarations");
|
|
49
|
+
const import_rules_1 = require("../config/import-rules");
|
|
50
|
+
const import_specifier_policy_1 = require("../commands/import-specifier-policy");
|
|
51
|
+
const mutation_comments_1 = require("../config/mutation-comments");
|
|
52
|
+
const typescript_preferences_1 = require("../config/typescript-preferences");
|
|
53
|
+
const semantic_move_policy_1 = require("./semantic-move-policy");
|
|
54
|
+
const semantic_program_roots_1 = require("./semantic-program-roots");
|
|
55
|
+
const semantic_move_owner_authority_1 = require("./semantic-move-owner-authority");
|
|
56
|
+
const semantic_move_owner_candidates_1 = require("./semantic-move-owner-candidates");
|
|
57
|
+
const DEFAULT_DEADLINE_MS = 120_000;
|
|
58
|
+
const MAX_CONFIGS = 32;
|
|
59
|
+
async function planStandaloneSemanticMove(input, dependencies = {
|
|
60
|
+
discover: file_mutation_discovery_1.discoverFileMutationConsumers,
|
|
61
|
+
fingerprint: file_mutation_discovery_1.computeFileMutationWorkspaceFingerprint,
|
|
62
|
+
runConfig: semantic_move_process_1.runSemanticMoveConfigWorker
|
|
63
|
+
}) {
|
|
64
|
+
const startedAt = Date.now();
|
|
65
|
+
const root = path.resolve(input.workspaceRoot);
|
|
66
|
+
const discover = input.semanticContextPool
|
|
67
|
+
? (request) => (0, file_mutation_discovery_1.discoverFileMutationConsumersWithProjectIndexLease)(input.semanticContextPool, request)
|
|
68
|
+
: dependencies.discover;
|
|
69
|
+
const fingerprint = input.semanticContextPool
|
|
70
|
+
? (workspaceRoot) => input.semanticContextPool.withWorkspaceProjectIndex((index) => (0, file_mutation_discovery_1.computeFileMutationWorkspaceFingerprintWithProjectIndex)(index, workspaceRoot))
|
|
71
|
+
: dependencies.fingerprint;
|
|
72
|
+
const sourceFile = path.resolve(root, input.sourceFile);
|
|
73
|
+
const targetFile = path.resolve(root, input.targetFile);
|
|
74
|
+
const workspacePreferences = input.importRules?.length
|
|
75
|
+
? undefined
|
|
76
|
+
: (0, typescript_preferences_1.readWorkspaceTypeScriptPreferences)(root);
|
|
77
|
+
const emptyDiscovery = discoverySummary(null);
|
|
78
|
+
if (!isInside(root, sourceFile) || !isRegularFile(sourceFile)) {
|
|
79
|
+
return incomplete("SEMANTIC_MOVE_SOURCE_INVALID", "Move source is missing or outside the workspace.", root, sourceFile, targetFile, emptyDiscovery, startedAt);
|
|
80
|
+
}
|
|
81
|
+
if (!isInside(root, targetFile) || samePath(sourceFile, targetFile) || (fs.existsSync(targetFile) && !isRegularFile(targetFile))) {
|
|
82
|
+
return incomplete("SEMANTIC_MOVE_TARGET_INVALID", "Move target must be a regular file or an absent file inside the workspace.", root, sourceFile, targetFile, emptyDiscovery, startedAt);
|
|
83
|
+
}
|
|
84
|
+
let sourceBytes;
|
|
85
|
+
try {
|
|
86
|
+
sourceBytes = fs.readFileSync(sourceFile);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return incomplete("SEMANTIC_MOVE_SOURCE_UNREADABLE", errorMessage(error), root, sourceFile, targetFile, emptyDiscovery, startedAt);
|
|
90
|
+
}
|
|
91
|
+
const sourceText = sourceBytes.toString("utf8");
|
|
92
|
+
const targetExisted = fs.existsSync(targetFile);
|
|
93
|
+
let targetBytes;
|
|
94
|
+
try {
|
|
95
|
+
targetBytes = targetExisted ? fs.readFileSync(targetFile) : Buffer.alloc(0);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
return incomplete("SEMANTIC_MOVE_TARGET_UNREADABLE", errorMessage(error), root, sourceFile, targetFile, emptyDiscovery, startedAt);
|
|
99
|
+
}
|
|
100
|
+
const targetText = targetBytes.toString("utf8");
|
|
101
|
+
const selection = buildSelection(sourceFile, sourceText, input.entities);
|
|
102
|
+
if (!selection.ok) {
|
|
103
|
+
return {
|
|
104
|
+
...incomplete(selection.code, selection.error, root, sourceFile, targetFile, emptyDiscovery, startedAt),
|
|
105
|
+
...(selection.ambiguities ? { ambiguities: selection.ambiguities } : {}),
|
|
106
|
+
...(selection.contiguousEntityGroups ? { contiguousEntityGroups: selection.contiguousEntityGroups } : {})
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const targetDeclarations = (0, semantic_move_declarations_1.collectSemanticMoveDeclarations)(targetFile, targetText);
|
|
110
|
+
const duplicateTarget = selection.entities.find((entity) => targetDeclarations.some((candidate) => candidate.name === entity.name && (!entity.kind || candidate.kind === entity.kind)));
|
|
111
|
+
if (duplicateTarget) {
|
|
112
|
+
return incomplete("TARGET_SYMBOL_ALREADY_EXISTS", `Target already declares moved symbol: ${duplicateTarget.name}`, root, sourceFile, targetFile, emptyDiscovery, startedAt);
|
|
113
|
+
}
|
|
114
|
+
const mutationComments = input.mutationComments ?? mutation_comments_1.DEFAULT_MUTATION_COMMENTS_POLICY;
|
|
115
|
+
const topFileHeader = (0, semantic_move_policy_1.findDetachedTopFileHeader)(sourceFile, sourceText, selection.firstSelectedName);
|
|
116
|
+
if (topFileHeader && mutationComments.topFileHeader === "fail") {
|
|
117
|
+
return incomplete("TS_MOVE_TO_FILE_TOP_FILE_HEADER_BLOCKED", "Detached top-of-file header policy is configured to fail instead of preserving the header in source.", root, sourceFile, targetFile, emptyDiscovery, startedAt);
|
|
118
|
+
}
|
|
119
|
+
const preservedHeader = topFileHeader && mutationComments.topFileHeader === "preserveInSource" ? topFileHeader : null;
|
|
120
|
+
const workerSourceText = preservedHeader ? sourceText.slice(preservedHeader.end) : sourceText;
|
|
121
|
+
const workerSelection = preservedHeader ? buildSelection(sourceFile, workerSourceText, input.entities) : selection;
|
|
122
|
+
if (!workerSelection.ok) {
|
|
123
|
+
return {
|
|
124
|
+
...incomplete(workerSelection.code, workerSelection.error, root, sourceFile, targetFile, emptyDiscovery, startedAt),
|
|
125
|
+
...(workerSelection.ambiguities ? { ambiguities: workerSelection.ambiguities } : {})
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
let discovery;
|
|
129
|
+
const discoveryStartedAt = input.metrics?.start();
|
|
130
|
+
try {
|
|
131
|
+
discovery = input.discovery ?? discover({ workspaceRoot: root, sourceFiles: [sourceFile] });
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (discoveryStartedAt !== undefined)
|
|
135
|
+
input.metrics?.end("planner.discovery", discoveryStartedAt);
|
|
136
|
+
return incomplete("SEMANTIC_MOVE_DISCOVERY_FAILED", errorMessage(error), root, sourceFile, targetFile, emptyDiscovery, startedAt);
|
|
137
|
+
}
|
|
138
|
+
if (discoveryStartedAt !== undefined)
|
|
139
|
+
input.metrics?.end("planner.discovery", discoveryStartedAt);
|
|
140
|
+
const summary = discoverySummary(discovery);
|
|
141
|
+
if (!discovery.complete || !discovery.sourceFiles.some((file) => samePath(file, sourceFile))) {
|
|
142
|
+
return incomplete("SEMANTIC_MOVE_DISCOVERY_INCOMPLETE", discovery.uncertainties.map((item) => `${item.kind}:${item.file}:${item.moduleSpecifier}`).join("; ") || "Neutral module discovery is incomplete.", root, sourceFile, targetFile, summary, startedAt);
|
|
143
|
+
}
|
|
144
|
+
if (discovery.ownerConfigs.length === 0 || discovery.ownerConfigs.length > MAX_CONFIGS) {
|
|
145
|
+
return incomplete("SEMANTIC_MOVE_OWNER_SET_UNSUPPORTED", `Expected 1-${MAX_CONFIGS} exact owner configs, found ${discovery.ownerConfigs.length}.`, root, sourceFile, targetFile, summary, startedAt);
|
|
146
|
+
}
|
|
147
|
+
const sourceOwners = ownersForFile(discovery, sourceFile);
|
|
148
|
+
if (sourceOwners.length === 0) {
|
|
149
|
+
return incomplete("SEMANTIC_MOVE_SOURCE_OWNER_MISSING", "Neutral discovery did not provide an exact source owner.", root, sourceFile, targetFile, summary, startedAt);
|
|
150
|
+
}
|
|
151
|
+
const snapshotStartedAt = input.metrics?.start();
|
|
152
|
+
let initialFingerprint;
|
|
153
|
+
try {
|
|
154
|
+
initialFingerprint = fingerprint(root);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
if (snapshotStartedAt !== undefined)
|
|
158
|
+
input.metrics?.end("planner.snapshot", snapshotStartedAt);
|
|
159
|
+
return incomplete("SEMANTIC_MOVE_SNAPSHOT_UNREADABLE", errorMessage(error), root, sourceFile, targetFile, summary, startedAt);
|
|
160
|
+
}
|
|
161
|
+
if (initialFingerprint !== discovery.workspaceFingerprint) {
|
|
162
|
+
if (snapshotStartedAt !== undefined)
|
|
163
|
+
input.metrics?.end("planner.snapshot", snapshotStartedAt);
|
|
164
|
+
return incomplete("SEMANTIC_MOVE_DISCOVERY_STALE", "Workspace changed after neutral discovery.", root, sourceFile, targetFile, summary, startedAt);
|
|
165
|
+
}
|
|
166
|
+
let acceptedSourceBytes;
|
|
167
|
+
try {
|
|
168
|
+
acceptedSourceBytes = fs.readFileSync(sourceFile);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
if (snapshotStartedAt !== undefined)
|
|
172
|
+
input.metrics?.end("planner.snapshot", snapshotStartedAt);
|
|
173
|
+
return incomplete("SEMANTIC_MOVE_SOURCE_SNAPSHOT_CHANGED", errorMessage(error), root, sourceFile, targetFile, summary, startedAt);
|
|
174
|
+
}
|
|
175
|
+
if (!acceptedSourceBytes.equals(sourceBytes)) {
|
|
176
|
+
if (snapshotStartedAt !== undefined)
|
|
177
|
+
input.metrics?.end("planner.snapshot", snapshotStartedAt);
|
|
178
|
+
return incomplete("SEMANTIC_MOVE_SOURCE_SNAPSHOT_CHANGED", "Move source changed while neutral discovery was being computed.", root, sourceFile, targetFile, summary, startedAt);
|
|
179
|
+
}
|
|
180
|
+
const evidenceFiles = uniquePaths([sourceFile, ...(targetExisted ? [targetFile] : []), ...discovery.consumerFiles]);
|
|
181
|
+
const snapshotHash = hashFiles(evidenceFiles);
|
|
182
|
+
if (snapshotStartedAt !== undefined)
|
|
183
|
+
input.metrics?.end("planner.snapshot", snapshotStartedAt);
|
|
184
|
+
const workerPath = input.testing?.configWorkerPath ?? path.join(__dirname, "semantic-move-config-worker-child.js");
|
|
185
|
+
const results = [];
|
|
186
|
+
const workerEvidence = [];
|
|
187
|
+
const workersStartedAt = input.metrics?.start();
|
|
188
|
+
for (const configFilePath of discovery.ownerConfigs) {
|
|
189
|
+
const exportStarCandidates = discovery.edges
|
|
190
|
+
.filter((edge) => edge.kind === "exportStar" && samePath(edge.targetFile, sourceFile) &&
|
|
191
|
+
!samePath(edge.fromFile, sourceFile) && ownersForFile(discovery, edge.fromFile).some((owner) => samePath(owner, configFilePath)))
|
|
192
|
+
.map((edge) => ({
|
|
193
|
+
barrelFile: edge.fromFile,
|
|
194
|
+
moduleSpecifier: edge.moduleSpecifier,
|
|
195
|
+
specifierStart: edge.specifierStart,
|
|
196
|
+
entities: selection.entities.map((entity) => ({
|
|
197
|
+
name: entity.name,
|
|
198
|
+
...(entity.kind ? { kind: entity.kind } : {})
|
|
199
|
+
}))
|
|
200
|
+
}));
|
|
201
|
+
const request = {
|
|
202
|
+
type: "semanticMoveConfig",
|
|
203
|
+
id: crypto.randomUUID(),
|
|
204
|
+
workspaceRoot: root,
|
|
205
|
+
configFilePath: path.normalize(configFilePath),
|
|
206
|
+
sourceFile,
|
|
207
|
+
sourceText: workerSourceText,
|
|
208
|
+
targetFile,
|
|
209
|
+
targetText,
|
|
210
|
+
selection: workerSelection.selection,
|
|
211
|
+
preferences: (0, semantic_move_policy_1.semanticMovePreferences)(targetFile, input.importRules ?? [], input.preferences, workspacePreferences),
|
|
212
|
+
programRootFiles: (0, semantic_program_roots_1.focusedSemanticProgramRootFiles)({
|
|
213
|
+
discovery,
|
|
214
|
+
configFilePath,
|
|
215
|
+
requiredFiles: [sourceFile, targetFile]
|
|
216
|
+
}),
|
|
217
|
+
...(exportStarCandidates.length > 0 ? { exportStarCandidates } : {}),
|
|
218
|
+
...(import_rules_1.SEMANTIC_MOVE_UNUSED_IMPORT_CLEANUP_ENABLED && input.importRules?.some((rule) => rule.unusedImports === "remove")
|
|
219
|
+
? { unusedImportCleanupRules: [...input.importRules] }
|
|
220
|
+
: {}),
|
|
221
|
+
...(input.metrics ? { metricsEnabled: true } : {})
|
|
222
|
+
};
|
|
223
|
+
let outcome;
|
|
224
|
+
try {
|
|
225
|
+
outcome = await dependencies.runConfig({
|
|
226
|
+
modulePath: workerPath,
|
|
227
|
+
request,
|
|
228
|
+
deadlineMs: input.deadlineMs ?? DEFAULT_DEADLINE_MS
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
if (workersStartedAt !== undefined)
|
|
233
|
+
input.metrics?.end("planner.ownerWorkers", workersStartedAt);
|
|
234
|
+
return incomplete(readErrorCode(error) ?? "SEMANTIC_MOVE_CONFIG_WORKER_FAILED", errorMessage(error), root, sourceFile, targetFile, summary, startedAt, snapshotHash, workerEvidence);
|
|
235
|
+
}
|
|
236
|
+
workerEvidence.push({
|
|
237
|
+
configFilePath: outcome.result.configFilePath,
|
|
238
|
+
pid: outcome.result.pid,
|
|
239
|
+
rssMiB: outcome.result.rssMiB,
|
|
240
|
+
durationMs: outcome.durationMs,
|
|
241
|
+
terminationConfirmed: outcome.terminationConfirmed,
|
|
242
|
+
fileEditCount: outcome.result.edits.length,
|
|
243
|
+
textChangeCount: outcome.result.edits.reduce((sum, edit) => sum + edit.textChanges.length, 0)
|
|
244
|
+
});
|
|
245
|
+
input.metrics?.observeWorker({
|
|
246
|
+
...(outcome.metrics ?? {}),
|
|
247
|
+
...(outcome.result.metrics ?? {})
|
|
248
|
+
});
|
|
249
|
+
const acceptedResult = rebasePreservedHeaderResult(outcome.result, sourceFile, sourceText, workerSourceText, preservedHeader);
|
|
250
|
+
dependencies.observeConfigResult?.(acceptedResult);
|
|
251
|
+
if (!outcome.terminationConfirmed || !outcome.result.ok || !outcome.result.containsSource) {
|
|
252
|
+
if (workersStartedAt !== undefined)
|
|
253
|
+
input.metrics?.end("planner.ownerWorkers", workersStartedAt);
|
|
254
|
+
return incomplete(outcome.result.failureKind === "provider-unsupported"
|
|
255
|
+
? "SEMANTIC_MOVE_PROVIDER_UNSUPPORTED"
|
|
256
|
+
: outcome.result.failureKind === "changes-overlap"
|
|
257
|
+
? "SEMANTIC_MOVE_CHANGES_OVERLAP"
|
|
258
|
+
: outcome.result.failureKind === "provider-failed"
|
|
259
|
+
? "SEMANTIC_MOVE_PROVIDER_FAILED"
|
|
260
|
+
: "SEMANTIC_MOVE_OWNER_PROOF_FAILED", outcome.result.error ?? `Exact owner projection ${configFilePath} did not provide a move plan.`, root, sourceFile, targetFile, summary, startedAt, snapshotHash, workerEvidence);
|
|
261
|
+
}
|
|
262
|
+
results.push(acceptedResult);
|
|
263
|
+
}
|
|
264
|
+
if (workersStartedAt !== undefined)
|
|
265
|
+
input.metrics?.end("planner.ownerWorkers", workersStartedAt);
|
|
266
|
+
const mergeStartedAt = input.metrics?.start();
|
|
267
|
+
const moduleSpecifierResolver = (0, semantic_move_owner_candidates_1.createSemanticMoveModuleSpecifierResolver)();
|
|
268
|
+
const canonicalModuleResolver = (0, semantic_move_owner_candidates_1.createSemanticMoveCanonicalModuleResolver)();
|
|
269
|
+
const merged = mergeOwnerEvidence({
|
|
270
|
+
root,
|
|
271
|
+
sourceFile,
|
|
272
|
+
targetFile,
|
|
273
|
+
sourceOwners,
|
|
274
|
+
targetExisted,
|
|
275
|
+
discovery,
|
|
276
|
+
results,
|
|
277
|
+
importRules: input.importRules ?? [],
|
|
278
|
+
moduleSpecifierResolver,
|
|
279
|
+
canonicalModuleResolver,
|
|
280
|
+
...(workspacePreferences ? { workspacePreferences } : {}),
|
|
281
|
+
...(input.metrics ? { metrics: input.metrics } : {})
|
|
282
|
+
});
|
|
283
|
+
if (mergeStartedAt !== undefined)
|
|
284
|
+
input.metrics?.end("planner.merge", mergeStartedAt);
|
|
285
|
+
if (!merged.ok) {
|
|
286
|
+
return incomplete(merged.code, merged.error, root, sourceFile, targetFile, summary, startedAt, snapshotHash, workerEvidence, merged.divergence ? [merged.divergence] : []);
|
|
287
|
+
}
|
|
288
|
+
const validationStartedAt = input.metrics?.start();
|
|
289
|
+
let validationEnded = false;
|
|
290
|
+
const endValidation = () => {
|
|
291
|
+
if (validationEnded || validationStartedAt === undefined)
|
|
292
|
+
return;
|
|
293
|
+
validationEnded = true;
|
|
294
|
+
input.metrics?.end("planner.validation", validationStartedAt);
|
|
295
|
+
};
|
|
296
|
+
const policyEdits = (0, semantic_move_policy_1.applySemanticMoveImportPolicies)({
|
|
297
|
+
workspaceRoot: root,
|
|
298
|
+
edits: merged.edits,
|
|
299
|
+
importRules: input.importRules ?? [],
|
|
300
|
+
...(workspacePreferences ? { workspacePreferences } : {}),
|
|
301
|
+
acceptsModuleSpecifier: ({ fileName, specifier, targetFile: resolvedTarget }) => {
|
|
302
|
+
const ownerConfigs = merged.moduleSpecifierOwners.get(pathKey(fileName));
|
|
303
|
+
return Boolean(ownerConfigs?.length && moduleSpecifierResolver({
|
|
304
|
+
specifier,
|
|
305
|
+
containingFile: fileName,
|
|
306
|
+
targetFile: resolvedTarget,
|
|
307
|
+
ownerConfigs
|
|
308
|
+
}));
|
|
309
|
+
},
|
|
310
|
+
resolveVirtualModuleTarget: ({ fileName, specifier }) => {
|
|
311
|
+
const ownerConfigs = merged.moduleSpecifierOwners.get(pathKey(fileName));
|
|
312
|
+
return ownerConfigs?.length && moduleSpecifierResolver({
|
|
313
|
+
specifier,
|
|
314
|
+
containingFile: fileName,
|
|
315
|
+
targetFile,
|
|
316
|
+
ownerConfigs
|
|
317
|
+
})
|
|
318
|
+
? targetFile
|
|
319
|
+
: null;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
const semanticEdits = applySafeDynamicImportMoveEdits({
|
|
323
|
+
workspaceRoot: root,
|
|
324
|
+
sourceFile,
|
|
325
|
+
targetFile,
|
|
326
|
+
entities: selection.entities,
|
|
327
|
+
discovery,
|
|
328
|
+
edits: policyEdits,
|
|
329
|
+
importRules: input.importRules ?? [],
|
|
330
|
+
...(workspacePreferences ? { workspacePreferences } : {}),
|
|
331
|
+
moduleSpecifierResolver
|
|
332
|
+
});
|
|
333
|
+
const namedReexportEdits = applySafeNamedReexportMoveEdits({
|
|
334
|
+
workspaceRoot: root,
|
|
335
|
+
sourceFile,
|
|
336
|
+
targetFile,
|
|
337
|
+
entities: selection.entities,
|
|
338
|
+
discovery,
|
|
339
|
+
edits: semanticEdits,
|
|
340
|
+
importRules: input.importRules ?? [],
|
|
341
|
+
...(workspacePreferences ? { workspacePreferences } : {}),
|
|
342
|
+
moduleSpecifierResolver
|
|
343
|
+
});
|
|
344
|
+
const structure = verifyMoveStructure(sourceFile, targetFile, sourceText, selection.entities, namedReexportEdits);
|
|
345
|
+
if (!structure.ok) {
|
|
346
|
+
endValidation();
|
|
347
|
+
return incomplete(structure.code, structure.error, root, sourceFile, targetFile, summary, startedAt, snapshotHash, workerEvidence);
|
|
348
|
+
}
|
|
349
|
+
const exportStarCompletion = applySafeExportStarMoveEdits({
|
|
350
|
+
workspaceRoot: root,
|
|
351
|
+
sourceFile,
|
|
352
|
+
targetFile,
|
|
353
|
+
entities: selection.entities,
|
|
354
|
+
discovery,
|
|
355
|
+
edits: namedReexportEdits,
|
|
356
|
+
results,
|
|
357
|
+
importRules: input.importRules ?? [],
|
|
358
|
+
...(workspacePreferences ? { workspacePreferences } : {}),
|
|
359
|
+
moduleSpecifierResolver
|
|
360
|
+
});
|
|
361
|
+
const completeEdits = exportStarCompletion.edits;
|
|
362
|
+
const edgeCoverage = verifyMovedEntityEdgeCoverage(sourceFile, selection.entities, discovery, completeEdits, exportStarCompletion.coveredEdges);
|
|
363
|
+
if (!edgeCoverage.ok) {
|
|
364
|
+
endValidation();
|
|
365
|
+
return incomplete(edgeCoverage.code, edgeCoverage.error, root, sourceFile, targetFile, summary, startedAt, snapshotHash, workerEvidence);
|
|
366
|
+
}
|
|
367
|
+
const synthesizedEditFiles = new Set(completeEdits
|
|
368
|
+
.filter((edit) => !policyEdits.some((candidate) => samePath(candidate.fileName, edit.fileName)))
|
|
369
|
+
.map((edit) => pathKey(edit.fileName)));
|
|
370
|
+
if (!verifyBeforeHashes(results, completeEdits, synthesizedEditFiles)) {
|
|
371
|
+
endValidation();
|
|
372
|
+
return incomplete("SEMANTIC_MOVE_EDIT_SNAPSHOT_CHANGED", "A config or edited file changed during planning.", root, sourceFile, targetFile, summary, startedAt, snapshotHash, workerEvidence);
|
|
373
|
+
}
|
|
374
|
+
let finalFingerprint;
|
|
375
|
+
try {
|
|
376
|
+
finalFingerprint = fingerprint(root);
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
endValidation();
|
|
380
|
+
return incomplete("SEMANTIC_MOVE_SNAPSHOT_UNREADABLE", errorMessage(error), root, sourceFile, targetFile, summary, startedAt, snapshotHash, workerEvidence);
|
|
381
|
+
}
|
|
382
|
+
const finalTargetMatches = fs.existsSync(targetFile) === targetExisted &&
|
|
383
|
+
(!targetExisted || fs.readFileSync(targetFile).equals(targetBytes));
|
|
384
|
+
if (finalFingerprint !== discovery.workspaceFingerprint || hashFiles(evidenceFiles) !== snapshotHash || !finalTargetMatches) {
|
|
385
|
+
endValidation();
|
|
386
|
+
return incomplete("SEMANTIC_MOVE_SNAPSHOT_CHANGED", "Workspace files, configs, packages, consumers, or target existence changed during planning.", root, sourceFile, targetFile, summary, startedAt, snapshotHash, workerEvidence);
|
|
387
|
+
}
|
|
388
|
+
endValidation();
|
|
389
|
+
const completePlan = {
|
|
390
|
+
complete: true,
|
|
391
|
+
code: "SEMANTIC_MOVE_PLAN_COMPLETE",
|
|
392
|
+
workspaceRoot: root,
|
|
393
|
+
sourceFile,
|
|
394
|
+
targetFile,
|
|
395
|
+
targetExisted,
|
|
396
|
+
entities: selection.entities,
|
|
397
|
+
edits: completeEdits,
|
|
398
|
+
discovery: summary,
|
|
399
|
+
configWorkers: workerEvidence,
|
|
400
|
+
snapshotHash,
|
|
401
|
+
planHash: "",
|
|
402
|
+
totalMs: Date.now() - startedAt,
|
|
403
|
+
packageManifestImpacts: (0, package_manifest_impact_1.collectMutationPackageManifestImpacts)({
|
|
404
|
+
mutationKind: "declaration-move",
|
|
405
|
+
sourceFile,
|
|
406
|
+
targetFile,
|
|
407
|
+
changedFiles: completeEdits.map((edit) => edit.fileName),
|
|
408
|
+
packageManifests: discovery.packageManifests
|
|
409
|
+
})
|
|
410
|
+
};
|
|
411
|
+
completePlan.planHash = (0, semantic_move_plan_hash_1.computeSemanticMovePlanHash)(completePlan);
|
|
412
|
+
return completePlan;
|
|
413
|
+
}
|
|
414
|
+
function buildSelection(sourceFile, sourceText, rawEntities) {
|
|
415
|
+
if (rawEntities.length === 0)
|
|
416
|
+
return { ok: false, code: "SEMANTIC_MOVE_ENTITY_REQUIRED", error: "At least one top-level entity is required." };
|
|
417
|
+
const candidates = (0, semantic_move_declarations_1.collectSemanticMoveDeclarations)(sourceFile, sourceText);
|
|
418
|
+
const seenCandidates = new Set();
|
|
419
|
+
const selected = [];
|
|
420
|
+
const entities = [];
|
|
421
|
+
const entityStatementRanges = [];
|
|
422
|
+
for (const [index, item] of rawEntities.entries()) {
|
|
423
|
+
if (!isValidIdentifierName(item.name)) {
|
|
424
|
+
return { ok: false, code: "SEMANTIC_MOVE_ENTITY_INVALID", error: `Invalid or duplicate entity: ${item.name}` };
|
|
425
|
+
}
|
|
426
|
+
const matches = candidates.filter((candidate) => candidate.name === item.name && (!item.kind || candidate.kind === item.kind));
|
|
427
|
+
if (matches.length === 0) {
|
|
428
|
+
return { ok: false, code: "SEMANTIC_MOVE_ENTITY_NOT_FOUND", error: `Top-level entity not found: ${item.kind ? `${item.kind}:` : ""}${item.name}` };
|
|
429
|
+
}
|
|
430
|
+
const matchedKinds = [...new Set(matches.map((candidate) => candidate.kind))];
|
|
431
|
+
if (!item.kind && matchedKinds.length > 1) {
|
|
432
|
+
return {
|
|
433
|
+
ok: false,
|
|
434
|
+
code: "SEMANTIC_MOVE_ENTITY_AMBIGUOUS",
|
|
435
|
+
error: `Top-level entity matches multiple declaration kinds: ${item.name} (${matchedKinds.join(", ")}). Specify the declaration kind.`,
|
|
436
|
+
ambiguities: [{ entity: item.name, kinds: matchedKinds }]
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
if (matches.some((candidate) => seenCandidates.has(candidateKey(candidate)))) {
|
|
440
|
+
return { ok: false, code: "SEMANTIC_MOVE_ENTITY_INVALID", error: `Invalid or duplicate entity: ${item.name}` };
|
|
441
|
+
}
|
|
442
|
+
const indexes = [...new Set(matches.map((candidate) => candidate.statementIndex))].sort((left, right) => left - right);
|
|
443
|
+
if (indexes.some((value, candidateIndex) => candidateIndex > 0 && value !== indexes[candidateIndex - 1] + 1)) {
|
|
444
|
+
return { ok: false, code: "SEMANTIC_MOVE_ENTITY_AMBIGUOUS", error: `Non-contiguous declarations found for: ${item.name}` };
|
|
445
|
+
}
|
|
446
|
+
for (const match of matches)
|
|
447
|
+
seenCandidates.add(candidateKey(match));
|
|
448
|
+
selected.push(...matches);
|
|
449
|
+
entityStatementRanges.push({
|
|
450
|
+
id: item.id ?? item.name,
|
|
451
|
+
first: indexes[0],
|
|
452
|
+
last: indexes[indexes.length - 1]
|
|
453
|
+
});
|
|
454
|
+
entities.push({
|
|
455
|
+
id: item.id ?? `entity-${index + 1}`,
|
|
456
|
+
name: item.name,
|
|
457
|
+
...(item.kind ?? (new Set(matches.map((candidate) => candidate.kind)).size === 1 ? matches[0].kind : null)
|
|
458
|
+
? { kind: item.kind ?? matches[0].kind }
|
|
459
|
+
: {}),
|
|
460
|
+
declarationStarts: matches.map((candidate) => candidate.nameStart)
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
const statementIndexes = [...new Set(selected.map((candidate) => candidate.statementIndex))].sort((left, right) => left - right);
|
|
464
|
+
for (const statementIndex of statementIndexes) {
|
|
465
|
+
const unrequested = candidates.find((candidate) => candidate.statementIndex === statementIndex && !seenCandidates.has(candidateKey(candidate)));
|
|
466
|
+
if (unrequested) {
|
|
467
|
+
return {
|
|
468
|
+
ok: false,
|
|
469
|
+
code: "SEMANTIC_MOVE_DECLARATION_GROUP_INCOMPLETE",
|
|
470
|
+
error: `Moving this declaration would also move unrequested top-level entity: ${unrequested.name}`
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (statementIndexes.some((value, index) => index > 0 && value !== statementIndexes[index - 1] + 1)) {
|
|
475
|
+
return {
|
|
476
|
+
ok: false,
|
|
477
|
+
code: "SEMANTIC_MOVE_ENTITIES_NOT_CONTIGUOUS",
|
|
478
|
+
error: "Requested top-level entities are not contiguous.",
|
|
479
|
+
contiguousEntityGroups: groupContiguousEntityRanges(entityStatementRanges)
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
const ordered = [...selected].sort((left, right) => left.statementStart - right.statementStart || left.nameStart - right.nameStart);
|
|
483
|
+
const selection = ordered.length === 1
|
|
484
|
+
? { pos: ordered[0].nameStart, end: ordered[0].nameEnd }
|
|
485
|
+
: { pos: ordered[0].statementStart, end: ordered[ordered.length - 1].statementEnd };
|
|
486
|
+
return { ok: true, entities, selection, firstSelectedName: ordered[0].name };
|
|
487
|
+
}
|
|
488
|
+
function candidateKey(candidate) {
|
|
489
|
+
return `${candidate.statementIndex}:${candidate.nameStart}:${candidate.kind}`;
|
|
490
|
+
}
|
|
491
|
+
function rebasePreservedHeaderResult(result, sourceFile, sourceText, workerSourceText, header) {
|
|
492
|
+
if (!header || !result.ok)
|
|
493
|
+
return result;
|
|
494
|
+
return {
|
|
495
|
+
...result,
|
|
496
|
+
edits: result.edits.map((edit) => {
|
|
497
|
+
if (!samePath(edit.fileName, sourceFile))
|
|
498
|
+
return edit;
|
|
499
|
+
const rendered = applyTextChanges(workerSourceText, edit.textChanges);
|
|
500
|
+
return {
|
|
501
|
+
...edit,
|
|
502
|
+
isNewFile: false,
|
|
503
|
+
textChanges: [{
|
|
504
|
+
span: { start: 0, length: sourceText.length },
|
|
505
|
+
newText: `${header.text}${rendered}`
|
|
506
|
+
}]
|
|
507
|
+
};
|
|
508
|
+
})
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
function mergeOwnerEvidence(input) {
|
|
512
|
+
const resultMap = new Map(input.results.map((result) => [pathKey(result.configFilePath), result]));
|
|
513
|
+
const ownerAuthority = (0, semantic_move_owner_authority_1.createSemanticMoveOwnerAuthority)(input.discovery);
|
|
514
|
+
const observations = new Map();
|
|
515
|
+
const allowed = new Set(uniquePaths([input.sourceFile, input.targetFile, ...input.discovery.consumerFiles]).map(pathKey));
|
|
516
|
+
for (const result of input.results) {
|
|
517
|
+
const config = pathKey(result.configFilePath);
|
|
518
|
+
const perFile = new Map();
|
|
519
|
+
for (const rawEdit of result.edits) {
|
|
520
|
+
const edit = normalizeFileEdit(rawEdit);
|
|
521
|
+
const file = pathKey(edit.fileName);
|
|
522
|
+
if (!isInside(input.root, edit.fileName) || !allowed.has(file) || perFile.has(file)) {
|
|
523
|
+
return { ok: false, code: "SEMANTIC_MOVE_EDIT_SCOPE_INVALID", error: `Unexpected or duplicate edit from ${result.configFilePath}: ${edit.fileName}` };
|
|
524
|
+
}
|
|
525
|
+
perFile.set(file, edit);
|
|
526
|
+
const fileObservations = observations.get(file) ?? new Map();
|
|
527
|
+
fileObservations.set(config, edit);
|
|
528
|
+
observations.set(file, fileObservations);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
const requiredFiles = [pathKey(input.sourceFile), pathKey(input.targetFile)];
|
|
532
|
+
if (requiredFiles.some((file) => !observations.has(file))) {
|
|
533
|
+
return { ok: false, code: "SEMANTIC_MOVE_REQUIRED_EDIT_MISSING", error: "Move plan did not contain both source and target edits." };
|
|
534
|
+
}
|
|
535
|
+
const edits = [];
|
|
536
|
+
const moduleSpecifierOwners = new Map();
|
|
537
|
+
for (const [file, fileObservations] of observations) {
|
|
538
|
+
const firstObservation = fileObservations.values().next().value;
|
|
539
|
+
if (!firstObservation)
|
|
540
|
+
continue;
|
|
541
|
+
const owners = samePath(firstObservation.fileName, input.sourceFile) || samePath(firstObservation.fileName, input.targetFile)
|
|
542
|
+
? input.sourceOwners
|
|
543
|
+
: ownersForFile(input.discovery, firstObservation.fileName);
|
|
544
|
+
if (owners.length === 0) {
|
|
545
|
+
return ownerDivergenceFailure({
|
|
546
|
+
reason: "owner-evidence-missing",
|
|
547
|
+
fileName: firstObservation.fileName,
|
|
548
|
+
owners,
|
|
549
|
+
observations: fileObservations,
|
|
550
|
+
error: `No exact owner evidence exists for ${firstObservation.fileName}.`
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
if (owners.length > 1)
|
|
554
|
+
input.metrics?.observeDivergence("multiOwnerFiles", firstObservation.fileName);
|
|
555
|
+
const ownerRoles = new Map(owners.map((owner) => [
|
|
556
|
+
pathKey(owner),
|
|
557
|
+
ownerAuthority.roleForConsumer({ consumerFile: firstObservation.fileName, ownerConfig: owner })
|
|
558
|
+
]));
|
|
559
|
+
const requiredOwners = owners.filter((owner) => ownerRoles.get(pathKey(owner)) !== "generated-declaration-projection");
|
|
560
|
+
let candidate = requiredOwners
|
|
561
|
+
.map((owner) => fileObservations.get(pathKey(owner)))
|
|
562
|
+
.find((observation) => Boolean(observation));
|
|
563
|
+
if (!candidate) {
|
|
564
|
+
return ownerDivergenceFailure({
|
|
565
|
+
reason: "owner-evidence-missing",
|
|
566
|
+
fileName: firstObservation.fileName,
|
|
567
|
+
owners,
|
|
568
|
+
ownerRoles,
|
|
569
|
+
observations: fileObservations,
|
|
570
|
+
error: `No non-projection owner edit exists for ${firstObservation.fileName}.`
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
const requiredObservations = requiredOwners
|
|
574
|
+
.map((owner) => fileObservations.get(pathKey(owner)))
|
|
575
|
+
.filter((observation) => Boolean(observation));
|
|
576
|
+
if (requiredObservations.length !== requiredOwners.length) {
|
|
577
|
+
return ownerDivergenceFailure({
|
|
578
|
+
reason: "owner-edit-missing",
|
|
579
|
+
fileName: candidate.fileName,
|
|
580
|
+
owners,
|
|
581
|
+
ownerRoles,
|
|
582
|
+
observations: fileObservations,
|
|
583
|
+
error: `An exact owner omitted ${candidate.fileName}.`
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
let reconciledModuleSpecifiers = false;
|
|
587
|
+
if (new Set(requiredObservations.map(editSignature)).size > 1) {
|
|
588
|
+
input.metrics?.observeDivergence("divergentProposalFiles", candidate.fileName);
|
|
589
|
+
const isNewTarget = samePath(candidate.fileName, input.targetFile) && !input.targetExisted;
|
|
590
|
+
if (samePath(candidate.fileName, input.sourceFile) || samePath(candidate.fileName, input.targetFile) && !isNewTarget ||
|
|
591
|
+
!isRegularFile(candidate.fileName) && !isNewTarget) {
|
|
592
|
+
input.metrics?.observeDivergence("unresolvedDivergences", candidate.fileName);
|
|
593
|
+
return ownerDivergenceFailure({
|
|
594
|
+
reason: "source-target-edit-disagreement",
|
|
595
|
+
fileName: candidate.fileName,
|
|
596
|
+
owners,
|
|
597
|
+
ownerRoles,
|
|
598
|
+
observations: fileObservations,
|
|
599
|
+
error: `Exact owners disagree on ${candidate.fileName}.`
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
const configuredEnding = input.importRules.length > 0
|
|
603
|
+
? (0, import_rules_1.resolveImportRuleForFile)(candidate.fileName, input.importRules)?.ending
|
|
604
|
+
: input.workspacePreferences
|
|
605
|
+
? (0, typescript_preferences_1.resolveWorkspaceImportPreferences)(input.workspacePreferences, candidate.fileName).ending
|
|
606
|
+
: undefined;
|
|
607
|
+
const preferredEnding = configuredEnding === "auto" || configuredEnding === "js" || configuredEnding === "minimal"
|
|
608
|
+
? configuredEnding
|
|
609
|
+
: undefined;
|
|
610
|
+
const reconciliation = isNewTarget
|
|
611
|
+
? (0, semantic_move_owner_candidates_1.reconcileSemanticMoveNewTargetCandidates)({
|
|
612
|
+
fileName: candidate.fileName,
|
|
613
|
+
sourceFile: input.sourceFile,
|
|
614
|
+
sourceText: fs.readFileSync(input.sourceFile, "utf8"),
|
|
615
|
+
ownerConfigs: requiredOwners,
|
|
616
|
+
observations: requiredObservations,
|
|
617
|
+
...(preferredEnding ? { preferredEnding } : {}),
|
|
618
|
+
resolver: input.canonicalModuleResolver
|
|
619
|
+
})
|
|
620
|
+
: (0, semantic_move_owner_candidates_1.reconcileSemanticMoveOwnerCandidates)({
|
|
621
|
+
fileName: candidate.fileName,
|
|
622
|
+
expectedText: fs.readFileSync(candidate.fileName, "utf8"),
|
|
623
|
+
targetFile: input.targetFile,
|
|
624
|
+
ownerConfigs: requiredOwners,
|
|
625
|
+
observations: requiredObservations,
|
|
626
|
+
resolver: input.moduleSpecifierResolver
|
|
627
|
+
});
|
|
628
|
+
const policyCanSelect = !isNewTarget && reconciliation.status === "ambiguous" && Boolean(input.importRules.length > 0
|
|
629
|
+
? (0, import_rules_1.resolveImportRuleForFile)(candidate.fileName, input.importRules)?.specifier
|
|
630
|
+
: input.workspacePreferences &&
|
|
631
|
+
(0, typescript_preferences_1.resolveWorkspaceImportPreferences)(input.workspacePreferences, candidate.fileName).specifier);
|
|
632
|
+
if (reconciliation.status !== "selected" && !policyCanSelect) {
|
|
633
|
+
input.metrics?.observeDivergence("unresolvedDivergences", candidate.fileName);
|
|
634
|
+
const reason = reconciliation.status === "ambiguous"
|
|
635
|
+
? `${reconciliation.validCandidateCount} module-specifier candidates are valid for every exact owner`
|
|
636
|
+
: reconciliation.status === "none-valid"
|
|
637
|
+
? "no module-specifier candidate is valid for every exact owner"
|
|
638
|
+
: "owner plans differ outside module-specifier literals";
|
|
639
|
+
return ownerDivergenceFailure({
|
|
640
|
+
reason: reconciliation.status === "ambiguous"
|
|
641
|
+
? "module-specifier-ambiguous"
|
|
642
|
+
: reconciliation.status === "none-valid"
|
|
643
|
+
? "module-specifier-none-valid"
|
|
644
|
+
: isNewTarget
|
|
645
|
+
? "source-target-edit-disagreement"
|
|
646
|
+
: "non-module-specifier-disagreement",
|
|
647
|
+
fileName: candidate.fileName,
|
|
648
|
+
owners,
|
|
649
|
+
ownerRoles,
|
|
650
|
+
observations: fileObservations,
|
|
651
|
+
...(reconciliation.status === "ambiguous"
|
|
652
|
+
? { universallyValidCandidateCount: reconciliation.validCandidateCount }
|
|
653
|
+
: reconciliation.status === "none-valid"
|
|
654
|
+
? { universallyValidCandidateCount: 0 }
|
|
655
|
+
: {}),
|
|
656
|
+
error: `${reason}: ${candidate.fileName}.`
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
candidate = reconciliation.edit;
|
|
660
|
+
reconciledModuleSpecifiers = true;
|
|
661
|
+
input.metrics?.observeDivergence("policyResolvedDivergences", candidate.fileName);
|
|
662
|
+
}
|
|
663
|
+
moduleSpecifierOwners.set(pathKey(candidate.fileName), requiredOwners);
|
|
664
|
+
const signature = editSignature(candidate);
|
|
665
|
+
for (const owner of owners) {
|
|
666
|
+
const ownerKey = pathKey(owner);
|
|
667
|
+
const observation = fileObservations.get(pathKey(owner));
|
|
668
|
+
if (!resultMap.has(ownerKey)) {
|
|
669
|
+
return ownerDivergenceFailure({
|
|
670
|
+
reason: "owner-evidence-missing",
|
|
671
|
+
fileName: candidate.fileName,
|
|
672
|
+
owners,
|
|
673
|
+
ownerRoles,
|
|
674
|
+
observations: fileObservations,
|
|
675
|
+
error: `${owner} disagrees on ${candidate.fileName}.`
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
// A proven declaration output describes the pre-build package view. Its worker must finish,
|
|
679
|
+
// but neither an empty nor a conflicting proposal can overrule the exact source owner.
|
|
680
|
+
if (ownerRoles.get(ownerKey) === "generated-declaration-projection")
|
|
681
|
+
continue;
|
|
682
|
+
if (!observation) {
|
|
683
|
+
return ownerDivergenceFailure({
|
|
684
|
+
reason: "owner-edit-missing",
|
|
685
|
+
fileName: candidate.fileName,
|
|
686
|
+
owners,
|
|
687
|
+
ownerRoles,
|
|
688
|
+
observations: fileObservations,
|
|
689
|
+
error: `${owner} disagrees on ${candidate.fileName}.`
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
if (!reconciledModuleSpecifiers && editSignature(observation) !== signature) {
|
|
693
|
+
return ownerDivergenceFailure({
|
|
694
|
+
reason: "owner-edit-disagreement",
|
|
695
|
+
fileName: candidate.fileName,
|
|
696
|
+
owners,
|
|
697
|
+
ownerRoles,
|
|
698
|
+
observations: fileObservations,
|
|
699
|
+
error: `${owner} disagrees on ${candidate.fileName}.`
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
const exactOwnerKeys = new Set(owners.map(pathKey));
|
|
704
|
+
if ([...fileObservations].some(([config, observation]) => !exactOwnerKeys.has(config) && editSignature(observation) !== signature)) {
|
|
705
|
+
return ownerDivergenceFailure({
|
|
706
|
+
reason: "non-owner-projection-disagreement",
|
|
707
|
+
fileName: candidate.fileName,
|
|
708
|
+
owners: uniquePaths([...owners, ...fileObservations.keys()]),
|
|
709
|
+
ownerRoles,
|
|
710
|
+
observations: fileObservations,
|
|
711
|
+
error: `A non-owning projection disagrees on ${candidate.fileName}.`
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
const isTarget = samePath(candidate.fileName, input.targetFile);
|
|
715
|
+
const expectedExists = isTarget ? input.targetExisted : true;
|
|
716
|
+
if (candidate.isNewFile !== !expectedExists || (expectedExists && !isRegularFile(candidate.fileName))) {
|
|
717
|
+
return { ok: false, code: "SEMANTIC_MOVE_FILE_SHAPE_INVALID", error: `Invalid new/existing file evidence for ${candidate.fileName}.` };
|
|
718
|
+
}
|
|
719
|
+
const expectedBytes = expectedExists ? fs.readFileSync(candidate.fileName) : Buffer.alloc(0);
|
|
720
|
+
const expectedText = expectedBytes.toString("utf8");
|
|
721
|
+
const textChanges = normalizeExistingTargetEndOfFileSentinel({
|
|
722
|
+
fileName: candidate.fileName,
|
|
723
|
+
expectedText,
|
|
724
|
+
changes: candidate.textChanges,
|
|
725
|
+
enabled: isTarget && expectedExists
|
|
726
|
+
});
|
|
727
|
+
const validation = validateTextChanges(candidate.fileName, expectedText, textChanges);
|
|
728
|
+
if (!validation.ok)
|
|
729
|
+
return validation;
|
|
730
|
+
edits.push({
|
|
731
|
+
...candidate,
|
|
732
|
+
textChanges,
|
|
733
|
+
expectedExists,
|
|
734
|
+
expectedText,
|
|
735
|
+
expectedHash: crypto.createHash("sha256").update(expectedBytes).digest("hex")
|
|
736
|
+
});
|
|
737
|
+
observations.set(file, fileObservations);
|
|
738
|
+
}
|
|
739
|
+
return {
|
|
740
|
+
ok: true,
|
|
741
|
+
edits: edits.sort((left, right) => left.fileName.localeCompare(right.fileName)),
|
|
742
|
+
moduleSpecifierOwners
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
function ownerDivergenceFailure(input) {
|
|
746
|
+
const signatures = new Set();
|
|
747
|
+
const ownerEvidence = uniquePaths(input.owners).map((owner) => {
|
|
748
|
+
const observation = input.observations.get(pathKey(owner));
|
|
749
|
+
if (observation)
|
|
750
|
+
signatures.add(editSignature(observation));
|
|
751
|
+
return {
|
|
752
|
+
configFilePath: path.normalize(owner),
|
|
753
|
+
role: input.ownerRoles?.get(pathKey(owner)) ?? "unproven",
|
|
754
|
+
proposal: observation ? "present" : "missing",
|
|
755
|
+
...(observation
|
|
756
|
+
? { proposalHash: crypto.createHash("sha256").update(editSignature(observation)).digest("hex") }
|
|
757
|
+
: {})
|
|
758
|
+
};
|
|
759
|
+
});
|
|
760
|
+
return {
|
|
761
|
+
ok: false,
|
|
762
|
+
code: "SEMANTIC_MOVE_OWNER_DIVERGENCE",
|
|
763
|
+
error: input.error,
|
|
764
|
+
divergence: {
|
|
765
|
+
reason: input.reason,
|
|
766
|
+
fileName: path.normalize(input.fileName),
|
|
767
|
+
ownerEvidence,
|
|
768
|
+
candidateCount: signatures.size,
|
|
769
|
+
...(input.universallyValidCandidateCount !== undefined
|
|
770
|
+
? { universallyValidCandidateCount: input.universallyValidCandidateCount }
|
|
771
|
+
: {})
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
function verifyMoveStructure(sourceFile, targetFile, sourceText, entities, edits) {
|
|
776
|
+
const sourceEdit = edits.find((edit) => samePath(edit.fileName, sourceFile));
|
|
777
|
+
const targetEdit = edits.find((edit) => samePath(edit.fileName, targetFile));
|
|
778
|
+
if (!sourceEdit || !targetEdit)
|
|
779
|
+
return { ok: false, code: "SEMANTIC_MOVE_REQUIRED_EDIT_MISSING", error: "Source or target edit is missing." };
|
|
780
|
+
const renderedSource = applyTextChanges(sourceEdit.expectedText, sourceEdit.textChanges);
|
|
781
|
+
const renderedTarget = applyTextChanges(targetEdit.expectedText, targetEdit.textChanges);
|
|
782
|
+
for (const entity of entities) {
|
|
783
|
+
const expectedCount = (0, semantic_move_declarations_1.countSemanticMoveDeclarations)(sourceFile, sourceText, entity.name, entity.kind);
|
|
784
|
+
if (expectedCount !== entity.declarationStarts.length || (0, semantic_move_declarations_1.countSemanticMoveDeclarations)(sourceFile, renderedSource, entity.name, entity.kind) !== 0 ||
|
|
785
|
+
(0, semantic_move_declarations_1.countSemanticMoveDeclarations)(targetFile, renderedTarget, entity.name, entity.kind) !== expectedCount) {
|
|
786
|
+
return {
|
|
787
|
+
ok: false,
|
|
788
|
+
code: "SEMANTIC_MOVE_STRUCTURE_DIVERGENCE",
|
|
789
|
+
error: `TypeScript move did not transfer the exact top-level declaration group for ${entity.name}.`
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
const requestedKinds = new Set(entities.map((entity) => `${entity.name}\0${entity.kind ?? "*"}`));
|
|
794
|
+
const protectedDeclarations = new Map();
|
|
795
|
+
for (const candidate of (0, semantic_move_declarations_1.collectSemanticMoveDeclarations)(sourceFile, sourceText)) {
|
|
796
|
+
if (requestedKinds.has(`${candidate.name}\0*`) || requestedKinds.has(`${candidate.name}\0${candidate.kind}`))
|
|
797
|
+
continue;
|
|
798
|
+
const key = `${candidate.name}\0${candidate.kind}`;
|
|
799
|
+
const current = protectedDeclarations.get(key);
|
|
800
|
+
protectedDeclarations.set(key, { name: candidate.name, kind: candidate.kind, count: (current?.count ?? 0) + 1 });
|
|
801
|
+
}
|
|
802
|
+
for (const declaration of protectedDeclarations.values()) {
|
|
803
|
+
if ((0, semantic_move_declarations_1.countSemanticMoveDeclarations)(sourceFile, renderedSource, declaration.name, declaration.kind) !== declaration.count) {
|
|
804
|
+
return {
|
|
805
|
+
ok: false,
|
|
806
|
+
code: "SEMANTIC_MOVE_STRUCTURE_DIVERGENCE",
|
|
807
|
+
error: `TypeScript move changed unrequested top-level ${declaration.kind} declaration ${declaration.name}.`
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
return { ok: true };
|
|
812
|
+
}
|
|
813
|
+
function groupContiguousEntityRanges(ranges) {
|
|
814
|
+
const ordered = [...ranges].sort((left, right) => left.first - right.first || left.last - right.last);
|
|
815
|
+
const groups = [];
|
|
816
|
+
for (const range of ordered) {
|
|
817
|
+
const current = groups[groups.length - 1];
|
|
818
|
+
if (current && range.first <= current.last + 1) {
|
|
819
|
+
current.last = Math.max(current.last, range.last);
|
|
820
|
+
current.ids.push(range.id);
|
|
821
|
+
}
|
|
822
|
+
else {
|
|
823
|
+
groups.push({ last: range.last, ids: [range.id] });
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
return groups.map((group) => group.ids);
|
|
827
|
+
}
|
|
828
|
+
function chooseSemanticMoveTargetSpecifier(input) {
|
|
829
|
+
let configuredRule = input.importRules.length > 0
|
|
830
|
+
? (0, import_rules_1.resolveImportRuleForFile)(input.fromFile, input.importRules)
|
|
831
|
+
: null;
|
|
832
|
+
if (!configuredRule && input.workspacePreferences) {
|
|
833
|
+
const preferences = (0, typescript_preferences_1.resolveWorkspaceImportPreferences)(input.workspacePreferences, input.fromFile);
|
|
834
|
+
if (preferences.specifier || preferences.ending) {
|
|
835
|
+
configuredRule = {
|
|
836
|
+
scope: ".",
|
|
837
|
+
scopePath: input.workspaceRoot,
|
|
838
|
+
...(preferences.specifier ? { specifier: preferences.specifier } : {}),
|
|
839
|
+
...(preferences.ending ? { ending: preferences.ending } : {})
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
const inferredEnding = /\.[cm]?js$/i.test(input.originalSpecifier) ? "js" : "minimal";
|
|
844
|
+
const rule = (0, import_specifier_policy_1.materializeAutoImportEnding)({
|
|
845
|
+
scope: configuredRule?.scope ?? ".",
|
|
846
|
+
scopePath: configuredRule?.scopePath ?? input.workspaceRoot,
|
|
847
|
+
specifier: configuredRule?.specifier ?? "relative",
|
|
848
|
+
ending: configuredRule?.ending ?? inferredEnding,
|
|
849
|
+
...(configuredRule?.builtin ? { builtin: configuredRule.builtin } : {}),
|
|
850
|
+
...(configuredRule?.unusedImports ? { unusedImports: configuredRule.unusedImports } : {})
|
|
851
|
+
}, input.originalSpecifier);
|
|
852
|
+
const config = (0, import_specifier_policy_1.readNearestTsConfig)(input.fromFile, input.workspaceRoot, input.configCache);
|
|
853
|
+
const preferred = (0, import_specifier_policy_1.chooseImportSpecifier)({
|
|
854
|
+
fromFile: input.fromFile,
|
|
855
|
+
targetFile: input.targetFile,
|
|
856
|
+
config,
|
|
857
|
+
rule,
|
|
858
|
+
allowIndexCollapse: true
|
|
859
|
+
});
|
|
860
|
+
const relative = (0, import_specifier_policy_1.chooseImportSpecifier)({
|
|
861
|
+
fromFile: input.fromFile,
|
|
862
|
+
targetFile: input.targetFile,
|
|
863
|
+
config,
|
|
864
|
+
rule: { ...rule, specifier: "relative" },
|
|
865
|
+
allowIndexCollapse: true
|
|
866
|
+
});
|
|
867
|
+
return [preferred, relative].find((specifier) => input.moduleSpecifierResolver({
|
|
868
|
+
specifier,
|
|
869
|
+
containingFile: input.fromFile,
|
|
870
|
+
targetFile: input.targetFile,
|
|
871
|
+
ownerConfigs: input.owners
|
|
872
|
+
})) ?? null;
|
|
873
|
+
}
|
|
874
|
+
function applySafeDynamicImportMoveEdits(input) {
|
|
875
|
+
const names = new Set(input.entities.map((entity) => entity.name));
|
|
876
|
+
const editsByFile = new Map(input.edits.map((edit) => [pathKey(edit.fileName), edit]));
|
|
877
|
+
const renderedByFile = new Map();
|
|
878
|
+
const processed = new Set();
|
|
879
|
+
const configCache = new Map();
|
|
880
|
+
for (const edge of input.discovery.edges) {
|
|
881
|
+
if (edge.kind !== "dynamic" || !samePath(edge.targetFile, input.sourceFile) || !isRegularFile(edge.fromFile)) {
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
const key = `${pathKey(edge.fromFile)}\0${edge.moduleSpecifier}`;
|
|
885
|
+
if (processed.has(key))
|
|
886
|
+
continue;
|
|
887
|
+
processed.add(key);
|
|
888
|
+
const edit = editsByFile.get(pathKey(edge.fromFile));
|
|
889
|
+
const expectedText = edit?.expectedText ?? fs.readFileSync(edge.fromFile, "utf8");
|
|
890
|
+
const currentText = renderedByFile.get(pathKey(edge.fromFile)) ??
|
|
891
|
+
(edit ? applyTextChanges(edit.expectedText, edit.textChanges) : expectedText);
|
|
892
|
+
const source = typescript_1.default.createSourceFile(edge.fromFile, currentText, typescript_1.default.ScriptTarget.Latest, true, scriptKindForFile(edge.fromFile));
|
|
893
|
+
const literals = collectSafeDynamicImportLiterals(source, edge.moduleSpecifier, names);
|
|
894
|
+
if (literals.length === 0)
|
|
895
|
+
continue;
|
|
896
|
+
const owners = ownersForFile(input.discovery, edge.fromFile);
|
|
897
|
+
if (owners.length === 0)
|
|
898
|
+
continue;
|
|
899
|
+
const nextSpecifier = chooseSemanticMoveTargetSpecifier({
|
|
900
|
+
workspaceRoot: input.workspaceRoot,
|
|
901
|
+
fromFile: edge.fromFile,
|
|
902
|
+
targetFile: input.targetFile,
|
|
903
|
+
originalSpecifier: edge.moduleSpecifier,
|
|
904
|
+
owners,
|
|
905
|
+
importRules: input.importRules,
|
|
906
|
+
...(input.workspacePreferences ? { workspacePreferences: input.workspacePreferences } : {}),
|
|
907
|
+
moduleSpecifierResolver: input.moduleSpecifierResolver,
|
|
908
|
+
configCache
|
|
909
|
+
});
|
|
910
|
+
if (!nextSpecifier)
|
|
911
|
+
continue;
|
|
912
|
+
let nextText = currentText;
|
|
913
|
+
for (const literal of literals.sort((left, right) => right.start - left.start)) {
|
|
914
|
+
nextText = `${nextText.slice(0, literal.start)}${nextSpecifier}${nextText.slice(literal.start + literal.length)}`;
|
|
915
|
+
}
|
|
916
|
+
renderedByFile.set(pathKey(edge.fromFile), nextText);
|
|
917
|
+
}
|
|
918
|
+
return mergeSemanticMoveRenderedFiles(input.edits, renderedByFile, input.discovery);
|
|
919
|
+
}
|
|
920
|
+
function exportStarEdgeKey(edge) {
|
|
921
|
+
return `${pathKey(edge.fromFile)}\0${edge.specifierStart}`;
|
|
922
|
+
}
|
|
923
|
+
function applySafeNamedReexportMoveEdits(input) {
|
|
924
|
+
const names = new Set(input.entities.map((entity) => entity.name));
|
|
925
|
+
const editsByFile = new Map(input.edits.map((edit) => [pathKey(edit.fileName), edit]));
|
|
926
|
+
const renderedByFile = new Map();
|
|
927
|
+
const processed = new Set();
|
|
928
|
+
const configCache = new Map();
|
|
929
|
+
const sourceEdit = editsByFile.get(pathKey(input.sourceFile));
|
|
930
|
+
const renderedSource = sourceEdit
|
|
931
|
+
? applyTextChanges(sourceEdit.expectedText, sourceEdit.textChanges)
|
|
932
|
+
: fs.readFileSync(input.sourceFile, "utf8");
|
|
933
|
+
const retainedNames = new Set((0, semantic_move_declarations_1.collectSemanticMoveDeclarations)(input.sourceFile, renderedSource).map((declaration) => declaration.name));
|
|
934
|
+
for (const edge of input.discovery.edges) {
|
|
935
|
+
if (edge.kind !== "reexport" || !samePath(edge.targetFile, input.sourceFile) || !isRegularFile(edge.fromFile))
|
|
936
|
+
continue;
|
|
937
|
+
const key = `${pathKey(edge.fromFile)}\0${edge.moduleSpecifier}`;
|
|
938
|
+
if (processed.has(key))
|
|
939
|
+
continue;
|
|
940
|
+
processed.add(key);
|
|
941
|
+
const edit = editsByFile.get(pathKey(edge.fromFile));
|
|
942
|
+
const expectedText = edit?.expectedText ?? fs.readFileSync(edge.fromFile, "utf8");
|
|
943
|
+
const currentText = renderedByFile.get(pathKey(edge.fromFile)) ??
|
|
944
|
+
(edit ? applyTextChanges(edit.expectedText, edit.textChanges) : expectedText);
|
|
945
|
+
const source = typescript_1.default.createSourceFile(edge.fromFile, currentText, typescript_1.default.ScriptTarget.Latest, true, scriptKindForFile(edge.fromFile));
|
|
946
|
+
const matching = source.statements.filter((statement) => typescript_1.default.isExportDeclaration(statement) && statement.exportClause !== undefined && typescript_1.default.isNamedExports(statement.exportClause) &&
|
|
947
|
+
statement.moduleSpecifier !== undefined && typescript_1.default.isStringLiteralLike(statement.moduleSpecifier) &&
|
|
948
|
+
statement.moduleSpecifier.text === edge.moduleSpecifier);
|
|
949
|
+
if (matching.length === 0)
|
|
950
|
+
continue;
|
|
951
|
+
const owners = ownersForFile(input.discovery, edge.fromFile);
|
|
952
|
+
if (owners.length === 0)
|
|
953
|
+
continue;
|
|
954
|
+
const nextSpecifier = chooseSemanticMoveTargetSpecifier({
|
|
955
|
+
workspaceRoot: input.workspaceRoot,
|
|
956
|
+
fromFile: edge.fromFile,
|
|
957
|
+
targetFile: input.targetFile,
|
|
958
|
+
originalSpecifier: edge.moduleSpecifier,
|
|
959
|
+
owners,
|
|
960
|
+
importRules: input.importRules,
|
|
961
|
+
...(input.workspacePreferences ? { workspacePreferences: input.workspacePreferences } : {}),
|
|
962
|
+
moduleSpecifierResolver: input.moduleSpecifierResolver,
|
|
963
|
+
configCache
|
|
964
|
+
});
|
|
965
|
+
if (!nextSpecifier)
|
|
966
|
+
continue;
|
|
967
|
+
const replacements = matching.flatMap((statement) => {
|
|
968
|
+
const replacement = renderSemanticMoveNamedReexportReplacement(source, statement, edge.moduleSpecifier, nextSpecifier, names, retainedNames);
|
|
969
|
+
return replacement === null
|
|
970
|
+
? []
|
|
971
|
+
: [{ start: statement.getStart(source), length: statement.getWidth(source), replacement }];
|
|
972
|
+
});
|
|
973
|
+
if (replacements.length === 0)
|
|
974
|
+
continue;
|
|
975
|
+
let rendered = currentText;
|
|
976
|
+
for (const replacement of replacements.sort((left, right) => right.start - left.start)) {
|
|
977
|
+
rendered = `${rendered.slice(0, replacement.start)}${replacement.replacement}${rendered.slice(replacement.start + replacement.length)}`;
|
|
978
|
+
}
|
|
979
|
+
renderedByFile.set(pathKey(edge.fromFile), rendered);
|
|
980
|
+
}
|
|
981
|
+
return mergeSemanticMoveRenderedFiles(input.edits, renderedByFile, input.discovery);
|
|
982
|
+
}
|
|
983
|
+
function renderSemanticMoveNamedReexportReplacement(source, statement, sourceSpecifier, targetSpecifier, movedNames, retainedNames) {
|
|
984
|
+
const clause = statement.exportClause;
|
|
985
|
+
if (!clause || !typescript_1.default.isNamedExports(clause) || statement.attributes)
|
|
986
|
+
return null;
|
|
987
|
+
const clauseStart = clause.getStart(source);
|
|
988
|
+
const comments = [...clause.getText(source).matchAll(/\/\/[^\r\n]*|\/\*[\s\S]*?\*\//g)].map((match) => ({
|
|
989
|
+
pos: clauseStart + match.index,
|
|
990
|
+
text: match[0]
|
|
991
|
+
}));
|
|
992
|
+
const claimedComments = new Set();
|
|
993
|
+
const descriptors = [];
|
|
994
|
+
for (const element of clause.elements) {
|
|
995
|
+
if (!typescript_1.default.isIdentifier(element.name) || element.propertyName && !typescript_1.default.isIdentifier(element.propertyName))
|
|
996
|
+
return null;
|
|
997
|
+
const sourceName = (element.propertyName ?? element.name).text;
|
|
998
|
+
const alias = element.propertyName ? `${element.propertyName.text} as ${element.name.text}` : element.name.text;
|
|
999
|
+
const typeOnly = statement.isTypeOnly || element.isTypeOnly;
|
|
1000
|
+
const leadingComments = (typescript_1.default.getLeadingCommentRanges(source.text, element.getFullStart()) ?? [])
|
|
1001
|
+
.filter((range) => range.pos >= clauseStart && range.end <= clause.getEnd());
|
|
1002
|
+
for (const range of leadingComments)
|
|
1003
|
+
claimedComments.add(range.pos);
|
|
1004
|
+
descriptors.push({
|
|
1005
|
+
sourceName,
|
|
1006
|
+
text: !statement.isTypeOnly && element.isTypeOnly ? `type ${alias}` : alias,
|
|
1007
|
+
typeOnly,
|
|
1008
|
+
comments: leadingComments.map((range) => source.text.slice(range.pos, range.end)),
|
|
1009
|
+
end: element.getEnd()
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
for (const comment of comments.filter((candidate) => !claimedComments.has(candidate.pos))) {
|
|
1013
|
+
let previous;
|
|
1014
|
+
for (let index = descriptors.length - 1; index >= 0; index--) {
|
|
1015
|
+
if (descriptors[index].end <= comment.pos) {
|
|
1016
|
+
previous = descriptors[index];
|
|
1017
|
+
break;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (previous && source.getLineAndCharacterOfPosition(previous.end).line === source.getLineAndCharacterOfPosition(comment.pos).line) {
|
|
1021
|
+
previous.comments.push(comment.text);
|
|
1022
|
+
claimedComments.add(comment.pos);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
const moved = descriptors.filter((descriptor) => movedNames.has(descriptor.sourceName) && !retainedNames.has(descriptor.sourceName));
|
|
1026
|
+
if (moved.length === 0)
|
|
1027
|
+
return null;
|
|
1028
|
+
const retained = descriptors.filter((descriptor) => !movedNames.has(descriptor.sourceName) || retainedNames.has(descriptor.sourceName));
|
|
1029
|
+
const rawSpecifier = statement.moduleSpecifier?.getText(source) ?? "";
|
|
1030
|
+
const quote = rawSpecifier.startsWith("'") ? "'" : '"';
|
|
1031
|
+
const semicolon = statement.getText(source).trimEnd().endsWith(";");
|
|
1032
|
+
return [
|
|
1033
|
+
...comments.filter((comment) => !claimedComments.has(comment.pos)).map((comment) => comment.text),
|
|
1034
|
+
...(retained.length > 0
|
|
1035
|
+
? [renderSemanticMoveNamedReexport(sourceSpecifier, quote, statement.isTypeOnly, retained, semicolon)]
|
|
1036
|
+
: []),
|
|
1037
|
+
renderSemanticMoveNamedReexport(targetSpecifier, quote, statement.isTypeOnly, moved, semicolon)
|
|
1038
|
+
].join("\n");
|
|
1039
|
+
}
|
|
1040
|
+
function renderSemanticMoveNamedReexport(moduleSpecifier, quote, statementTypeOnly, descriptors, semicolon) {
|
|
1041
|
+
const prefix = statementTypeOnly ? "export type" : "export";
|
|
1042
|
+
const suffix = semicolon ? ";" : "";
|
|
1043
|
+
const typeModes = new Set(descriptors.map((descriptor) => descriptor.typeOnly));
|
|
1044
|
+
if (descriptors.length <= 2 && typeModes.size === 1 && descriptors.every((descriptor) => descriptor.comments.length === 0)) {
|
|
1045
|
+
return `${prefix} { ${descriptors.map((descriptor) => descriptor.text).join(", ")} } from ${quote}${moduleSpecifier}${quote}${suffix}`;
|
|
1046
|
+
}
|
|
1047
|
+
return [
|
|
1048
|
+
`${prefix} {`,
|
|
1049
|
+
...descriptors.flatMap((descriptor) => [
|
|
1050
|
+
...descriptor.comments.flatMap((comment) => comment.split(/\r?\n/).map((line) => ` ${line.trimStart()}`)),
|
|
1051
|
+
` ${descriptor.text},`
|
|
1052
|
+
]),
|
|
1053
|
+
`} from ${quote}${moduleSpecifier}${quote}${suffix}`
|
|
1054
|
+
].join("\n");
|
|
1055
|
+
}
|
|
1056
|
+
function applySafeExportStarMoveEdits(input) {
|
|
1057
|
+
const coveredEdges = new Set();
|
|
1058
|
+
const editsByFile = new Map(input.edits.map((edit) => [pathKey(edit.fileName), edit]));
|
|
1059
|
+
const sourceEdit = editsByFile.get(pathKey(input.sourceFile));
|
|
1060
|
+
const renderedSource = sourceEdit
|
|
1061
|
+
? applyTextChanges(sourceEdit.expectedText, sourceEdit.textChanges)
|
|
1062
|
+
: fs.readFileSync(input.sourceFile, "utf8");
|
|
1063
|
+
if (!typescript_1.default.isExternalModule(typescript_1.default.createSourceFile(input.sourceFile, renderedSource, typescript_1.default.ScriptTarget.Latest, true, scriptKindForFile(input.sourceFile)))) {
|
|
1064
|
+
return { edits: [...input.edits], coveredEdges };
|
|
1065
|
+
}
|
|
1066
|
+
const renderedByFile = new Map();
|
|
1067
|
+
const configCache = new Map();
|
|
1068
|
+
for (const edge of input.discovery.edges) {
|
|
1069
|
+
if (edge.kind !== "exportStar" || !samePath(edge.targetFile, input.sourceFile) || !isRegularFile(edge.fromFile))
|
|
1070
|
+
continue;
|
|
1071
|
+
const owners = ownersForFile(input.discovery, edge.fromFile);
|
|
1072
|
+
if (owners.length === 0)
|
|
1073
|
+
continue;
|
|
1074
|
+
const valueEntities = [];
|
|
1075
|
+
const typeEntities = [];
|
|
1076
|
+
let completeProof = true;
|
|
1077
|
+
for (const entity of input.entities) {
|
|
1078
|
+
const proofs = owners.map((owner) => input.results
|
|
1079
|
+
.find((result) => samePath(result.configFilePath, owner))
|
|
1080
|
+
?.exportStarProofs?.find((proof) => samePath(proof.barrelFile, edge.fromFile) &&
|
|
1081
|
+
proof.specifierStart === edge.specifierStart && proof.entity === entity.name));
|
|
1082
|
+
if (proofs.some((proof) => !proof?.checked)) {
|
|
1083
|
+
completeProof = false;
|
|
1084
|
+
break;
|
|
1085
|
+
}
|
|
1086
|
+
const matches = proofs.map((proof) => proof.matchesSourceSymbol);
|
|
1087
|
+
if (new Set(matches).size !== 1) {
|
|
1088
|
+
completeProof = false;
|
|
1089
|
+
break;
|
|
1090
|
+
}
|
|
1091
|
+
if (!matches[0]) {
|
|
1092
|
+
completeProof = false;
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
const typeOnly = proofs.map((proof) => proof.typeOnly);
|
|
1096
|
+
if (new Set(typeOnly).size !== 1) {
|
|
1097
|
+
completeProof = false;
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
(typeOnly[0] ? typeEntities : valueEntities).push(entity.name);
|
|
1101
|
+
}
|
|
1102
|
+
if (!completeProof)
|
|
1103
|
+
continue;
|
|
1104
|
+
const uniqueValueEntities = [...new Set(valueEntities)];
|
|
1105
|
+
const valueEntityNames = new Set(uniqueValueEntities);
|
|
1106
|
+
const uniqueTypeEntities = [...new Set(typeEntities)].filter((name) => !valueEntityNames.has(name));
|
|
1107
|
+
const nextSpecifier = chooseSemanticMoveTargetSpecifier({
|
|
1108
|
+
workspaceRoot: input.workspaceRoot,
|
|
1109
|
+
fromFile: edge.fromFile,
|
|
1110
|
+
targetFile: input.targetFile,
|
|
1111
|
+
originalSpecifier: edge.moduleSpecifier,
|
|
1112
|
+
owners,
|
|
1113
|
+
importRules: input.importRules,
|
|
1114
|
+
...(input.workspacePreferences ? { workspacePreferences: input.workspacePreferences } : {}),
|
|
1115
|
+
moduleSpecifierResolver: input.moduleSpecifierResolver,
|
|
1116
|
+
configCache
|
|
1117
|
+
});
|
|
1118
|
+
if (!nextSpecifier)
|
|
1119
|
+
continue;
|
|
1120
|
+
const existing = editsByFile.get(pathKey(edge.fromFile));
|
|
1121
|
+
const expectedText = existing?.expectedText ?? fs.readFileSync(edge.fromFile, "utf8");
|
|
1122
|
+
const currentText = renderedByFile.get(pathKey(edge.fromFile)) ??
|
|
1123
|
+
(existing ? applyTextChanges(existing.expectedText, existing.textChanges) : expectedText);
|
|
1124
|
+
const barrelSource = typescript_1.default.createSourceFile(edge.fromFile, currentText, typescript_1.default.ScriptTarget.Latest, true, scriptKindForFile(edge.fromFile));
|
|
1125
|
+
const star = barrelSource.statements.find((statement) => typescript_1.default.isExportDeclaration(statement) && !statement.exportClause && statement.moduleSpecifier !== undefined &&
|
|
1126
|
+
typescript_1.default.isStringLiteralLike(statement.moduleSpecifier) && statement.moduleSpecifier.text === edge.moduleSpecifier);
|
|
1127
|
+
if (!star?.moduleSpecifier || !typescript_1.default.isStringLiteralLike(star.moduleSpecifier))
|
|
1128
|
+
continue;
|
|
1129
|
+
const rawSpecifier = star.moduleSpecifier.getText(barrelSource);
|
|
1130
|
+
const quote = rawSpecifier.startsWith("'") ? "'" : '"';
|
|
1131
|
+
renderedByFile.set(pathKey(edge.fromFile), mergeOrAppendSemanticMoveNamedExport({
|
|
1132
|
+
source: barrelSource,
|
|
1133
|
+
text: currentText,
|
|
1134
|
+
moduleSpecifier: nextSpecifier,
|
|
1135
|
+
quote,
|
|
1136
|
+
valueEntities: uniqueValueEntities,
|
|
1137
|
+
typeEntities: uniqueTypeEntities
|
|
1138
|
+
}));
|
|
1139
|
+
coveredEdges.add(exportStarEdgeKey(edge));
|
|
1140
|
+
}
|
|
1141
|
+
return {
|
|
1142
|
+
edits: mergeSemanticMoveRenderedFiles(input.edits, renderedByFile, input.discovery),
|
|
1143
|
+
coveredEdges
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
function mergeSemanticMoveRenderedFiles(edits, renderedByFile, discovery) {
|
|
1147
|
+
if (renderedByFile.size === 0)
|
|
1148
|
+
return [...edits];
|
|
1149
|
+
const result = [...edits];
|
|
1150
|
+
for (const [fileKey, rendered] of renderedByFile) {
|
|
1151
|
+
const existingIndex = result.findIndex((edit) => pathKey(edit.fileName) === fileKey);
|
|
1152
|
+
if (existingIndex >= 0) {
|
|
1153
|
+
const existing = result[existingIndex];
|
|
1154
|
+
result[existingIndex] = {
|
|
1155
|
+
...existing,
|
|
1156
|
+
textChanges: [{ span: { start: 0, length: existing.expectedText.length }, newText: rendered }]
|
|
1157
|
+
};
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
const fileName = discovery.edges.find((edge) => pathKey(edge.fromFile) === fileKey).fromFile;
|
|
1161
|
+
const expectedBytes = fs.readFileSync(fileName);
|
|
1162
|
+
const expectedText = expectedBytes.toString("utf8");
|
|
1163
|
+
result.push({
|
|
1164
|
+
fileName,
|
|
1165
|
+
isNewFile: false,
|
|
1166
|
+
textChanges: [{ span: { start: 0, length: expectedText.length }, newText: rendered }],
|
|
1167
|
+
expectedExists: true,
|
|
1168
|
+
expectedText,
|
|
1169
|
+
expectedHash: crypto.createHash("sha256").update(expectedBytes).digest("hex")
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
return result.sort((left, right) => left.fileName.localeCompare(right.fileName));
|
|
1173
|
+
}
|
|
1174
|
+
function renderSemanticMoveNamedExport(moduleSpecifier, quote, valueEntities, typeEntities) {
|
|
1175
|
+
const specifiers = [
|
|
1176
|
+
...valueEntities,
|
|
1177
|
+
...typeEntities.map((name) => `type ${name}`)
|
|
1178
|
+
];
|
|
1179
|
+
if (specifiers.length <= 2 && (valueEntities.length === 0 || typeEntities.length === 0)) {
|
|
1180
|
+
return `export { ${specifiers.join(", ")} } from ${quote}${moduleSpecifier}${quote};`;
|
|
1181
|
+
}
|
|
1182
|
+
return [
|
|
1183
|
+
"export {",
|
|
1184
|
+
...specifiers.map((specifier) => ` ${specifier},`),
|
|
1185
|
+
`} from ${quote}${moduleSpecifier}${quote};`
|
|
1186
|
+
].join("\n");
|
|
1187
|
+
}
|
|
1188
|
+
function mergeOrAppendSemanticMoveNamedExport(input) {
|
|
1189
|
+
const matching = input.source.statements.filter((statement) => typescript_1.default.isExportDeclaration(statement) && statement.exportClause !== undefined && typescript_1.default.isNamedExports(statement.exportClause) &&
|
|
1190
|
+
statement.moduleSpecifier !== undefined && typescript_1.default.isStringLiteralLike(statement.moduleSpecifier) &&
|
|
1191
|
+
statement.moduleSpecifier.text === input.moduleSpecifier);
|
|
1192
|
+
const append = () => {
|
|
1193
|
+
const rendered = renderSemanticMoveNamedExport(input.moduleSpecifier, input.quote, input.valueEntities, input.typeEntities);
|
|
1194
|
+
const separator = input.text.length === 0 || input.text.endsWith("\n") ? "" : "\n";
|
|
1195
|
+
return `${input.text}${separator}${rendered}\n`;
|
|
1196
|
+
};
|
|
1197
|
+
if (matching.length === 0)
|
|
1198
|
+
return append();
|
|
1199
|
+
const indexes = matching.map((statement) => input.source.statements.indexOf(statement));
|
|
1200
|
+
if (indexes.some((value, index) => index > 0 && value !== indexes[index - 1] + 1))
|
|
1201
|
+
return append();
|
|
1202
|
+
const existingValues = [];
|
|
1203
|
+
const existingTypes = [];
|
|
1204
|
+
for (const statement of matching) {
|
|
1205
|
+
const clause = statement.exportClause;
|
|
1206
|
+
if (!clause || !typescript_1.default.isNamedExports(clause))
|
|
1207
|
+
return append();
|
|
1208
|
+
const statementValues = [];
|
|
1209
|
+
const statementTypes = [];
|
|
1210
|
+
for (const element of clause.elements) {
|
|
1211
|
+
if (element.propertyName || !typescript_1.default.isIdentifier(element.name))
|
|
1212
|
+
return append();
|
|
1213
|
+
(statement.isTypeOnly || element.isTypeOnly ? statementTypes : statementValues).push(element.name.text);
|
|
1214
|
+
}
|
|
1215
|
+
const rawSpecifier = statement.moduleSpecifier?.getText(input.source) ?? "";
|
|
1216
|
+
const statementQuote = rawSpecifier.startsWith("'") ? "'" : '"';
|
|
1217
|
+
if (statement.getText(input.source) !== renderSemanticMoveNamedExport(input.moduleSpecifier, statementQuote, statementValues, statementTypes))
|
|
1218
|
+
return append();
|
|
1219
|
+
existingValues.push(...statementValues);
|
|
1220
|
+
existingTypes.push(...statementTypes);
|
|
1221
|
+
}
|
|
1222
|
+
const valueEntities = [...new Set([...existingValues, ...input.valueEntities])];
|
|
1223
|
+
const valueNames = new Set(valueEntities);
|
|
1224
|
+
const typeEntities = [...new Set([...existingTypes, ...input.typeEntities])].filter((name) => !valueNames.has(name));
|
|
1225
|
+
const rendered = renderSemanticMoveNamedExport(input.moduleSpecifier, input.quote, valueEntities, typeEntities);
|
|
1226
|
+
const first = matching[0];
|
|
1227
|
+
const last = matching[matching.length - 1];
|
|
1228
|
+
return `${input.text.slice(0, first.getStart(input.source))}${rendered}${input.text.slice(last.getEnd())}`;
|
|
1229
|
+
}
|
|
1230
|
+
function collectSafeDynamicImportLiterals(source, moduleSpecifier, names) {
|
|
1231
|
+
const literals = [];
|
|
1232
|
+
const visit = (node) => {
|
|
1233
|
+
if (typescript_1.default.isCallExpression(node) && node.expression.kind === typescript_1.default.SyntaxKind.ImportKeyword &&
|
|
1234
|
+
node.arguments.length === 1 && typescript_1.default.isStringLiteralLike(node.arguments[0]) &&
|
|
1235
|
+
node.arguments[0].text === moduleSpecifier && analyzeDynamicImportUsage(source, node, names) === "movedOnly") {
|
|
1236
|
+
const literal = node.arguments[0];
|
|
1237
|
+
literals.push({ start: literal.getStart(source) + 1, length: literal.text.length });
|
|
1238
|
+
}
|
|
1239
|
+
typescript_1.default.forEachChild(node, visit);
|
|
1240
|
+
};
|
|
1241
|
+
visit(source);
|
|
1242
|
+
return literals;
|
|
1243
|
+
}
|
|
1244
|
+
function analyzeDynamicImportUsage(source, call, names) {
|
|
1245
|
+
const initializer = typescript_1.default.isAwaitExpression(call.parent) ? call.parent : call;
|
|
1246
|
+
const declaration = initializer.parent;
|
|
1247
|
+
if (!typescript_1.default.isVariableDeclaration(declaration) || declaration.initializer !== initializer || !typescript_1.default.isIdentifier(declaration.name)) {
|
|
1248
|
+
return "uncertain";
|
|
1249
|
+
}
|
|
1250
|
+
const binding = declaration.name;
|
|
1251
|
+
let moved = false;
|
|
1252
|
+
let retained = false;
|
|
1253
|
+
let uncertain = false;
|
|
1254
|
+
const visit = (node) => {
|
|
1255
|
+
if (uncertain)
|
|
1256
|
+
return;
|
|
1257
|
+
if (typescript_1.default.isIdentifier(node) && node.text === binding.text && node !== binding) {
|
|
1258
|
+
const parent = node.parent;
|
|
1259
|
+
let property = null;
|
|
1260
|
+
if (typescript_1.default.isPropertyAccessExpression(parent) && parent.expression === node) {
|
|
1261
|
+
property = parent.name.text;
|
|
1262
|
+
}
|
|
1263
|
+
else if (typescript_1.default.isElementAccessExpression(parent) && parent.expression === node &&
|
|
1264
|
+
parent.argumentExpression && typescript_1.default.isStringLiteralLike(parent.argumentExpression)) {
|
|
1265
|
+
property = parent.argumentExpression.text;
|
|
1266
|
+
}
|
|
1267
|
+
else {
|
|
1268
|
+
uncertain = true;
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
if (names.has(property))
|
|
1272
|
+
moved = true;
|
|
1273
|
+
else
|
|
1274
|
+
retained = true;
|
|
1275
|
+
}
|
|
1276
|
+
typescript_1.default.forEachChild(node, visit);
|
|
1277
|
+
};
|
|
1278
|
+
visit(source);
|
|
1279
|
+
if (uncertain)
|
|
1280
|
+
return "uncertain";
|
|
1281
|
+
if (moved && retained)
|
|
1282
|
+
return "mixed";
|
|
1283
|
+
if (moved)
|
|
1284
|
+
return "movedOnly";
|
|
1285
|
+
if (retained)
|
|
1286
|
+
return "unaffected";
|
|
1287
|
+
return "uncertain";
|
|
1288
|
+
}
|
|
1289
|
+
function verifyMovedEntityEdgeCoverage(sourceFile, entities, discovery, edits, coveredExportStarEdges = new Set()) {
|
|
1290
|
+
const names = new Set(entities.map((entity) => entity.name));
|
|
1291
|
+
const editsByFile = new Map(edits.map((edit) => [pathKey(edit.fileName), edit]));
|
|
1292
|
+
const sourceEdit = editsByFile.get(pathKey(sourceFile));
|
|
1293
|
+
const renderedSource = sourceEdit ? applyTextChanges(sourceEdit.expectedText, sourceEdit.textChanges) : fs.readFileSync(sourceFile, "utf8");
|
|
1294
|
+
const sourceRemainsModule = typescript_1.default.isExternalModule(typescript_1.default.createSourceFile(sourceFile, renderedSource, typescript_1.default.ScriptTarget.Latest, true, scriptKindForFile(sourceFile)));
|
|
1295
|
+
for (const edge of discovery.edges.filter((candidate) => samePath(candidate.targetFile, sourceFile))) {
|
|
1296
|
+
if (samePath(edge.fromFile, sourceFile) || !isRegularFile(edge.fromFile))
|
|
1297
|
+
continue;
|
|
1298
|
+
if (edge.kind === "exportStar" && coveredExportStarEdges.has(exportStarEdgeKey(edge)))
|
|
1299
|
+
continue;
|
|
1300
|
+
const originalText = fs.readFileSync(edge.fromFile, "utf8");
|
|
1301
|
+
const originalAffected = sourceRemainsModule
|
|
1302
|
+
? moduleEdgeCarriesMovedEntity(edge.kind, edge.moduleSpecifier, originalText, edge.fromFile, names)
|
|
1303
|
+
: containsModuleEdge(originalText, edge.fromFile, edge.moduleSpecifier);
|
|
1304
|
+
if (!originalAffected)
|
|
1305
|
+
continue;
|
|
1306
|
+
const edit = editsByFile.get(pathKey(edge.fromFile));
|
|
1307
|
+
const finalText = edit ? applyTextChanges(edit.expectedText, edit.textChanges) : originalText;
|
|
1308
|
+
const finalAffected = sourceRemainsModule
|
|
1309
|
+
? moduleEdgeCarriesMovedEntity(edge.kind, edge.moduleSpecifier, finalText, edge.fromFile, names)
|
|
1310
|
+
: containsModuleEdge(finalText, edge.fromFile, edge.moduleSpecifier);
|
|
1311
|
+
if (finalAffected) {
|
|
1312
|
+
return {
|
|
1313
|
+
ok: false,
|
|
1314
|
+
code: "SEMANTIC_MOVE_REQUIRED_EDGE_UNCHANGED",
|
|
1315
|
+
error: `TypeScript move left a required ${edge.kind} edge pointing at the old source: ${edge.fromFile}:${edge.line}.`
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return { ok: true };
|
|
1320
|
+
}
|
|
1321
|
+
function containsModuleEdge(text, fileName, moduleSpecifier) {
|
|
1322
|
+
const source = typescript_1.default.createSourceFile(fileName, text, typescript_1.default.ScriptTarget.Latest, true, scriptKindForFile(fileName));
|
|
1323
|
+
return containsModuleSpecifier(source, moduleSpecifier);
|
|
1324
|
+
}
|
|
1325
|
+
function moduleEdgeCarriesMovedEntity(kind, moduleSpecifier, text, fileName, names) {
|
|
1326
|
+
const source = typescript_1.default.createSourceFile(fileName, text, typescript_1.default.ScriptTarget.Latest, true, scriptKindForFile(fileName));
|
|
1327
|
+
if (kind === "dynamic") {
|
|
1328
|
+
let carries = false;
|
|
1329
|
+
const visit = (node) => {
|
|
1330
|
+
if (carries)
|
|
1331
|
+
return;
|
|
1332
|
+
if (typescript_1.default.isCallExpression(node) && node.expression.kind === typescript_1.default.SyntaxKind.ImportKeyword &&
|
|
1333
|
+
node.arguments.length === 1 && typescript_1.default.isStringLiteralLike(node.arguments[0]) &&
|
|
1334
|
+
node.arguments[0].text === moduleSpecifier && analyzeDynamicImportUsage(source, node, names) !== "unaffected") {
|
|
1335
|
+
carries = true;
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
typescript_1.default.forEachChild(node, visit);
|
|
1339
|
+
};
|
|
1340
|
+
visit(source);
|
|
1341
|
+
return carries;
|
|
1342
|
+
}
|
|
1343
|
+
let carries = false;
|
|
1344
|
+
source.forEachChild((node) => {
|
|
1345
|
+
if (carries)
|
|
1346
|
+
return;
|
|
1347
|
+
if (typescript_1.default.isImportDeclaration(node) && moduleText(node.moduleSpecifier) === moduleSpecifier) {
|
|
1348
|
+
const bindings = node.importClause?.namedBindings;
|
|
1349
|
+
if (bindings && typescript_1.default.isNamedImports(bindings)) {
|
|
1350
|
+
carries = bindings.elements.some((element) => names.has((element.propertyName ?? element.name).text));
|
|
1351
|
+
}
|
|
1352
|
+
else if (bindings && typescript_1.default.isNamespaceImport(bindings)) {
|
|
1353
|
+
carries = namespaceReferencesEntity(source, bindings.name.text, names);
|
|
1354
|
+
}
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
if (typescript_1.default.isExportDeclaration(node) && node.moduleSpecifier && moduleText(node.moduleSpecifier) === moduleSpecifier) {
|
|
1358
|
+
if (!node.exportClause || typescript_1.default.isNamespaceExport(node.exportClause))
|
|
1359
|
+
carries = true;
|
|
1360
|
+
else
|
|
1361
|
+
carries = node.exportClause.elements.some((element) => names.has((element.propertyName ?? element.name).text));
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
if (kind === "require" || kind === "importEquals") {
|
|
1365
|
+
carries = containsModuleSpecifier(node, moduleSpecifier);
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
return carries;
|
|
1369
|
+
}
|
|
1370
|
+
function namespaceReferencesEntity(source, namespaceName, names) {
|
|
1371
|
+
let found = false;
|
|
1372
|
+
const visit = (node) => {
|
|
1373
|
+
if (found)
|
|
1374
|
+
return;
|
|
1375
|
+
if (typescript_1.default.isPropertyAccessExpression(node) && typescript_1.default.isIdentifier(node.expression) && node.expression.text === namespaceName && names.has(node.name.text)) {
|
|
1376
|
+
found = true;
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
if (typescript_1.default.isQualifiedName(node) && typescript_1.default.isIdentifier(node.left) && node.left.text === namespaceName && names.has(node.right.text)) {
|
|
1380
|
+
found = true;
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
typescript_1.default.forEachChild(node, visit);
|
|
1384
|
+
};
|
|
1385
|
+
typescript_1.default.forEachChild(source, visit);
|
|
1386
|
+
return found;
|
|
1387
|
+
}
|
|
1388
|
+
function containsModuleSpecifier(node, moduleSpecifier) {
|
|
1389
|
+
let found = false;
|
|
1390
|
+
const visit = (candidate) => {
|
|
1391
|
+
if (found)
|
|
1392
|
+
return;
|
|
1393
|
+
if (typescript_1.default.isStringLiteralLike(candidate) && candidate.text === moduleSpecifier) {
|
|
1394
|
+
found = true;
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
typescript_1.default.forEachChild(candidate, visit);
|
|
1398
|
+
};
|
|
1399
|
+
visit(node);
|
|
1400
|
+
return found;
|
|
1401
|
+
}
|
|
1402
|
+
function moduleText(node) {
|
|
1403
|
+
return typescript_1.default.isStringLiteralLike(node) ? node.text : null;
|
|
1404
|
+
}
|
|
1405
|
+
function verifyBeforeHashes(results, edits, synthesizedFiles = new Set()) {
|
|
1406
|
+
if (results.some((result) => !result.configHash || result.configHash !== hashFile(result.configFilePath)))
|
|
1407
|
+
return false;
|
|
1408
|
+
for (const edit of edits.filter((item) => item.expectedExists)) {
|
|
1409
|
+
const observed = results.flatMap((result) => Object.entries(result.beforeHashes))
|
|
1410
|
+
.filter(([file]) => samePath(file, edit.fileName))
|
|
1411
|
+
.map(([, hash]) => hash);
|
|
1412
|
+
if ((observed.length === 0 && !synthesizedFiles.has(pathKey(edit.fileName))) ||
|
|
1413
|
+
observed.some((hash) => hash !== edit.expectedHash) || hashFile(edit.fileName) !== edit.expectedHash)
|
|
1414
|
+
return false;
|
|
1415
|
+
}
|
|
1416
|
+
return true;
|
|
1417
|
+
}
|
|
1418
|
+
function validateTextChanges(fileName, text, changes) {
|
|
1419
|
+
const ordered = [...changes].sort((left, right) => left.span.start - right.span.start || left.span.length - right.span.length);
|
|
1420
|
+
for (const [index, change] of ordered.entries()) {
|
|
1421
|
+
if (change.span.start < 0 || change.span.length < 0 || change.span.start + change.span.length > text.length) {
|
|
1422
|
+
return { ok: false, code: "SEMANTIC_MOVE_EDIT_SPAN_INVALID", error: `Invalid edit span in ${fileName}.` };
|
|
1423
|
+
}
|
|
1424
|
+
const previous = index > 0 ? ordered[index - 1] : undefined;
|
|
1425
|
+
if (previous && previous.span.start + previous.span.length > change.span.start) {
|
|
1426
|
+
return { ok: false, code: "SEMANTIC_MOVE_EDIT_OVERLAP", error: `Overlapping edits in ${fileName}.` };
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
return { ok: true };
|
|
1430
|
+
}
|
|
1431
|
+
function normalizeExistingTargetEndOfFileSentinel(input) {
|
|
1432
|
+
if (!input.enabled)
|
|
1433
|
+
return [...input.changes];
|
|
1434
|
+
const source = typescript_1.default.createSourceFile(input.fileName, input.expectedText, typescript_1.default.ScriptTarget.Latest, true, scriptKindForFile(input.fileName));
|
|
1435
|
+
if (source.statements.length > 0)
|
|
1436
|
+
return [...input.changes];
|
|
1437
|
+
return input.changes.map((change) => change.span.length === 0 && change.span.start === input.expectedText.length + 1
|
|
1438
|
+
? { ...change, span: { start: input.expectedText.length, length: 0 } }
|
|
1439
|
+
: change);
|
|
1440
|
+
}
|
|
1441
|
+
function normalizeFileEdit(edit) {
|
|
1442
|
+
return {
|
|
1443
|
+
fileName: path.normalize(edit.fileName),
|
|
1444
|
+
isNewFile: edit.isNewFile,
|
|
1445
|
+
textChanges: edit.textChanges.map((change) => ({
|
|
1446
|
+
span: { start: change.span.start, length: change.span.length },
|
|
1447
|
+
newText: change.newText
|
|
1448
|
+
}))
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
function editSignature(edit) {
|
|
1452
|
+
return JSON.stringify({ isNewFile: edit.isNewFile, textChanges: edit.textChanges });
|
|
1453
|
+
}
|
|
1454
|
+
function applyTextChanges(text, changes) {
|
|
1455
|
+
const ordered = [...changes].sort((left, right) => left.span.start - right.span.start || left.span.length - right.span.length);
|
|
1456
|
+
let cursor = 0;
|
|
1457
|
+
let output = "";
|
|
1458
|
+
for (const change of ordered) {
|
|
1459
|
+
output += text.slice(cursor, change.span.start) + change.newText;
|
|
1460
|
+
cursor = change.span.start + change.span.length;
|
|
1461
|
+
}
|
|
1462
|
+
return output + text.slice(cursor);
|
|
1463
|
+
}
|
|
1464
|
+
function ownersForFile(discovery, fileName) {
|
|
1465
|
+
return discovery.fileOwners.find((item) => samePath(item.file, fileName))?.ownerConfigs.map(path.normalize).sort() ?? [];
|
|
1466
|
+
}
|
|
1467
|
+
function discoverySummary(discovery) {
|
|
1468
|
+
return {
|
|
1469
|
+
filesScanned: discovery?.filesScanned ?? 0,
|
|
1470
|
+
bytesScanned: discovery?.bytesScanned ?? 0,
|
|
1471
|
+
consumerCount: discovery?.consumerFiles.length ?? 0,
|
|
1472
|
+
edgeCount: discovery?.edgeCount ?? 0,
|
|
1473
|
+
ownerConfigCount: discovery?.ownerConfigs.length ?? 0,
|
|
1474
|
+
workspaceFingerprint: discovery?.workspaceFingerprint ?? ""
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
function incomplete(code, error, root, sourceFile, targetFile, discovery, startedAt, snapshotHash = "", configWorkers = [], divergences = []) {
|
|
1478
|
+
return {
|
|
1479
|
+
complete: false,
|
|
1480
|
+
code,
|
|
1481
|
+
error,
|
|
1482
|
+
workspaceRoot: root,
|
|
1483
|
+
sourceFile,
|
|
1484
|
+
targetFile,
|
|
1485
|
+
targetExisted: fs.existsSync(targetFile),
|
|
1486
|
+
entities: [],
|
|
1487
|
+
edits: [],
|
|
1488
|
+
discovery,
|
|
1489
|
+
configWorkers,
|
|
1490
|
+
snapshotHash,
|
|
1491
|
+
planHash: "",
|
|
1492
|
+
totalMs: Date.now() - startedAt,
|
|
1493
|
+
...(divergences.length > 0 ? { divergences } : {})
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
function isValidIdentifierName(value) {
|
|
1497
|
+
const source = typescript_1.default.createSourceFile("name.ts", `const ${value}=1`, typescript_1.default.ScriptTarget.Latest, true, typescript_1.default.ScriptKind.TS);
|
|
1498
|
+
const parseDiagnostics = source.parseDiagnostics ?? [];
|
|
1499
|
+
const statement = source.statements[0];
|
|
1500
|
+
return parseDiagnostics.length === 0 && statement !== undefined && typescript_1.default.isVariableStatement(statement) &&
|
|
1501
|
+
statement.declarationList.declarations.length === 1 &&
|
|
1502
|
+
typescript_1.default.isIdentifier(statement.declarationList.declarations[0].name) &&
|
|
1503
|
+
statement.declarationList.declarations[0].name.text === value;
|
|
1504
|
+
}
|
|
1505
|
+
function scriptKindForFile(file) {
|
|
1506
|
+
const extension = path.extname(file).toLowerCase();
|
|
1507
|
+
if (extension === ".tsx")
|
|
1508
|
+
return typescript_1.default.ScriptKind.TSX;
|
|
1509
|
+
if ([".js", ".mjs", ".cjs"].includes(extension))
|
|
1510
|
+
return typescript_1.default.ScriptKind.JS;
|
|
1511
|
+
if (extension === ".jsx")
|
|
1512
|
+
return typescript_1.default.ScriptKind.JSX;
|
|
1513
|
+
return typescript_1.default.ScriptKind.TS;
|
|
1514
|
+
}
|
|
1515
|
+
function isRegularFile(file) {
|
|
1516
|
+
try {
|
|
1517
|
+
return fs.statSync(file).isFile();
|
|
1518
|
+
}
|
|
1519
|
+
catch {
|
|
1520
|
+
return false;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
function hashFiles(files) {
|
|
1524
|
+
const hash = crypto.createHash("sha256");
|
|
1525
|
+
for (const file of uniquePaths(files))
|
|
1526
|
+
hash.update(file).update("\0").update(fs.readFileSync(file)).update("\0");
|
|
1527
|
+
return hash.digest("hex");
|
|
1528
|
+
}
|
|
1529
|
+
function hashFile(file) {
|
|
1530
|
+
return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
|
1531
|
+
}
|
|
1532
|
+
function uniquePaths(files) {
|
|
1533
|
+
return [...new Map(files.map((file) => [pathKey(file), path.normalize(file)])).values()].sort();
|
|
1534
|
+
}
|
|
1535
|
+
function isInside(root, file) {
|
|
1536
|
+
const relative = path.relative(root, path.resolve(file));
|
|
1537
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
1538
|
+
}
|
|
1539
|
+
function samePath(left, right) {
|
|
1540
|
+
return pathKey(left) === pathKey(right);
|
|
1541
|
+
}
|
|
1542
|
+
function pathKey(file) {
|
|
1543
|
+
const normalized = path.normalize(file);
|
|
1544
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
1545
|
+
}
|
|
1546
|
+
function errorMessage(error) {
|
|
1547
|
+
return error instanceof Error ? error.message : String(error);
|
|
1548
|
+
}
|
|
1549
|
+
function readErrorCode(error) {
|
|
1550
|
+
return error && typeof error === "object" && typeof error.code === "string"
|
|
1551
|
+
? error.code
|
|
1552
|
+
: null;
|
|
1553
|
+
}
|