@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
|
@@ -1,9 +1,1553 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isInspectGraphRequest = isInspectGraphRequest;
|
|
37
|
+
exports.runInspectGraph = runInspectGraph;
|
|
38
|
+
exports.formatGraphAgentText = formatGraphAgentText;
|
|
39
|
+
exports.buildInspectGraphModel = buildInspectGraphModel;
|
|
40
|
+
const fs = __importStar(require("node:fs"));
|
|
41
|
+
const path = __importStar(require("node:path"));
|
|
42
|
+
const glob_match_1 = require("../config/glob-match");
|
|
43
|
+
const local_config_1 = require("../config/local-config");
|
|
44
|
+
const agent_text_1 = require("../shared/agent-text");
|
|
45
|
+
const errors_1 = require("../shared/errors");
|
|
46
|
+
const session_workspace_1 = require("./session-workspace");
|
|
47
|
+
const json_file_input_1 = require("./json-file-input");
|
|
48
|
+
const GRAPH_SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts"];
|
|
49
|
+
const GRAPH_IMPORT_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
50
|
+
const GRAPH_EXCLUDED_DIRS = new Set(["node_modules", ".git", "dist", "out", "build", ".tmp"]);
|
|
51
|
+
const DEFAULT_EDGE_LIMIT = 12;
|
|
52
|
+
const DEFAULT_LIST_LIMIT = 8;
|
|
53
|
+
const TSCONFIG_FILE_NAME = "tsconfig.json";
|
|
54
|
+
function isInspectGraphRequest(restArgs, stdinPayload) {
|
|
55
|
+
return restArgs[0] === "graph";
|
|
56
|
+
}
|
|
57
|
+
function runInspectGraph(cwd, restArgs, stdinPayload) {
|
|
58
|
+
const localConfig = (0, local_config_1.resolveLocalCliConfig)(cwd);
|
|
59
|
+
const payload = readGraphInputPayload(cwd, restArgs, stdinPayload);
|
|
60
|
+
if (payload) {
|
|
61
|
+
const requests = expandGraphPayloadRequests(payload);
|
|
62
|
+
const results = requests.map((request, index) => {
|
|
63
|
+
if (!isObject(request)) {
|
|
64
|
+
throw new errors_1.CliError("INVALID_GRAPH_REQUEST", `inspect graph request at index ${index} must be an object.`);
|
|
65
|
+
}
|
|
66
|
+
return runGraphRequest(cwd, parseGraphOptionsFromPayload(cwd, request, localConfig.config.inspect.graph.exclude));
|
|
67
|
+
});
|
|
68
|
+
return results.length === 1
|
|
69
|
+
? results[0]
|
|
70
|
+
: formatGraphBatchResponse(results);
|
|
71
|
+
}
|
|
72
|
+
return runGraphRequest(cwd, parseGraphOptions(cwd, restArgs, localConfig.config.inspect.graph.exclude));
|
|
73
|
+
}
|
|
74
|
+
function runGraphRequest(cwd, options) {
|
|
75
|
+
const model = options.fromFile ? readGraphModel(cwd, options.fromFile) : buildGraphModel(cwd, options);
|
|
76
|
+
const writtenTo = options.toFile ? writeGraphModel(cwd, options.toFile, model) : null;
|
|
77
|
+
const saved = options.save ? (0, session_workspace_1.saveSessionOutputJson)(cwd, "graph", options.save, model) : null;
|
|
78
|
+
const response = formatGraphResponse(cwd, model, {
|
|
79
|
+
focus: options.focus,
|
|
80
|
+
depth: options.depth,
|
|
81
|
+
writtenTo,
|
|
82
|
+
symbols: options.symbols,
|
|
83
|
+
symbolUsage: options.symbolUsage,
|
|
84
|
+
sections: options.sections,
|
|
85
|
+
...(options.view !== undefined ? { view: options.view } : {})
|
|
86
|
+
});
|
|
87
|
+
if (saved) {
|
|
88
|
+
response.saved = saved.saved;
|
|
89
|
+
response.next = `Focus with: aib inspect graph --from-file ${saved.saved} --focus <file>`;
|
|
90
|
+
}
|
|
91
|
+
return (0, agent_text_1.attachAgentText)(response, formatGraphAgentText(response));
|
|
92
|
+
}
|
|
93
|
+
function formatGraphBatchResponse(results) {
|
|
94
|
+
const saved = results
|
|
95
|
+
.map((result) => typeof result.saved === "string" ? result.saved : "")
|
|
96
|
+
.filter((value) => value !== "");
|
|
97
|
+
const response = {
|
|
98
|
+
operation: "inspect.graphBatch",
|
|
99
|
+
summary: `${results.length} graph requests${saved.length > 0 ? `, ${saved.length} saved` : ""}`,
|
|
100
|
+
results: results.map(formatGraphBatchResult)
|
|
101
|
+
};
|
|
102
|
+
if (saved.length > 0) {
|
|
103
|
+
response.saved = saved;
|
|
104
|
+
response.next = "Focus a saved graph with: aib inspect graph --from-file <saved> --focus <file>";
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
response.next = "Focus files with --focus, or inspect exports/code on relevant files.";
|
|
108
|
+
}
|
|
109
|
+
return (0, agent_text_1.attachAgentText)(response, formatGraphAgentText(response));
|
|
110
|
+
}
|
|
111
|
+
function formatGraphBatchResult(result) {
|
|
112
|
+
const entries = Object.entries(result).filter(([key]) => key !== "operation" &&
|
|
113
|
+
key !== "last" &&
|
|
114
|
+
key !== "next" &&
|
|
115
|
+
!(key === "graphFile" && typeof result.saved === "string"));
|
|
116
|
+
return Object.fromEntries(entries);
|
|
117
|
+
}
|
|
118
|
+
function formatGraphAgentText(response) {
|
|
119
|
+
const operation = typeof response.operation === "string" ? response.operation : "";
|
|
120
|
+
if (operation !== "inspect.graph" && operation !== "inspect.graphBatch") {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const lines = [operation];
|
|
124
|
+
if (typeof response.summary === "string") {
|
|
125
|
+
lines.push(response.summary);
|
|
126
|
+
}
|
|
127
|
+
const batchResults = Array.isArray(response.results) ? response.results : null;
|
|
128
|
+
if (batchResults) {
|
|
129
|
+
appendGraphScalar(lines, "view", response.view);
|
|
130
|
+
appendGraphScalar(lines, "excluded", response.excluded);
|
|
131
|
+
appendGraphPaths(lines, response.paths);
|
|
132
|
+
}
|
|
133
|
+
if (batchResults) {
|
|
134
|
+
batchResults.forEach((item, index) => {
|
|
135
|
+
if (!isObject(item)) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
lines.push(`[${index + 1}] ${typeof item.summary === "string" ? item.summary : "graph result"}`);
|
|
139
|
+
appendGraphResponseBody(lines, item);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
appendGraphResponseBody(lines, response);
|
|
144
|
+
}
|
|
145
|
+
if (!batchResults) {
|
|
146
|
+
appendGraphList(lines, "saved", response.saved);
|
|
147
|
+
}
|
|
148
|
+
appendGraphScalar(lines, "next", response.next);
|
|
149
|
+
return lines.join("\n");
|
|
150
|
+
}
|
|
151
|
+
function appendGraphResponseBody(lines, response) {
|
|
152
|
+
appendGraphScalar(lines, "view", response.view);
|
|
153
|
+
appendGraphScalar(lines, "excluded", response.excluded);
|
|
154
|
+
appendGraphPaths(lines, response.paths);
|
|
155
|
+
appendGraphList(lines, "hubs", response.hubs);
|
|
156
|
+
appendGraphList(lines, "scopeEntrypoints", response.scopeEntrypoints);
|
|
157
|
+
appendGraphList(lines, "scopeLeaves", response.scopeLeaves);
|
|
158
|
+
appendGraphList(lines, "edges", response.edges);
|
|
159
|
+
appendGraphFocus(lines, response.focus);
|
|
160
|
+
appendGraphScalar(lines, "saved", response.saved);
|
|
161
|
+
}
|
|
162
|
+
function appendGraphFocus(lines, focusValue) {
|
|
163
|
+
if (!Array.isArray(focusValue) || focusValue.length === 0) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
lines.push("focus:");
|
|
167
|
+
for (const rawFocus of focusValue) {
|
|
168
|
+
if (!isObject(rawFocus)) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const file = typeof rawFocus.file === "string" ? rawFocus.file : "unknown";
|
|
172
|
+
lines.push(`${file}:`);
|
|
173
|
+
appendGraphList(lines, "imports", rawFocus.imports);
|
|
174
|
+
appendGraphList(lines, "symbols", rawFocus.symbols);
|
|
175
|
+
appendGraphUsage(lines, rawFocus.usage);
|
|
176
|
+
appendGraphList(lines, "importsOther", rawFocus.importsOther);
|
|
177
|
+
appendGraphList(lines, "importedBy", rawFocus.importedBy);
|
|
178
|
+
appendGraphList(lines, "external", rawFocus.external);
|
|
179
|
+
appendGraphList(lines, "omittedLocalImports", rawFocus.omittedLocalImports);
|
|
180
|
+
appendGraphScalar(lines, "error", rawFocus.error);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function appendGraphUsage(lines, usageValue) {
|
|
184
|
+
if (!isObject(usageValue)) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
for (const key of ["value", "type", "unknown"]) {
|
|
188
|
+
appendGraphList(lines, `usage.${key}`, usageValue[key]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function appendGraphPaths(lines, pathsValue) {
|
|
192
|
+
if (!isObject(pathsValue) || Object.keys(pathsValue).length === 0) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
lines.push("paths:");
|
|
196
|
+
for (const [token, prefix] of Object.entries(pathsValue)) {
|
|
197
|
+
if (typeof prefix === "string") {
|
|
198
|
+
lines.push(`${token} = ${prefix}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function appendGraphScalar(lines, label, value) {
|
|
203
|
+
if (typeof value === "string" && value !== "") {
|
|
204
|
+
lines.push(`${label}: ${value}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function appendGraphList(lines, label, value) {
|
|
208
|
+
const items = Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
|
|
209
|
+
if (items.length === 0) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
lines.push(`${label}:`);
|
|
213
|
+
for (const item of items) {
|
|
214
|
+
lines.push(item);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function readGraphInputPayload(cwd, restArgs, stdinPayload) {
|
|
218
|
+
if (stdinPayload !== undefined) {
|
|
219
|
+
if (!isObject(stdinPayload)) {
|
|
220
|
+
throw new errors_1.CliError("INVALID_GRAPH_STDIN", "inspect graph --stdin expects a JSON object payload.");
|
|
221
|
+
}
|
|
222
|
+
return stdinPayload;
|
|
223
|
+
}
|
|
224
|
+
const fromJson = readFlagValue(restArgs, "--from-json");
|
|
225
|
+
return fromJson ? (0, json_file_input_1.readJsonObjectFile)(cwd, fromJson, "INVALID_GRAPH_JSON_FILE") : null;
|
|
226
|
+
}
|
|
227
|
+
function expandGraphPayloadRequests(payload) {
|
|
228
|
+
return Array.isArray(payload.requests) ? payload.requests : [payload];
|
|
229
|
+
}
|
|
230
|
+
function parseGraphOptions(cwd, restArgs, configExcludePatterns) {
|
|
231
|
+
const scopes = [];
|
|
232
|
+
const focus = [];
|
|
233
|
+
const cliExcludePatterns = [];
|
|
234
|
+
const excludeFromFiles = [];
|
|
235
|
+
let depth;
|
|
236
|
+
let view;
|
|
237
|
+
let toFile;
|
|
238
|
+
let fromFile;
|
|
239
|
+
let save;
|
|
240
|
+
let symbols = (0, session_workspace_1.readSessionBooleanPref)(cwd, "inspect.graph.symbols", false);
|
|
241
|
+
let symbolUsage = (0, session_workspace_1.readSessionBooleanPref)(cwd, "inspect.graph.symbolUsage", false);
|
|
242
|
+
for (let index = 1; index < restArgs.length; index += 1) {
|
|
243
|
+
const arg = restArgs[index];
|
|
244
|
+
if (!arg) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (arg === "--focus") {
|
|
248
|
+
const value = restArgs[index + 1];
|
|
249
|
+
if (!value) {
|
|
250
|
+
throw new errors_1.CliError("MISSING_FLAG_VALUE", "Expected a value after --focus.");
|
|
251
|
+
}
|
|
252
|
+
focus.push(toGraphPath(cwd, resolveMaybeRelative(cwd, value)));
|
|
253
|
+
index += 1;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (arg === "--depth") {
|
|
257
|
+
const value = restArgs[index + 1];
|
|
258
|
+
const parsed = value === undefined ? Number.NaN : Number.parseInt(value, 10);
|
|
259
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 2) {
|
|
260
|
+
throw new errors_1.CliError("INVALID_GRAPH_DEPTH", "inspect graph --depth expects 0, 1, or 2.");
|
|
261
|
+
}
|
|
262
|
+
depth = parsed;
|
|
263
|
+
index += 1;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (arg === "--view") {
|
|
267
|
+
const value = restArgs[index + 1];
|
|
268
|
+
if (!value) {
|
|
269
|
+
throw new errors_1.CliError("MISSING_FLAG_VALUE", "Expected a value after --view.");
|
|
270
|
+
}
|
|
271
|
+
view = readGraphView(value);
|
|
272
|
+
index += 1;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (arg === "--to-file") {
|
|
276
|
+
const value = restArgs[index + 1];
|
|
277
|
+
if (!value) {
|
|
278
|
+
throw new errors_1.CliError("MISSING_FLAG_VALUE", "Expected a value after --to-file.");
|
|
279
|
+
}
|
|
280
|
+
toFile = value;
|
|
281
|
+
index += 1;
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (arg === "--from-file") {
|
|
285
|
+
const value = restArgs[index + 1];
|
|
286
|
+
if (!value) {
|
|
287
|
+
throw new errors_1.CliError("MISSING_FLAG_VALUE", "Expected a value after --from-file.");
|
|
288
|
+
}
|
|
289
|
+
fromFile = value;
|
|
290
|
+
index += 1;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (arg === "--save") {
|
|
294
|
+
const value = restArgs[index + 1];
|
|
295
|
+
if (!value) {
|
|
296
|
+
throw new errors_1.CliError("MISSING_FLAG_VALUE", "Expected a value after --save.");
|
|
297
|
+
}
|
|
298
|
+
save = value;
|
|
299
|
+
index += 1;
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (arg === "--exclude") {
|
|
303
|
+
const value = restArgs[index + 1];
|
|
304
|
+
if (!value) {
|
|
305
|
+
throw new errors_1.CliError("MISSING_FLAG_VALUE", "Expected a value after --exclude.");
|
|
306
|
+
}
|
|
307
|
+
cliExcludePatterns.push(value);
|
|
308
|
+
index += 1;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (arg === "--exclude-from") {
|
|
312
|
+
const value = restArgs[index + 1];
|
|
313
|
+
if (!value) {
|
|
314
|
+
throw new errors_1.CliError("MISSING_FLAG_VALUE", "Expected a value after --exclude-from.");
|
|
315
|
+
}
|
|
316
|
+
excludeFromFiles.push(value);
|
|
317
|
+
index += 1;
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
if (arg === "--symbols") {
|
|
321
|
+
symbols = true;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (arg === "--symbol-usage") {
|
|
325
|
+
symbols = true;
|
|
326
|
+
symbolUsage = true;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (arg.startsWith("--")) {
|
|
330
|
+
throw new errors_1.CliError("UNKNOWN_INSPECT_GRAPH_FLAG", `Unknown inspect graph flag: ${arg}`);
|
|
331
|
+
}
|
|
332
|
+
scopes.push(toGraphPath(cwd, resolveMaybeRelative(cwd, arg)));
|
|
333
|
+
}
|
|
334
|
+
if (fromFile && scopes.length > 0) {
|
|
335
|
+
throw new errors_1.CliError("GRAPH_INPUT_SOURCE_CONFLICT", "Use either inspect graph <scopes...> or --from-file, not both.");
|
|
336
|
+
}
|
|
337
|
+
if (toFile && save) {
|
|
338
|
+
throw new errors_1.CliError("GRAPH_OUTPUT_TARGET_CONFLICT", "Use either inspect graph --to-file or --save, not both.");
|
|
339
|
+
}
|
|
340
|
+
if (!fromFile && scopes.length === 0) {
|
|
341
|
+
throw new errors_1.CliError("GRAPH_SCOPE_REQUIRED", "inspect graph expects at least one file or directory scope.");
|
|
342
|
+
}
|
|
343
|
+
const effectiveDepth = depth ?? graphViewDepth(view ?? defaultGraphView(cwd, scopes, fromFile));
|
|
344
|
+
const effectiveView = depth !== undefined ? graphViewFromDepth(depth) : view ?? defaultGraphView(cwd, scopes, fromFile);
|
|
345
|
+
return {
|
|
346
|
+
scopes,
|
|
347
|
+
focus,
|
|
348
|
+
depth: effectiveDepth,
|
|
349
|
+
symbols: symbols || symbolUsage,
|
|
350
|
+
symbolUsage,
|
|
351
|
+
sections: readGraphSectionsFromPrefs(cwd),
|
|
352
|
+
excludePatterns: uniqueSorted([
|
|
353
|
+
...configExcludePatterns,
|
|
354
|
+
...excludeFromFiles.flatMap((file) => readExcludePatternsFile(cwd, file)),
|
|
355
|
+
...cliExcludePatterns
|
|
356
|
+
]),
|
|
357
|
+
...(save !== undefined ? { save } : {}),
|
|
358
|
+
...(toFile !== undefined ? { toFile } : {}),
|
|
359
|
+
...(fromFile !== undefined ? { fromFile } : {}),
|
|
360
|
+
...(effectiveView !== undefined ? { view: effectiveView } : {})
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function parseGraphOptionsFromPayload(cwd, payload, configExcludePatterns) {
|
|
364
|
+
const rawScopes = readStringArray(payload.scopes) ?? readStringArray(payload.scope) ?? readStringArray(payload.files) ?? readStringArray(payload.file) ?? [];
|
|
365
|
+
const rawFocus = readStringArray(payload.focus) ?? [];
|
|
366
|
+
const rawExclude = readStringArray(payload.exclude) ?? readStringArray(payload.excludePatterns) ?? [];
|
|
367
|
+
const rawExcludeFrom = readStringArray(payload.excludeFrom) ?? [];
|
|
368
|
+
const fromFile = readString(payload.fromFile) ?? readString(payload.from);
|
|
369
|
+
const toFile = readString(payload.toFile) ?? readString(payload.outputFile);
|
|
370
|
+
const save = readString(payload.save);
|
|
371
|
+
const requestedDepth = readOptionalGraphDepth(payload.depth);
|
|
372
|
+
const requestedView = payload.view !== undefined ? readGraphView(payload.view) : undefined;
|
|
373
|
+
const requestedSymbolUsage = readBoolean(payload.symbolUsage);
|
|
374
|
+
const effectiveSymbolUsage = requestedSymbolUsage ?? (0, session_workspace_1.readSessionBooleanPref)(cwd, "inspect.graph.symbolUsage", false);
|
|
375
|
+
const effectiveSymbols = effectiveSymbolUsage ||
|
|
376
|
+
(readBoolean(payload.symbols) ?? (0, session_workspace_1.readSessionBooleanPref)(cwd, "inspect.graph.symbols", false));
|
|
377
|
+
if (fromFile && rawScopes.length > 0) {
|
|
378
|
+
throw new errors_1.CliError("GRAPH_INPUT_SOURCE_CONFLICT", "Use either inspect graph scopes or fromFile/from, not both.");
|
|
379
|
+
}
|
|
380
|
+
if (toFile && save) {
|
|
381
|
+
throw new errors_1.CliError("GRAPH_OUTPUT_TARGET_CONFLICT", "Use either toFile or save, not both.");
|
|
382
|
+
}
|
|
383
|
+
if (!fromFile && rawScopes.length === 0) {
|
|
384
|
+
throw new errors_1.CliError("GRAPH_SCOPE_REQUIRED", "inspect graph JSON expects scope/scopes or fromFile/from.");
|
|
385
|
+
}
|
|
386
|
+
const scopes = rawScopes.map((scope) => toGraphPath(cwd, resolveMaybeRelative(cwd, scope)));
|
|
387
|
+
const effectiveDepth = requestedDepth ?? graphViewDepth(requestedView ?? defaultGraphView(cwd, scopes, fromFile ?? undefined));
|
|
388
|
+
const effectiveView = requestedDepth !== undefined ? graphViewFromDepth(requestedDepth) : requestedView ?? defaultGraphView(cwd, scopes, fromFile ?? undefined);
|
|
389
|
+
return {
|
|
390
|
+
scopes,
|
|
391
|
+
focus: rawFocus.map((focus) => toGraphPath(cwd, resolveMaybeRelative(cwd, focus))),
|
|
392
|
+
depth: effectiveDepth,
|
|
393
|
+
symbols: effectiveSymbols,
|
|
394
|
+
symbolUsage: effectiveSymbolUsage,
|
|
395
|
+
sections: readGraphSectionsFromPayload(cwd, payload),
|
|
396
|
+
excludePatterns: uniqueSorted([
|
|
397
|
+
...configExcludePatterns,
|
|
398
|
+
...rawExcludeFrom.flatMap((file) => readExcludePatternsFile(cwd, file)),
|
|
399
|
+
...rawExclude
|
|
400
|
+
]),
|
|
401
|
+
...(save ? { save } : {}),
|
|
402
|
+
...(toFile ? { toFile } : {}),
|
|
403
|
+
...(fromFile ? { fromFile } : {}),
|
|
404
|
+
...(effectiveView !== undefined ? { view: effectiveView } : {})
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function buildGraphModel(cwd, options) {
|
|
408
|
+
const tsconfigResolver = createTsConfigResolver(cwd);
|
|
409
|
+
const excludeFilter = createGraphExcludeFilter(cwd, options.excludePatterns);
|
|
410
|
+
const files = collectScopedFiles(cwd, options.scopes, excludeFilter);
|
|
411
|
+
const visited = new Set(files);
|
|
412
|
+
let frontier = files;
|
|
413
|
+
for (let level = 0; level < options.depth; level += 1) {
|
|
414
|
+
const next = new Set();
|
|
415
|
+
for (const file of frontier) {
|
|
416
|
+
for (const resolved of readLocalImports(cwd, file, tsconfigResolver).imports) {
|
|
417
|
+
if (!excludeFilter.isExcluded(resolved) && !visited.has(resolved)) {
|
|
418
|
+
visited.add(resolved);
|
|
419
|
+
next.add(resolved);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
frontier = Array.from(next).sort();
|
|
424
|
+
}
|
|
425
|
+
const allFiles = Array.from(visited).sort();
|
|
426
|
+
const graphPathSet = new Set(allFiles.map((file) => toGraphPath(cwd, file)));
|
|
427
|
+
const edgeMap = new Map();
|
|
428
|
+
const externalByFile = new Map();
|
|
429
|
+
const omittedLocalImportsByFile = new Map();
|
|
430
|
+
for (const file of allFiles) {
|
|
431
|
+
const parsed = readLocalImports(cwd, file, tsconfigResolver);
|
|
432
|
+
const from = toGraphPath(cwd, file);
|
|
433
|
+
externalByFile.set(from, parsed.external);
|
|
434
|
+
const omittedLocalImports = [];
|
|
435
|
+
for (const target of parsed.imports) {
|
|
436
|
+
if (excludeFilter.isExcluded(target)) {
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
const to = toGraphPath(cwd, target);
|
|
440
|
+
if (!graphPathSet.has(to)) {
|
|
441
|
+
omittedLocalImports.push(to);
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
edgeMap.set(`${from}\n${to}\n${parsed.specifiers.get(target) ?? ""}`, {
|
|
445
|
+
from,
|
|
446
|
+
to,
|
|
447
|
+
specifier: parsed.specifiers.get(target) ?? "",
|
|
448
|
+
resolution: parsed.resolutions.get(target) ?? "relative",
|
|
449
|
+
symbols: parsed.symbols.get(target) ?? []
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
if (omittedLocalImports.length > 0) {
|
|
453
|
+
omittedLocalImportsByFile.set(from, uniqueSorted(omittedLocalImports));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const edges = Array.from(edgeMap.values()).sort(compareEdges);
|
|
457
|
+
const fileMap = new Map();
|
|
458
|
+
for (const file of allFiles) {
|
|
459
|
+
const graphPath = toGraphPath(cwd, file);
|
|
460
|
+
const filePayload = {
|
|
461
|
+
file: graphPath,
|
|
462
|
+
imports: [],
|
|
463
|
+
importedBy: [],
|
|
464
|
+
external: externalByFile.get(graphPath) ?? []
|
|
465
|
+
};
|
|
466
|
+
const omittedLocalImports = omittedLocalImportsByFile.get(graphPath);
|
|
467
|
+
if (omittedLocalImports && omittedLocalImports.length > 0) {
|
|
468
|
+
filePayload.omittedLocalImports = omittedLocalImports;
|
|
469
|
+
}
|
|
470
|
+
fileMap.set(graphPath, filePayload);
|
|
471
|
+
}
|
|
472
|
+
for (const edge of edges) {
|
|
473
|
+
fileMap.get(edge.from)?.imports.push(edge.to);
|
|
474
|
+
fileMap.get(edge.to)?.importedBy.push(edge.from);
|
|
475
|
+
}
|
|
476
|
+
const externalPackages = uniqueSorted(Array.from(externalByFile.values()).flat());
|
|
477
|
+
const excluded = excludeFilter.excluded.size > 0
|
|
478
|
+
? {
|
|
479
|
+
count: excludeFilter.excluded.size,
|
|
480
|
+
patterns: options.excludePatterns
|
|
481
|
+
}
|
|
482
|
+
: undefined;
|
|
483
|
+
return {
|
|
484
|
+
operation: "inspect.graph",
|
|
485
|
+
version: 1,
|
|
486
|
+
root: path.resolve(cwd),
|
|
487
|
+
scopes: options.scopes,
|
|
488
|
+
generatedAt: new Date().toISOString(),
|
|
489
|
+
files: Array.from(fileMap.values()).sort((left, right) => left.file.localeCompare(right.file)),
|
|
490
|
+
edges,
|
|
491
|
+
externalPackages,
|
|
492
|
+
timeoutHint: buildGraphTimeoutHint(fileMap.size, edges.length, options.view),
|
|
493
|
+
...(excluded ? { excluded } : {})
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
function buildInspectGraphModel(cwd, options) {
|
|
497
|
+
return buildGraphModel(cwd, {
|
|
498
|
+
scopes: options.scopes,
|
|
499
|
+
focus: [],
|
|
500
|
+
depth: options.depth,
|
|
501
|
+
symbols: true,
|
|
502
|
+
symbolUsage: options.symbolUsage ?? true,
|
|
503
|
+
sections: {
|
|
504
|
+
hubs: false,
|
|
505
|
+
entrypoints: false,
|
|
506
|
+
leaves: false,
|
|
507
|
+
edges: true
|
|
508
|
+
},
|
|
509
|
+
excludePatterns: options.excludePatterns ?? []
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function buildGraphTimeoutHint(fileCount, edgeCount, view) {
|
|
513
|
+
const viewMultiplier = view === "expanded" ? 2 : view === "neighbors" ? 1.5 : 1;
|
|
514
|
+
const recommendedTimeoutMs = Math.min(600_000, Math.max(60_000, Math.round((30_000 + fileCount * 250 + edgeCount * 100) * viewMultiplier)));
|
|
515
|
+
return {
|
|
516
|
+
recommendedTimeoutMs,
|
|
517
|
+
fileCount,
|
|
518
|
+
edgeCount,
|
|
519
|
+
view: view ?? "local"
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
function collectScopedFiles(cwd, scopes, excludeFilter) {
|
|
523
|
+
const files = new Set();
|
|
524
|
+
for (const scope of scopes) {
|
|
525
|
+
const absolute = resolveMaybeRelative(cwd, scope);
|
|
526
|
+
if (!fs.existsSync(absolute)) {
|
|
527
|
+
throw new errors_1.CliError("GRAPH_SCOPE_NOT_FOUND", `Graph scope does not exist: ${scope}`);
|
|
528
|
+
}
|
|
529
|
+
const stat = fs.statSync(absolute);
|
|
530
|
+
if (stat.isDirectory()) {
|
|
531
|
+
for (const file of walkSourceFiles(absolute)) {
|
|
532
|
+
if (!excludeFilter.isExcluded(file)) {
|
|
533
|
+
files.add(file);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
if (stat.isFile() && isGraphSourceFile(absolute) && !excludeFilter.isExcluded(absolute)) {
|
|
539
|
+
files.add(absolute);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return Array.from(files).sort();
|
|
543
|
+
}
|
|
544
|
+
function createGraphExcludeFilter(cwd, patterns) {
|
|
545
|
+
const normalizedPatterns = uniqueSorted(patterns.map((pattern) => normalizeGraphPath(pattern.trim())).filter(Boolean));
|
|
546
|
+
const excluded = new Set();
|
|
547
|
+
return {
|
|
548
|
+
patterns: normalizedPatterns,
|
|
549
|
+
excluded,
|
|
550
|
+
isExcluded(file) {
|
|
551
|
+
if (normalizedPatterns.length === 0) {
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
const graphPath = toGraphPath(cwd, file);
|
|
555
|
+
const matches = (0, glob_match_1.matchesAnyGlob)(graphPath, normalizedPatterns);
|
|
556
|
+
if (matches) {
|
|
557
|
+
excluded.add(graphPath);
|
|
558
|
+
}
|
|
559
|
+
return matches;
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function walkSourceFiles(dir) {
|
|
564
|
+
const files = [];
|
|
565
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
566
|
+
if (entry.isDirectory()) {
|
|
567
|
+
if (!GRAPH_EXCLUDED_DIRS.has(entry.name)) {
|
|
568
|
+
files.push(...walkSourceFiles(path.join(dir, entry.name)));
|
|
569
|
+
}
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const filePath = path.join(dir, entry.name);
|
|
573
|
+
if (entry.isFile() && isGraphSourceFile(filePath)) {
|
|
574
|
+
files.push(filePath);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return files;
|
|
578
|
+
}
|
|
579
|
+
function readExcludePatternsFile(cwd, rawFile) {
|
|
580
|
+
const filePath = resolveMaybeRelative(cwd, rawFile);
|
|
581
|
+
try {
|
|
582
|
+
return fs.readFileSync(filePath, "utf8")
|
|
583
|
+
.split(/\r?\n/)
|
|
584
|
+
.map((line) => line.trim())
|
|
585
|
+
.filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
586
|
+
}
|
|
587
|
+
catch {
|
|
588
|
+
throw new errors_1.CliError("GRAPH_EXCLUDE_FILE_NOT_FOUND", `Could not read inspect graph --exclude-from file: ${rawFile}`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
function readLocalImports(cwd, file, tsconfigResolver) {
|
|
592
|
+
const text = fs.readFileSync(file, "utf8");
|
|
593
|
+
const imports = new Set();
|
|
594
|
+
const external = new Set();
|
|
595
|
+
const specifiers = new Map();
|
|
596
|
+
const resolutions = new Map();
|
|
597
|
+
const symbols = new Map();
|
|
598
|
+
for (const reference of collectModuleReferences(text)) {
|
|
599
|
+
const { specifier } = reference;
|
|
600
|
+
if (specifier.startsWith(".")) {
|
|
601
|
+
const resolved = resolveLocalImport(file, specifier);
|
|
602
|
+
if (resolved) {
|
|
603
|
+
imports.add(resolved);
|
|
604
|
+
specifiers.set(resolved, specifier);
|
|
605
|
+
resolutions.set(resolved, "relative");
|
|
606
|
+
symbols.set(resolved, mergeSymbols(symbols.get(resolved), reference.symbols));
|
|
607
|
+
}
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
const resolved = tsconfigResolver.resolve(file, specifier);
|
|
611
|
+
if (resolved) {
|
|
612
|
+
imports.add(resolved);
|
|
613
|
+
specifiers.set(resolved, specifier);
|
|
614
|
+
resolutions.set(resolved, "tsconfig-paths");
|
|
615
|
+
symbols.set(resolved, mergeSymbols(symbols.get(resolved), reference.symbols));
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
external.add(toExternalPackageName(specifier));
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
imports: Array.from(imports).sort(),
|
|
622
|
+
external: Array.from(external).sort(),
|
|
623
|
+
specifiers,
|
|
624
|
+
resolutions,
|
|
625
|
+
symbols
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
function collectModuleReferences(text) {
|
|
629
|
+
const references = [];
|
|
630
|
+
const seen = new Set();
|
|
631
|
+
const importText = stripCommentsOutsideStrings(text);
|
|
632
|
+
const structuredImportPattern = /\bimport\s+(type\s+)?(?:([$A-Z_a-z][$\w]*)\s*,\s*)?(\*\s+as\s+[$A-Z_a-z][$\w]*|\{[\s\S]*?\})\s+from\s+["']([^"']+)["']/g;
|
|
633
|
+
for (const match of importText.matchAll(structuredImportPattern)) {
|
|
634
|
+
const defaultName = match[2]?.trim();
|
|
635
|
+
const namedClause = match[3]?.trim() ?? "";
|
|
636
|
+
const specifier = match[4]?.trim();
|
|
637
|
+
addModuleReference(references, seen, specifier, parseImportSymbols(defaultName, namedClause, Boolean(match[1])));
|
|
638
|
+
}
|
|
639
|
+
const defaultImportPattern = /\bimport\s+(type\s+)?([$A-Z_a-z][$\w]*)\s+from\s+["']([^"']+)["']/g;
|
|
640
|
+
for (const match of importText.matchAll(defaultImportPattern)) {
|
|
641
|
+
const defaultName = match[2]?.trim();
|
|
642
|
+
const specifier = match[3]?.trim();
|
|
643
|
+
addModuleReference(references, seen, specifier, defaultName ? [createImportSymbol(`default:${defaultName}`, Boolean(match[1]) ? "type" : "value")] : []);
|
|
644
|
+
}
|
|
645
|
+
const exportFromPattern = /\bexport\s+(type\s+)?(\{[\s\S]*?\}|\*)\s+from\s+["']([^"']+)["']/g;
|
|
646
|
+
for (const match of importText.matchAll(exportFromPattern)) {
|
|
647
|
+
const exportClause = match[2]?.trim() ?? "";
|
|
648
|
+
const specifier = match[3]?.trim();
|
|
649
|
+
addModuleReference(references, seen, specifier, exportClause === "*" ? [createImportSymbol("*", Boolean(match[1]) ? "type" : "value")] : parseNamedBindings(exportClause, Boolean(match[1])));
|
|
650
|
+
}
|
|
651
|
+
const specifierOnlyPatterns = [
|
|
652
|
+
/\bimport\s+["']([^"']+)["']/g,
|
|
653
|
+
/\bimport\s*\(\s*["']([^"']+)["']\s*\)/g,
|
|
654
|
+
/\brequire\s*\(\s*["']([^"']+)["']\s*\)/g
|
|
655
|
+
];
|
|
656
|
+
for (const pattern of specifierOnlyPatterns) {
|
|
657
|
+
for (const match of importText.matchAll(pattern)) {
|
|
658
|
+
addModuleReference(references, seen, match[1]?.trim(), []);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return references;
|
|
662
|
+
}
|
|
663
|
+
function addModuleReference(references, seen, specifier, symbols) {
|
|
664
|
+
if (!specifier) {
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const normalizedSymbols = uniqueSortedImportSymbols(symbols.filter((symbol) => symbol.name.trim() !== ""));
|
|
668
|
+
const key = `${specifier}\n${normalizedSymbols.map((symbol) => `${symbol.usage}:${symbol.name}`).join("\n")}`;
|
|
669
|
+
if (seen.has(key)) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
seen.add(key);
|
|
673
|
+
references.push({
|
|
674
|
+
specifier,
|
|
675
|
+
symbols: normalizedSymbols
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
function parseImportSymbols(defaultName, namedClause, typeOnly) {
|
|
679
|
+
const usage = typeOnly ? "type" : "value";
|
|
680
|
+
const symbols = [];
|
|
681
|
+
if (defaultName) {
|
|
682
|
+
symbols.push(createImportSymbol(`default:${defaultName}`, usage));
|
|
683
|
+
}
|
|
684
|
+
const namespaceMatch = namedClause.match(/^\*\s+as\s+([$A-Z_a-z][$\w]*)$/);
|
|
685
|
+
if (namespaceMatch?.[1]) {
|
|
686
|
+
symbols.push(createImportSymbol(`*:${namespaceMatch[1]}`, usage));
|
|
687
|
+
return symbols;
|
|
688
|
+
}
|
|
689
|
+
return [...symbols, ...parseNamedBindings(namedClause, typeOnly)];
|
|
690
|
+
}
|
|
691
|
+
function parseNamedBindings(rawClause, typeOnly) {
|
|
692
|
+
const trimmed = rawClause.trim();
|
|
693
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
|
|
694
|
+
return [];
|
|
695
|
+
}
|
|
696
|
+
return trimmed
|
|
697
|
+
.slice(1, -1)
|
|
698
|
+
.split(",")
|
|
699
|
+
.map((item) => {
|
|
700
|
+
const trimmedItem = item.trim();
|
|
701
|
+
const inlineType = trimmedItem.startsWith("type ");
|
|
702
|
+
return {
|
|
703
|
+
raw: inlineType ? trimmedItem.replace(/^type\s+/, "") : trimmedItem,
|
|
704
|
+
usage: typeOnly || inlineType ? "type" : "value"
|
|
705
|
+
};
|
|
706
|
+
})
|
|
707
|
+
.filter((item) => item.raw !== "")
|
|
708
|
+
.map((item) => {
|
|
709
|
+
const aliasMatch = item.raw.match(/^([$A-Z_a-z][$\w]*)\s+as\s+([$A-Z_a-z][$\w]*)$/);
|
|
710
|
+
const name = aliasMatch?.[1] && aliasMatch[2] ? `${aliasMatch[1]} as ${aliasMatch[2]}` : item.raw;
|
|
711
|
+
return createImportSymbol(name, item.usage);
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
function createImportSymbol(name, usage) {
|
|
715
|
+
return { name, usage };
|
|
716
|
+
}
|
|
717
|
+
function mergeSymbols(existing, next) {
|
|
718
|
+
return uniqueSortedImportSymbols([...(existing ?? []), ...next]);
|
|
719
|
+
}
|
|
720
|
+
function uniqueSortedImportSymbols(items) {
|
|
721
|
+
const unique = new Map();
|
|
722
|
+
for (const item of items) {
|
|
723
|
+
unique.set(`${item.usage}:${item.name}`, item);
|
|
724
|
+
}
|
|
725
|
+
return Array.from(unique.values()).sort((left, right) => left.usage.localeCompare(right.usage) || left.name.localeCompare(right.name));
|
|
726
|
+
}
|
|
727
|
+
function resolveLocalImport(fromFile, specifier) {
|
|
728
|
+
const base = path.resolve(path.dirname(fromFile), specifier);
|
|
729
|
+
return resolveImportBase(base);
|
|
730
|
+
}
|
|
731
|
+
function resolveImportBase(base) {
|
|
732
|
+
const candidates = new Set();
|
|
733
|
+
candidates.add(base);
|
|
734
|
+
const ext = path.extname(base);
|
|
735
|
+
if (ext) {
|
|
736
|
+
if ([".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
|
|
737
|
+
const withoutExt = base.slice(0, -ext.length);
|
|
738
|
+
for (const sourceExt of GRAPH_SOURCE_EXTENSIONS) {
|
|
739
|
+
candidates.add(`${withoutExt}${sourceExt}`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (!GRAPH_IMPORT_EXTENSIONS.includes(ext)) {
|
|
743
|
+
for (const candidateExt of GRAPH_SOURCE_EXTENSIONS) {
|
|
744
|
+
candidates.add(`${base}${candidateExt}`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
else {
|
|
749
|
+
for (const candidateExt of GRAPH_IMPORT_EXTENSIONS) {
|
|
750
|
+
candidates.add(`${base}${candidateExt}`);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
for (const candidateExt of GRAPH_IMPORT_EXTENSIONS) {
|
|
754
|
+
candidates.add(path.join(base, `index${candidateExt}`));
|
|
755
|
+
}
|
|
756
|
+
for (const candidate of candidates) {
|
|
757
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile() && isGraphSourceFile(candidate)) {
|
|
758
|
+
return candidate;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
function createTsConfigResolver(cwd) {
|
|
764
|
+
const configCache = new Map();
|
|
765
|
+
return {
|
|
766
|
+
resolve(fromFile, specifier) {
|
|
767
|
+
const configPath = findNearestTsConfig(path.dirname(fromFile), cwd);
|
|
768
|
+
if (!configPath) {
|
|
769
|
+
return null;
|
|
770
|
+
}
|
|
771
|
+
const config = readTsConfigInfo(configPath, configCache);
|
|
772
|
+
if (!config || config.mappings.length === 0) {
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
for (const mapping of config.mappings) {
|
|
776
|
+
const wildcard = matchPathPattern(specifier, mapping);
|
|
777
|
+
if (wildcard === null) {
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
for (const target of mapping.targets) {
|
|
781
|
+
const substituted = target.includes("*") ? target.replace(/\*/g, wildcard) : target;
|
|
782
|
+
const base = path.resolve(config.baseUrl, substituted);
|
|
783
|
+
const resolved = resolveImportBase(base);
|
|
784
|
+
if (resolved) {
|
|
785
|
+
return resolved;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
function findNearestTsConfig(startDir, stopDir) {
|
|
794
|
+
let current = path.resolve(startDir);
|
|
795
|
+
const stop = path.parse(path.resolve(stopDir)).root;
|
|
796
|
+
while (true) {
|
|
797
|
+
const candidate = path.join(current, TSCONFIG_FILE_NAME);
|
|
798
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
799
|
+
return candidate;
|
|
800
|
+
}
|
|
801
|
+
const parent = path.dirname(current);
|
|
802
|
+
if (parent === current || current === stop) {
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
current = parent;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function readTsConfigInfo(configPath, cache) {
|
|
809
|
+
const normalizedConfigPath = path.resolve(configPath);
|
|
810
|
+
if (cache.has(normalizedConfigPath)) {
|
|
811
|
+
return cache.get(normalizedConfigPath) ?? null;
|
|
812
|
+
}
|
|
813
|
+
cache.set(normalizedConfigPath, null);
|
|
814
|
+
const parsed = readParsedTsConfig(normalizedConfigPath);
|
|
815
|
+
if (!parsed) {
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
const configDir = path.dirname(normalizedConfigPath);
|
|
819
|
+
const baseUrl = parsed.baseUrl ? path.resolve(configDir, parsed.baseUrl) : configDir;
|
|
820
|
+
const mappings = Object.entries(parsed.paths ?? {})
|
|
821
|
+
.flatMap(([pattern, targets]) => createPathMapping(pattern, targets))
|
|
822
|
+
.sort((left, right) => right.specificity - left.specificity || left.pattern.localeCompare(right.pattern));
|
|
823
|
+
const info = {
|
|
824
|
+
configPath: normalizedConfigPath,
|
|
825
|
+
baseUrl,
|
|
826
|
+
mappings
|
|
827
|
+
};
|
|
828
|
+
cache.set(normalizedConfigPath, info);
|
|
829
|
+
return info;
|
|
830
|
+
}
|
|
831
|
+
function readParsedTsConfig(configPath) {
|
|
832
|
+
try {
|
|
833
|
+
const raw = parseJsoncObject(fs.readFileSync(configPath, "utf8"));
|
|
834
|
+
const parent = readExtendsTsConfig(configPath, raw.extends);
|
|
835
|
+
const compilerOptions = isRecord(raw.compilerOptions) ? raw.compilerOptions : {};
|
|
836
|
+
const paths = readPaths(compilerOptions.paths);
|
|
837
|
+
const baseUrl = typeof compilerOptions.baseUrl === "string"
|
|
838
|
+
? path.resolve(path.dirname(configPath), compilerOptions.baseUrl)
|
|
839
|
+
: undefined;
|
|
840
|
+
return {
|
|
841
|
+
...(parent ?? {}),
|
|
842
|
+
...(baseUrl !== undefined ? { baseUrl } : {}),
|
|
843
|
+
...(paths ? { paths } : {})
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
catch {
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
function readExtendsTsConfig(configPath, rawExtends) {
|
|
851
|
+
if (typeof rawExtends !== "string" || rawExtends.trim() === "") {
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
const configDir = path.dirname(configPath);
|
|
855
|
+
const candidates = tsConfigExtendsCandidates(configDir, rawExtends.trim());
|
|
856
|
+
for (const candidate of candidates) {
|
|
857
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
858
|
+
return readParsedTsConfig(candidate);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return null;
|
|
862
|
+
}
|
|
863
|
+
function tsConfigExtendsCandidates(configDir, rawExtends) {
|
|
864
|
+
const base = rawExtends.startsWith(".") || path.isAbsolute(rawExtends)
|
|
865
|
+
? path.resolve(configDir, rawExtends)
|
|
866
|
+
: path.resolve(configDir, "node_modules", rawExtends);
|
|
867
|
+
const candidates = [base];
|
|
868
|
+
if (!path.extname(base)) {
|
|
869
|
+
candidates.push(`${base}.json`, path.join(base, TSCONFIG_FILE_NAME));
|
|
870
|
+
}
|
|
871
|
+
return candidates;
|
|
872
|
+
}
|
|
873
|
+
function parseJsoncObject(text) {
|
|
874
|
+
const withoutComments = stripCommentsOutsideStrings(text);
|
|
875
|
+
const withoutTrailingCommas = withoutComments.replace(/,\s*([}\]])/g, "$1");
|
|
876
|
+
const parsed = JSON.parse(withoutTrailingCommas);
|
|
877
|
+
return isRecord(parsed) ? parsed : {};
|
|
878
|
+
}
|
|
879
|
+
function isRecord(value) {
|
|
880
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
881
|
+
}
|
|
882
|
+
function stripCommentsOutsideStrings(text) {
|
|
883
|
+
let result = "";
|
|
884
|
+
let inString = false;
|
|
885
|
+
let escaped = false;
|
|
886
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
887
|
+
const current = text[index];
|
|
888
|
+
const next = text[index + 1];
|
|
889
|
+
if (inString) {
|
|
890
|
+
result += current;
|
|
891
|
+
if (escaped) {
|
|
892
|
+
escaped = false;
|
|
893
|
+
}
|
|
894
|
+
else if (current === "\\") {
|
|
895
|
+
escaped = true;
|
|
896
|
+
}
|
|
897
|
+
else if (current === "\"") {
|
|
898
|
+
inString = false;
|
|
899
|
+
}
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
if (current === "\"") {
|
|
903
|
+
inString = true;
|
|
904
|
+
result += current;
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
if (current === "/" && next === "/") {
|
|
908
|
+
while (index < text.length && text[index] !== "\n") {
|
|
909
|
+
index += 1;
|
|
910
|
+
}
|
|
911
|
+
result += "\n";
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
if (current === "/" && next === "*") {
|
|
915
|
+
index += 2;
|
|
916
|
+
while (index < text.length && !(text[index] === "*" && text[index + 1] === "/")) {
|
|
917
|
+
index += 1;
|
|
918
|
+
}
|
|
919
|
+
index += 1;
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
result += current;
|
|
923
|
+
}
|
|
924
|
+
return result;
|
|
925
|
+
}
|
|
926
|
+
function readPaths(value) {
|
|
927
|
+
if (!isRecord(value)) {
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
const paths = {};
|
|
931
|
+
for (const [key, rawTargets] of Object.entries(value)) {
|
|
932
|
+
if (!Array.isArray(rawTargets)) {
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
935
|
+
const targets = rawTargets.filter((target) => typeof target === "string" && target.trim() !== "");
|
|
936
|
+
if (targets.length > 0) {
|
|
937
|
+
paths[key] = targets;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return Object.keys(paths).length > 0 ? paths : null;
|
|
941
|
+
}
|
|
942
|
+
function createPathMapping(pattern, targets) {
|
|
943
|
+
if (typeof pattern !== "string" || pattern.trim() === "") {
|
|
944
|
+
return [];
|
|
945
|
+
}
|
|
946
|
+
const starIndex = pattern.indexOf("*");
|
|
947
|
+
const prefix = starIndex >= 0 ? pattern.slice(0, starIndex) : pattern;
|
|
948
|
+
const suffix = starIndex >= 0 ? pattern.slice(starIndex + 1) : "";
|
|
949
|
+
return [{
|
|
950
|
+
pattern,
|
|
951
|
+
prefix,
|
|
952
|
+
suffix,
|
|
953
|
+
targets,
|
|
954
|
+
specificity: prefix.length + suffix.length
|
|
955
|
+
}];
|
|
956
|
+
}
|
|
957
|
+
function matchPathPattern(specifier, mapping) {
|
|
958
|
+
if (!mapping.pattern.includes("*")) {
|
|
959
|
+
return specifier === mapping.pattern ? "" : null;
|
|
960
|
+
}
|
|
961
|
+
if (!specifier.startsWith(mapping.prefix) || !specifier.endsWith(mapping.suffix)) {
|
|
962
|
+
return null;
|
|
963
|
+
}
|
|
964
|
+
return specifier.slice(mapping.prefix.length, specifier.length - mapping.suffix.length);
|
|
965
|
+
}
|
|
966
|
+
function formatGraphResponse(cwd, model, options) {
|
|
967
|
+
const initialFormatter = createGraphPathFormatter(collectVisibleGraphPaths(model, options));
|
|
968
|
+
const initialResponse = formatGraphResponsePayload(cwd, model, options, initialFormatter);
|
|
969
|
+
const usefulAliases = usefulAliasesForResponse(initialResponse, initialFormatter);
|
|
970
|
+
const formatter = usefulAliases.length === initialFormatter.aliases.length
|
|
971
|
+
? initialFormatter
|
|
972
|
+
: createGraphPathFormatterFromAliases(usefulAliases);
|
|
973
|
+
const response = formatter === initialFormatter
|
|
974
|
+
? initialResponse
|
|
975
|
+
: formatGraphResponsePayload(cwd, model, options, formatter);
|
|
976
|
+
return withUsedPathAliases(response, formatter);
|
|
977
|
+
}
|
|
978
|
+
function formatGraphResponsePayload(cwd, model, options, formatter) {
|
|
979
|
+
const response = {
|
|
980
|
+
operation: "inspect.graph",
|
|
981
|
+
summary: `${model.files.length} files, ${model.edges.length} local edges, ${model.externalPackages.length} external packages`
|
|
982
|
+
};
|
|
983
|
+
if (model.timeoutHint) {
|
|
984
|
+
response.timeoutHint = model.timeoutHint;
|
|
985
|
+
}
|
|
986
|
+
if (options.view) {
|
|
987
|
+
response.view = options.view;
|
|
988
|
+
}
|
|
989
|
+
if (model.excluded && model.excluded.count > 0) {
|
|
990
|
+
response.excluded = `${model.excluded.count} files by graph.exclude`;
|
|
991
|
+
}
|
|
992
|
+
if (options.writtenTo) {
|
|
993
|
+
response.graphFile = options.writtenTo;
|
|
994
|
+
}
|
|
995
|
+
if (options.focus.length > 0) {
|
|
996
|
+
response.focus = options.focus.slice(0, 10).map((focus) => formatFocus(model, normalizeGraphPath(focus), formatter, options.symbols, options.symbolUsage));
|
|
997
|
+
response.next = nextHint(options, model);
|
|
998
|
+
return response;
|
|
999
|
+
}
|
|
1000
|
+
const hubs = model.files
|
|
1001
|
+
.map((file) => ({
|
|
1002
|
+
file: file.file,
|
|
1003
|
+
total: file.imports.length + file.importedBy.length,
|
|
1004
|
+
imports: file.imports.length,
|
|
1005
|
+
importedBy: file.importedBy.length
|
|
1006
|
+
}))
|
|
1007
|
+
.filter((item) => item.total > 0)
|
|
1008
|
+
.sort((left, right) => right.total - left.total || left.file.localeCompare(right.file))
|
|
1009
|
+
.slice(0, DEFAULT_LIST_LIMIT)
|
|
1010
|
+
.map((item) => `${formatter.compactPath(item.file)} imports ${item.imports}, importedBy ${item.importedBy}`);
|
|
1011
|
+
if (options.sections.hubs && hubs.length > 0) {
|
|
1012
|
+
response.hubs = hubs;
|
|
1013
|
+
}
|
|
1014
|
+
const entrypoints = model.files
|
|
1015
|
+
.filter((file) => file.importedBy.length === 0 && file.imports.length > 0)
|
|
1016
|
+
.sort((left, right) => right.imports.length - left.imports.length || left.file.localeCompare(right.file))
|
|
1017
|
+
.map((file) => file.file);
|
|
1018
|
+
const visibleEntrypoints = formatter.compactList(entrypoints, DEFAULT_LIST_LIMIT);
|
|
1019
|
+
if (options.sections.entrypoints && visibleEntrypoints.length > 0) {
|
|
1020
|
+
response.scopeEntrypoints = visibleEntrypoints;
|
|
1021
|
+
}
|
|
1022
|
+
const leaves = model.files
|
|
1023
|
+
.filter((file) => file.imports.length === 0 && file.importedBy.length > 0)
|
|
1024
|
+
.sort((left, right) => right.importedBy.length - left.importedBy.length || left.file.localeCompare(right.file))
|
|
1025
|
+
.map((file) => file.file);
|
|
1026
|
+
const visibleLeaves = formatter.compactList(leaves, DEFAULT_LIST_LIMIT);
|
|
1027
|
+
if (options.sections.leaves && visibleLeaves.length > 0) {
|
|
1028
|
+
response.scopeLeaves = visibleLeaves;
|
|
1029
|
+
}
|
|
1030
|
+
const edges = formatEdgeSample(model.edges, formatter, DEFAULT_EDGE_LIMIT, options.symbols);
|
|
1031
|
+
if (options.sections.edges && edges.length > 0) {
|
|
1032
|
+
response.edges = edges;
|
|
1033
|
+
}
|
|
1034
|
+
response.next = options.writtenTo
|
|
1035
|
+
? `Focus saved graph: aib inspect graph --from-file ${toCliDisplayPath(cwd, options.writtenTo)} --focus <file>`
|
|
1036
|
+
: nextHint(options, model);
|
|
1037
|
+
return response;
|
|
1038
|
+
}
|
|
1039
|
+
function formatFocus(model, focus, formatter, includeSymbols, symbolUsage) {
|
|
1040
|
+
const file = model.files.find((item) => normalizeGraphPath(item.file) === focus);
|
|
1041
|
+
if (!file) {
|
|
1042
|
+
return {
|
|
1043
|
+
file: focus,
|
|
1044
|
+
ok: false,
|
|
1045
|
+
code: "GRAPH_FOCUS_NOT_FOUND",
|
|
1046
|
+
error: "Focus file is not present in this graph."
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
const payload = {
|
|
1050
|
+
file: formatter.compactPath(file.file)
|
|
1051
|
+
};
|
|
1052
|
+
if (file.imports.length > 0) {
|
|
1053
|
+
if (includeSymbols) {
|
|
1054
|
+
const outgoingEdges = model.edges.filter((edge) => edge.from === file.file);
|
|
1055
|
+
if (symbolUsage) {
|
|
1056
|
+
const usage = formatSymbolImportUsage(outgoingEdges, formatter, DEFAULT_LIST_LIMIT);
|
|
1057
|
+
if (Object.keys(usage).length > 0) {
|
|
1058
|
+
payload.usage = usage;
|
|
1059
|
+
}
|
|
1060
|
+
const importsOther = importsWithoutVisibleSymbols(file.imports, outgoingEdges, formatter);
|
|
1061
|
+
if (importsOther.length > 0) {
|
|
1062
|
+
payload.importsOther = importsOther;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
else {
|
|
1066
|
+
payload.symbols = formatSymbolImportList(outgoingEdges, formatter, DEFAULT_LIST_LIMIT);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
else {
|
|
1070
|
+
payload.imports = formatter.compactList(file.imports, DEFAULT_LIST_LIMIT);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
if (file.importedBy.length > 0) {
|
|
1074
|
+
payload.importedBy = formatter.compactList(file.importedBy, DEFAULT_LIST_LIMIT);
|
|
1075
|
+
}
|
|
1076
|
+
if (file.external.length > 0) {
|
|
1077
|
+
payload.external = summarizeList(file.external, DEFAULT_LIST_LIMIT);
|
|
1078
|
+
}
|
|
1079
|
+
if (file.omittedLocalImports && file.omittedLocalImports.length > 0) {
|
|
1080
|
+
payload.omittedLocalImports = formatter.compactList(file.omittedLocalImports, DEFAULT_LIST_LIMIT);
|
|
1081
|
+
}
|
|
1082
|
+
return payload;
|
|
1083
|
+
}
|
|
1084
|
+
function nextHint(options, model) {
|
|
1085
|
+
if (options.depth === 0 && model.files.some((file) => (file.omittedLocalImports ?? []).length > 0)) {
|
|
1086
|
+
return "Use --depth 1 to include direct local imports outside the initial scope.";
|
|
1087
|
+
}
|
|
1088
|
+
return "Use --depth 2 for a wider local neighborhood, or inspect exports/code on focused files.";
|
|
1089
|
+
}
|
|
1090
|
+
function collectVisibleGraphPaths(model, options) {
|
|
1091
|
+
const paths = new Set();
|
|
1092
|
+
if (options.focus.length > 0) {
|
|
1093
|
+
for (const rawFocus of options.focus.slice(0, 10)) {
|
|
1094
|
+
const focus = normalizeGraphPath(rawFocus);
|
|
1095
|
+
const file = model.files.find((item) => normalizeGraphPath(item.file) === focus);
|
|
1096
|
+
if (!file) {
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
paths.add(file.file);
|
|
1100
|
+
for (const item of file.imports.slice(0, DEFAULT_LIST_LIMIT)) {
|
|
1101
|
+
paths.add(item);
|
|
1102
|
+
}
|
|
1103
|
+
for (const item of file.importedBy.slice(0, DEFAULT_LIST_LIMIT)) {
|
|
1104
|
+
paths.add(item);
|
|
1105
|
+
}
|
|
1106
|
+
for (const item of (file.omittedLocalImports ?? []).slice(0, DEFAULT_LIST_LIMIT)) {
|
|
1107
|
+
paths.add(item);
|
|
1108
|
+
}
|
|
1109
|
+
if (options.symbols) {
|
|
1110
|
+
for (const edge of model.edges.filter((item) => item.from === file.file).slice(0, DEFAULT_LIST_LIMIT)) {
|
|
1111
|
+
paths.add(edge.to);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return Array.from(paths);
|
|
1116
|
+
}
|
|
1117
|
+
if (options.sections.hubs) {
|
|
1118
|
+
const hubs = model.files
|
|
1119
|
+
.map((file) => ({
|
|
1120
|
+
file: file.file,
|
|
1121
|
+
total: file.imports.length + file.importedBy.length,
|
|
1122
|
+
imports: file.imports.length,
|
|
1123
|
+
importedBy: file.importedBy.length
|
|
1124
|
+
}))
|
|
1125
|
+
.filter((item) => item.total > 0)
|
|
1126
|
+
.sort((left, right) => right.total - left.total || left.file.localeCompare(right.file))
|
|
1127
|
+
.slice(0, DEFAULT_LIST_LIMIT);
|
|
1128
|
+
for (const item of hubs) {
|
|
1129
|
+
paths.add(item.file);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (options.sections.entrypoints) {
|
|
1133
|
+
for (const item of model.files
|
|
1134
|
+
.filter((file) => file.importedBy.length === 0 && file.imports.length > 0)
|
|
1135
|
+
.sort((left, right) => right.imports.length - left.imports.length || left.file.localeCompare(right.file))
|
|
1136
|
+
.map((file) => file.file)
|
|
1137
|
+
.slice(0, DEFAULT_LIST_LIMIT)) {
|
|
1138
|
+
paths.add(item);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
if (options.sections.leaves) {
|
|
1142
|
+
for (const item of model.files
|
|
1143
|
+
.filter((file) => file.imports.length === 0 && file.importedBy.length > 0)
|
|
1144
|
+
.sort((left, right) => right.importedBy.length - left.importedBy.length || left.file.localeCompare(right.file))
|
|
1145
|
+
.map((file) => file.file)
|
|
1146
|
+
.slice(0, DEFAULT_LIST_LIMIT)) {
|
|
1147
|
+
paths.add(item);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
if (options.sections.edges) {
|
|
1151
|
+
for (const edge of model.edges.slice(0, DEFAULT_EDGE_LIMIT)) {
|
|
1152
|
+
paths.add(edge.from);
|
|
1153
|
+
paths.add(edge.to);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
return Array.from(paths);
|
|
1157
|
+
}
|
|
1158
|
+
function createGraphPathFormatter(pathValues) {
|
|
1159
|
+
return createGraphPathFormatterFromAliases(buildGraphPathAliases(pathValues));
|
|
1160
|
+
}
|
|
1161
|
+
function createGraphPathFormatterFromAliases(aliases) {
|
|
1162
|
+
return {
|
|
1163
|
+
aliases,
|
|
1164
|
+
compactPath(pathValue) {
|
|
1165
|
+
return compactGraphPath(pathValue, aliases);
|
|
1166
|
+
},
|
|
1167
|
+
compactList(paths, limit) {
|
|
1168
|
+
return compactPathList(paths, limit, aliases);
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
function buildGraphPathAliases(pathValues) {
|
|
1173
|
+
const counts = new Map();
|
|
1174
|
+
for (const pathValue of pathValues) {
|
|
1175
|
+
for (const prefix of candidatePathPrefixes(pathValue)) {
|
|
1176
|
+
counts.set(prefix, (counts.get(prefix) ?? 0) + 1);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
const usedTokens = new Set();
|
|
1180
|
+
const candidates = Array.from(counts.entries())
|
|
1181
|
+
.map(([prefix, count]) => {
|
|
1182
|
+
const token = `<${path.basename(prefix)}>`;
|
|
1183
|
+
return {
|
|
1184
|
+
prefix,
|
|
1185
|
+
count,
|
|
1186
|
+
token,
|
|
1187
|
+
score: Math.max(0, prefix.length - token.length) * count
|
|
1188
|
+
};
|
|
1189
|
+
})
|
|
1190
|
+
.filter((item) => item.count >= 2 && (item.score >= 16 || item.count >= 3) && item.prefix !== "src/main")
|
|
1191
|
+
.sort((left, right) => right.score - left.score || right.count - left.count || right.prefix.length - left.prefix.length || left.prefix.localeCompare(right.prefix))
|
|
1192
|
+
.filter((item) => {
|
|
1193
|
+
if (usedTokens.has(item.token)) {
|
|
1194
|
+
return false;
|
|
1195
|
+
}
|
|
1196
|
+
usedTokens.add(item.token);
|
|
1197
|
+
return true;
|
|
1198
|
+
});
|
|
1199
|
+
return pruneNestedAliases(candidates)
|
|
1200
|
+
.slice(0, 6)
|
|
1201
|
+
.sort((left, right) => right.prefix.length - left.prefix.length)
|
|
1202
|
+
.map((item) => ({
|
|
1203
|
+
token: item.token,
|
|
1204
|
+
prefix: item.prefix
|
|
1205
|
+
}));
|
|
1206
|
+
}
|
|
1207
|
+
function pruneNestedAliases(aliases) {
|
|
1208
|
+
return aliases.filter((alias) => {
|
|
1209
|
+
const broader = aliases.find((candidate) => candidate.prefix !== alias.prefix &&
|
|
1210
|
+
alias.prefix.startsWith(`${candidate.prefix}/`) &&
|
|
1211
|
+
candidate.count >= alias.count &&
|
|
1212
|
+
candidate.score >= 12 &&
|
|
1213
|
+
alias.score < 40);
|
|
1214
|
+
return !broader;
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
function candidatePathPrefixes(pathValue) {
|
|
1218
|
+
const parts = normalizeGraphPath(pathValue).split("/");
|
|
1219
|
+
const prefixes = [];
|
|
1220
|
+
for (let index = 2; index < parts.length; index += 1) {
|
|
1221
|
+
const prefix = parts.slice(0, index).join("/");
|
|
1222
|
+
if (prefix.length >= 8) {
|
|
1223
|
+
prefixes.push(prefix);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
return prefixes;
|
|
1227
|
+
}
|
|
1228
|
+
function withUsedPathAliases(response, formatter) {
|
|
1229
|
+
const pathAliases = pathAliasesPayload(formatter, response);
|
|
1230
|
+
if (!pathAliases) {
|
|
1231
|
+
return response;
|
|
1232
|
+
}
|
|
1233
|
+
return {
|
|
1234
|
+
operation: response.operation ?? "inspect.graph",
|
|
1235
|
+
summary: response.summary ?? "",
|
|
1236
|
+
paths: pathAliases,
|
|
1237
|
+
...Object.fromEntries(Object.entries(response).filter(([key]) => key !== "operation" && key !== "summary"))
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
function usefulAliasesForResponse(response, formatter) {
|
|
1241
|
+
const serializedResponse = JSON.stringify(response);
|
|
1242
|
+
return formatter.aliases.filter((alias) => countOccurrences(serializedResponse, alias.token) >= 2);
|
|
1243
|
+
}
|
|
1244
|
+
function countOccurrences(text, pattern) {
|
|
1245
|
+
let count = 0;
|
|
1246
|
+
let offset = 0;
|
|
1247
|
+
while (true) {
|
|
1248
|
+
const index = text.indexOf(pattern, offset);
|
|
1249
|
+
if (index < 0) {
|
|
1250
|
+
return count;
|
|
1251
|
+
}
|
|
1252
|
+
count += 1;
|
|
1253
|
+
offset = index + pattern.length;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
function pathAliasesPayload(formatter, response) {
|
|
1257
|
+
if (formatter.aliases.length === 0) {
|
|
1258
|
+
return null;
|
|
1259
|
+
}
|
|
1260
|
+
const serializedResponse = JSON.stringify(response);
|
|
1261
|
+
const payload = {};
|
|
1262
|
+
for (const alias of formatter.aliases) {
|
|
1263
|
+
if (serializedResponse.includes(alias.token)) {
|
|
1264
|
+
payload[alias.token] = alias.prefix;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return Object.keys(payload).length > 0 ? payload : null;
|
|
1268
|
+
}
|
|
1269
|
+
function compactGraphPath(pathValue, aliases) {
|
|
1270
|
+
const normalized = normalizeGraphPath(pathValue);
|
|
1271
|
+
const alias = aliases.find((candidate) => normalized === candidate.prefix || normalized.startsWith(`${candidate.prefix}/`));
|
|
1272
|
+
if (!alias) {
|
|
1273
|
+
return normalized;
|
|
1274
|
+
}
|
|
1275
|
+
if (normalized === alias.prefix) {
|
|
1276
|
+
return alias.token;
|
|
1277
|
+
}
|
|
1278
|
+
return `${alias.token}/${normalized.slice(alias.prefix.length + 1)}`;
|
|
1279
|
+
}
|
|
1280
|
+
function compactPathList(paths, limit, aliases) {
|
|
1281
|
+
const visible = paths.slice(0, limit).map((item) => compactGraphPath(item, aliases));
|
|
1282
|
+
const grouped = groupCompactPaths(visible);
|
|
1283
|
+
const hidden = paths.length - Math.min(paths.length, limit);
|
|
1284
|
+
return hidden > 0 ? [...grouped, `+ ${hidden} more`] : grouped;
|
|
1285
|
+
}
|
|
1286
|
+
function groupCompactPaths(paths) {
|
|
1287
|
+
const groups = new Map();
|
|
1288
|
+
const singles = [];
|
|
1289
|
+
for (const pathValue of paths) {
|
|
1290
|
+
const markerEnd = pathValue.startsWith("<") ? pathValue.indexOf(">") : -1;
|
|
1291
|
+
if (markerEnd <= 0) {
|
|
1292
|
+
singles.push(pathValue);
|
|
1293
|
+
continue;
|
|
1294
|
+
}
|
|
1295
|
+
const slashIndex = pathValue.indexOf("/", markerEnd);
|
|
1296
|
+
if (slashIndex < 0) {
|
|
1297
|
+
singles.push(pathValue);
|
|
1298
|
+
continue;
|
|
1299
|
+
}
|
|
1300
|
+
const prefix = pathValue.slice(0, slashIndex);
|
|
1301
|
+
const rest = pathValue.slice(slashIndex + 1);
|
|
1302
|
+
const existing = groups.get(prefix) ?? [];
|
|
1303
|
+
existing.push(rest);
|
|
1304
|
+
groups.set(prefix, existing);
|
|
1305
|
+
}
|
|
1306
|
+
return [
|
|
1307
|
+
...Array.from(groups.entries()).map(([prefix, items]) => items.length === 1 ? `${prefix}/${items[0]}` : `${prefix}/: ${items.join(", ")}`),
|
|
1308
|
+
...singles
|
|
1309
|
+
].sort();
|
|
1310
|
+
}
|
|
1311
|
+
function formatEdgeSample(edges, formatter, limit, includeSymbols) {
|
|
1312
|
+
const visible = edges.slice(0, limit);
|
|
1313
|
+
const groups = new Map();
|
|
1314
|
+
const singles = [];
|
|
1315
|
+
for (const edge of visible) {
|
|
1316
|
+
const from = formatter.compactPath(edge.from);
|
|
1317
|
+
const to = formatter.compactPath(edge.to);
|
|
1318
|
+
const symbolsSuffix = includeSymbols ? formatSymbolsSuffix(edge.symbols) : "";
|
|
1319
|
+
const markerEnd = to.startsWith("<") ? to.indexOf(">") : -1;
|
|
1320
|
+
const slashIndex = markerEnd > 0 ? to.indexOf("/", markerEnd) : -1;
|
|
1321
|
+
if (slashIndex < 0) {
|
|
1322
|
+
singles.push(`${from} -> ${to}${symbolsSuffix}`);
|
|
1323
|
+
continue;
|
|
1324
|
+
}
|
|
1325
|
+
const targetPrefix = to.slice(0, slashIndex);
|
|
1326
|
+
const targetRest = `${to.slice(slashIndex + 1)}${symbolsSuffix}`;
|
|
1327
|
+
const key = `${from} -> ${targetPrefix}`;
|
|
1328
|
+
const existing = groups.get(key) ?? [];
|
|
1329
|
+
existing.push(targetRest);
|
|
1330
|
+
groups.set(key, existing);
|
|
1331
|
+
}
|
|
1332
|
+
const grouped = [
|
|
1333
|
+
...Array.from(groups.entries()).map(([prefix, items]) => items.length === 1 ? `${prefix}/${items[0]}` : `${prefix}/: ${items.join(", ")}`),
|
|
1334
|
+
...singles
|
|
1335
|
+
].sort();
|
|
1336
|
+
const hidden = edges.length - Math.min(edges.length, limit);
|
|
1337
|
+
return hidden > 0 ? [...grouped, `+ ${hidden} more`] : grouped;
|
|
1338
|
+
}
|
|
1339
|
+
function formatSymbolImportList(edges, formatter, limit) {
|
|
1340
|
+
const visible = edges.slice(0, limit).map((edge) => `${formatter.compactPath(edge.to)}${formatSymbolsSuffix(edge.symbols)}`);
|
|
1341
|
+
const hidden = edges.length - Math.min(edges.length, limit);
|
|
1342
|
+
return hidden > 0 ? [...visible, `+ ${hidden} more files`] : visible;
|
|
1343
|
+
}
|
|
1344
|
+
function formatSymbolImportUsage(edges, formatter, limit) {
|
|
1345
|
+
const visible = edges.slice(0, limit);
|
|
1346
|
+
const typeSymbols = visible.flatMap((edge) => formatSymbolUsageEntries(edge, formatter, "type"));
|
|
1347
|
+
const valueSymbols = visible.flatMap((edge) => formatSymbolUsageEntries(edge, formatter, "value"));
|
|
1348
|
+
const hidden = edges.length - Math.min(edges.length, limit);
|
|
1349
|
+
const payload = {};
|
|
1350
|
+
if (valueSymbols.length > 0) {
|
|
1351
|
+
payload.value = valueSymbols;
|
|
1352
|
+
}
|
|
1353
|
+
if (typeSymbols.length > 0) {
|
|
1354
|
+
payload.type = typeSymbols;
|
|
1355
|
+
}
|
|
1356
|
+
if (hidden > 0) {
|
|
1357
|
+
payload.moreFiles = `+ ${hidden} more files`;
|
|
1358
|
+
}
|
|
1359
|
+
return payload;
|
|
1360
|
+
}
|
|
1361
|
+
function importsWithoutVisibleSymbols(imports, edges, formatter) {
|
|
1362
|
+
const withVisibleSymbols = new Set(edges
|
|
1363
|
+
.filter((edge) => normalizeImportSymbols(edge.symbols).length > 0)
|
|
1364
|
+
.map((edge) => edge.to));
|
|
1365
|
+
const missing = imports.filter((file) => !withVisibleSymbols.has(file));
|
|
1366
|
+
return formatter.compactList(missing, DEFAULT_LIST_LIMIT);
|
|
1367
|
+
}
|
|
1368
|
+
function formatSymbolUsageEntries(edge, formatter, usage) {
|
|
1369
|
+
const symbols = normalizeImportSymbols(edge.symbols).filter((symbol) => symbol.usage === usage);
|
|
1370
|
+
if (symbols.length === 0) {
|
|
1371
|
+
return [];
|
|
1372
|
+
}
|
|
1373
|
+
return [`${formatter.compactPath(edge.to)}${formatSymbolsSuffix(symbols)}`];
|
|
1374
|
+
}
|
|
1375
|
+
function formatSymbolsSuffix(symbols) {
|
|
1376
|
+
const normalizedSymbols = normalizeImportSymbols(symbols);
|
|
1377
|
+
if (normalizedSymbols.length === 0) {
|
|
1378
|
+
return "";
|
|
1379
|
+
}
|
|
1380
|
+
return `: ${summarizeSymbols(normalizedSymbols, 4).join(", ")}`;
|
|
1381
|
+
}
|
|
1382
|
+
function summarizeSymbols(symbols, limit) {
|
|
1383
|
+
const visible = symbols.slice(0, limit).map((symbol) => symbol.name);
|
|
1384
|
+
const hidden = symbols.length - Math.min(symbols.length, limit);
|
|
1385
|
+
return hidden > 0 ? [...visible, `+ ${hidden} more symbols`] : visible;
|
|
1386
|
+
}
|
|
1387
|
+
function normalizeImportSymbols(symbols) {
|
|
1388
|
+
return (symbols ?? [])
|
|
1389
|
+
.map((symbol) => typeof symbol === "string" ? createImportSymbol(symbol, "value") : symbol)
|
|
1390
|
+
.filter((symbol) => symbol.name.trim() !== "");
|
|
1391
|
+
}
|
|
1392
|
+
function readGraphModel(cwd, rawFile) {
|
|
1393
|
+
const filePath = resolveMaybeRelative(cwd, (0, session_workspace_1.resolveSessionFileArgument)(cwd, rawFile));
|
|
1394
|
+
const raw = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
1395
|
+
if (!isGraphModel(raw)) {
|
|
1396
|
+
throw new errors_1.CliError("INVALID_GRAPH_FILE", "inspect graph --from-file expects a graph JSON file created by inspect graph --to-file.");
|
|
1397
|
+
}
|
|
1398
|
+
return raw;
|
|
1399
|
+
}
|
|
1400
|
+
function writeGraphModel(cwd, rawFile, model) {
|
|
1401
|
+
const filePath = resolveMaybeRelative(cwd, rawFile);
|
|
1402
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
1403
|
+
fs.writeFileSync(filePath, `${JSON.stringify(model, null, 2)}\n`);
|
|
1404
|
+
return filePath;
|
|
1405
|
+
}
|
|
1406
|
+
function isGraphModel(value) {
|
|
1407
|
+
return Boolean(value) &&
|
|
1408
|
+
typeof value === "object" &&
|
|
1409
|
+
!Array.isArray(value) &&
|
|
1410
|
+
value.operation === "inspect.graph" &&
|
|
1411
|
+
Array.isArray(value.files) &&
|
|
1412
|
+
Array.isArray(value.edges);
|
|
1413
|
+
}
|
|
1414
|
+
function readString(value) {
|
|
1415
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
1416
|
+
}
|
|
1417
|
+
function readStringArray(value) {
|
|
1418
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1419
|
+
return [value.trim()];
|
|
1420
|
+
}
|
|
1421
|
+
if (!Array.isArray(value)) {
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
return value
|
|
1425
|
+
.filter((item) => typeof item === "string" && item.trim() !== "")
|
|
1426
|
+
.map((item) => item.trim());
|
|
1427
|
+
}
|
|
1428
|
+
function readBoolean(value) {
|
|
1429
|
+
return typeof value === "boolean" ? value : null;
|
|
1430
|
+
}
|
|
1431
|
+
function readGraphSectionsFromPrefs(cwd) {
|
|
1432
|
+
return {
|
|
1433
|
+
hubs: (0, session_workspace_1.readSessionBooleanPref)(cwd, "inspect.graph.hubs", true),
|
|
1434
|
+
entrypoints: (0, session_workspace_1.readSessionBooleanPref)(cwd, "inspect.graph.entrypoints", true),
|
|
1435
|
+
leaves: (0, session_workspace_1.readSessionBooleanPref)(cwd, "inspect.graph.leaves", true),
|
|
1436
|
+
edges: (0, session_workspace_1.readSessionBooleanPref)(cwd, "inspect.graph.edges", true)
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function readGraphSectionsFromPayload(cwd, payload) {
|
|
1440
|
+
const defaults = readGraphSectionsFromPrefs(cwd);
|
|
1441
|
+
return {
|
|
1442
|
+
hubs: readBoolean(payload.hubs) ?? readBoolean(payload.showHubs) ?? defaults.hubs,
|
|
1443
|
+
entrypoints: readBoolean(payload.entrypoints) ??
|
|
1444
|
+
readBoolean(payload.scopeEntrypoints) ??
|
|
1445
|
+
readBoolean(payload.showEntrypoints) ??
|
|
1446
|
+
defaults.entrypoints,
|
|
1447
|
+
leaves: readBoolean(payload.leaves) ??
|
|
1448
|
+
readBoolean(payload.scopeLeaves) ??
|
|
1449
|
+
readBoolean(payload.showLeaves) ??
|
|
1450
|
+
defaults.leaves,
|
|
1451
|
+
edges: readBoolean(payload.edges) ?? readBoolean(payload.showEdges) ?? defaults.edges
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
function readOptionalGraphDepth(value) {
|
|
1455
|
+
if (value === undefined) {
|
|
1456
|
+
return undefined;
|
|
1457
|
+
}
|
|
1458
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
|
|
1459
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 2) {
|
|
1460
|
+
throw new errors_1.CliError("INVALID_GRAPH_DEPTH", "inspect graph depth expects 0, 1, or 2.");
|
|
1461
|
+
}
|
|
1462
|
+
return parsed;
|
|
1463
|
+
}
|
|
1464
|
+
function readGraphView(value) {
|
|
1465
|
+
if (value === "local" || value === "neighbors" || value === "expanded") {
|
|
1466
|
+
return value;
|
|
1467
|
+
}
|
|
1468
|
+
throw new errors_1.CliError("INVALID_GRAPH_VIEW", "inspect graph view expects local, neighbors, or expanded.");
|
|
1469
|
+
}
|
|
1470
|
+
function graphViewDepth(view) {
|
|
1471
|
+
if (view === "local") {
|
|
1472
|
+
return 0;
|
|
1473
|
+
}
|
|
1474
|
+
if (view === "expanded") {
|
|
1475
|
+
return 2;
|
|
1476
|
+
}
|
|
1477
|
+
return 1;
|
|
1478
|
+
}
|
|
1479
|
+
function graphViewFromDepth(depth) {
|
|
1480
|
+
if (depth === 0) {
|
|
1481
|
+
return "local";
|
|
1482
|
+
}
|
|
1483
|
+
if (depth === 2) {
|
|
1484
|
+
return "expanded";
|
|
1485
|
+
}
|
|
1486
|
+
return "neighbors";
|
|
1487
|
+
}
|
|
1488
|
+
function defaultGraphView(cwd, scopes, fromFile) {
|
|
1489
|
+
if (fromFile) {
|
|
1490
|
+
return undefined;
|
|
1491
|
+
}
|
|
1492
|
+
if (scopes.length !== 1) {
|
|
1493
|
+
return "neighbors";
|
|
1494
|
+
}
|
|
1495
|
+
const absolute = resolveMaybeRelative(cwd, scopes[0]);
|
|
1496
|
+
if (fs.existsSync(absolute) && fs.statSync(absolute).isDirectory()) {
|
|
1497
|
+
return "local";
|
|
1498
|
+
}
|
|
1499
|
+
return "neighbors";
|
|
1500
|
+
}
|
|
1501
|
+
function readFlagValue(args, flag) {
|
|
1502
|
+
const index = args.indexOf(flag);
|
|
1503
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
1504
|
+
}
|
|
1505
|
+
function isObject(value) {
|
|
1506
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1507
|
+
}
|
|
1508
|
+
function isGraphSourceFile(filePath) {
|
|
1509
|
+
return GRAPH_SOURCE_EXTENSIONS.includes(path.extname(filePath)) &&
|
|
1510
|
+
!filePath.endsWith(".d.ts");
|
|
1511
|
+
}
|
|
1512
|
+
function toExternalPackageName(specifier) {
|
|
1513
|
+
if (specifier.startsWith("node:")) {
|
|
1514
|
+
return specifier;
|
|
1515
|
+
}
|
|
1516
|
+
if (specifier.startsWith("@")) {
|
|
1517
|
+
const parts = specifier.split("/");
|
|
1518
|
+
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : specifier;
|
|
1519
|
+
}
|
|
1520
|
+
return specifier.split("/")[0] ?? specifier;
|
|
1521
|
+
}
|
|
1522
|
+
function resolveMaybeRelative(cwd, rawPath) {
|
|
1523
|
+
return path.resolve(cwd, rawPath);
|
|
1524
|
+
}
|
|
1525
|
+
function toGraphPath(cwd, filePath) {
|
|
1526
|
+
return normalizeGraphPath(path.relative(cwd, path.resolve(filePath)));
|
|
1527
|
+
}
|
|
1528
|
+
function normalizeGraphPath(filePath) {
|
|
1529
|
+
return filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
1530
|
+
}
|
|
1531
|
+
function toCliDisplayPath(cwd, filePath) {
|
|
1532
|
+
const relative = path.relative(cwd, filePath);
|
|
1533
|
+
return relative && !relative.startsWith("..") ? normalizeGraphPath(relative) : filePath;
|
|
1534
|
+
}
|
|
1535
|
+
function uniqueSorted(items) {
|
|
1536
|
+
return Array.from(new Set(items)).sort();
|
|
1537
|
+
}
|
|
1538
|
+
function compareEdges(left, right) {
|
|
1539
|
+
return left.from.localeCompare(right.from) || left.to.localeCompare(right.to) || left.specifier.localeCompare(right.specifier);
|
|
1540
|
+
}
|
|
1541
|
+
function summarizeList(items, limit) {
|
|
1542
|
+
return withHiddenCount(items.slice(0, limit), items.length);
|
|
1543
|
+
}
|
|
1544
|
+
function withHiddenCount(visible, total) {
|
|
1545
|
+
const hidden = total - visible.length;
|
|
1546
|
+
return hidden > 0 ? [...visible, `+ ${hidden} more`] : visible;
|
|
1547
|
+
}
|
|
1548
|
+
function countEntrypoints(files) {
|
|
1549
|
+
return files.filter((file) => file.importedBy.length === 0 && file.imports.length > 0).length;
|
|
1550
|
+
}
|
|
1551
|
+
function countLeaves(files) {
|
|
1552
|
+
return files.filter((file) => file.imports.length === 0 && file.importedBy.length > 0).length;
|
|
1553
|
+
}
|