@lzehrung/codegraph 3.0.1 → 3.1.1
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 +17 -17
- package/codegraph-skill/codegraph/SKILL.md +6 -2
- package/dist/agent/artifact.d.ts.map +1 -1
- package/dist/agent/artifact.js +4 -9
- package/dist/agent/artifact.js.map +1 -1
- package/dist/agent/call-hierarchy.d.ts +4 -2
- package/dist/agent/call-hierarchy.d.ts.map +1 -1
- package/dist/agent/call-hierarchy.js +20 -29
- package/dist/agent/call-hierarchy.js.map +1 -1
- package/dist/agent/explain.d.ts.map +1 -1
- package/dist/agent/explain.js +24 -18
- package/dist/agent/explain.js.map +1 -1
- package/dist/agent/explore.d.ts.map +1 -1
- package/dist/agent/explore.js +2 -11
- package/dist/agent/explore.js.map +1 -1
- package/dist/agent/file-view.d.ts.map +1 -1
- package/dist/agent/file-view.js +4 -12
- package/dist/agent/file-view.js.map +1 -1
- package/dist/agent/normalize.d.ts +0 -3
- package/dist/agent/normalize.d.ts.map +1 -1
- package/dist/agent/normalize.js +1 -7
- package/dist/agent/normalize.js.map +1 -1
- package/dist/agent/orient.d.ts.map +1 -1
- package/dist/agent/orient.js +2 -6
- package/dist/agent/orient.js.map +1 -1
- package/dist/agent/packet.d.ts.map +1 -1
- package/dist/agent/packet.js +2 -5
- package/dist/agent/packet.js.map +1 -1
- package/dist/agent/query-index/content.d.ts.map +1 -1
- package/dist/agent/query-index/content.js +2 -6
- package/dist/agent/query-index/content.js.map +1 -1
- package/dist/agent/query-index/update.d.ts.map +1 -1
- package/dist/agent/query-index/update.js +8 -45
- package/dist/agent/query-index/update.js.map +1 -1
- package/dist/agent/query-index/worker-pool.d.ts.map +1 -1
- package/dist/agent/query-index/worker-pool.js +2 -1
- package/dist/agent/query-index/worker-pool.js.map +1 -1
- package/dist/agent/refactor-plan.d.ts +2 -2
- package/dist/agent/refactor-plan.d.ts.map +1 -1
- package/dist/agent/refactor-plan.js +11 -57
- package/dist/agent/refactor-plan.js.map +1 -1
- package/dist/agent/rename-preview.d.ts.map +1 -1
- package/dist/agent/rename-preview.js +166 -23
- package/dist/agent/rename-preview.js.map +1 -1
- package/dist/agent/search.d.ts +13 -0
- package/dist/agent/search.d.ts.map +1 -1
- package/dist/agent/search.js +17 -26
- package/dist/agent/search.js.map +1 -1
- package/dist/agent/semantic-symbols.d.ts +1 -1
- package/dist/agent/semantic-symbols.d.ts.map +1 -1
- package/dist/agent/semantic-symbols.js +2 -5
- package/dist/agent/semantic-symbols.js.map +1 -1
- package/dist/agent/semantic.d.ts +5 -0
- package/dist/agent/semantic.d.ts.map +1 -1
- package/dist/agent/semantic.js +12 -1
- package/dist/agent/semantic.js.map +1 -1
- package/dist/agent/session.d.ts +9 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +59 -41
- package/dist/agent/session.js.map +1 -1
- package/dist/agent/type-hierarchy.d.ts +4 -2
- package/dist/agent/type-hierarchy.d.ts.map +1 -1
- package/dist/agent/type-hierarchy.js +19 -31
- package/dist/agent/type-hierarchy.js.map +1 -1
- package/dist/agent/workspace-symbols.d.ts +1 -1
- package/dist/agent/workspace-symbols.d.ts.map +1 -1
- package/dist/agent/workspace-symbols.js +7 -15
- package/dist/agent/workspace-symbols.js.map +1 -1
- package/dist/analysis-summary.d.ts +1 -4
- package/dist/analysis-summary.d.ts.map +1 -1
- package/dist/analysis-summary.js +58 -19
- package/dist/analysis-summary.js.map +1 -1
- package/dist/bin/cli.js +41060 -27641
- package/dist/bin/query-index-worker.js +268 -241
- package/dist/bootstrap/tree-sitter-languages.d.ts +2 -0
- package/dist/bootstrap/tree-sitter-languages.d.ts.map +1 -1
- package/dist/bootstrap/tree-sitter-languages.js +6 -19
- package/dist/bootstrap/tree-sitter-languages.js.map +1 -1
- package/dist/chunking/chunk-matches.d.ts.map +1 -1
- package/dist/chunking/chunk-matches.js +6 -42
- package/dist/chunking/chunk-matches.js.map +1 -1
- package/dist/chunking/chunk-split.d.ts +7 -0
- package/dist/chunking/chunk-split.d.ts.map +1 -1
- package/dist/chunking/chunk-split.js +3 -3
- package/dist/chunking/chunk-split.js.map +1 -1
- package/dist/chunking/chunk-text-file.js +2 -30
- package/dist/chunking/chunk-text-file.js.map +1 -1
- package/dist/cli/affected.d.ts.map +1 -1
- package/dist/cli/affected.js +1 -9
- package/dist/cli/affected.js.map +1 -1
- package/dist/cli/call-hierarchy.js +3 -6
- package/dist/cli/call-hierarchy.js.map +1 -1
- package/dist/cli/chunk.d.ts.map +1 -1
- package/dist/cli/chunk.js +4 -11
- package/dist/cli/chunk.js.map +1 -1
- package/dist/cli/command-table.d.ts.map +1 -1
- package/dist/cli/command-table.js +7 -0
- package/dist/cli/command-table.js.map +1 -1
- package/dist/cli/context.d.ts +1 -0
- package/dist/cli/context.d.ts.map +1 -1
- package/dist/cli/context.js +1 -1
- package/dist/cli/context.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +1 -11
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/duplicates.d.ts +5 -0
- package/dist/cli/duplicates.d.ts.map +1 -1
- package/dist/cli/duplicates.js +15 -2
- package/dist/cli/duplicates.js.map +1 -1
- package/dist/cli/graph.d.ts +4 -12
- package/dist/cli/graph.d.ts.map +1 -1
- package/dist/cli/graph.js +17 -8
- package/dist/cli/graph.js.map +1 -1
- package/dist/cli/help.d.ts +4 -4
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +17 -9
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/index.d.ts +2 -12
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/links.d.ts.map +1 -1
- package/dist/cli/links.js +4 -10
- package/dist/cli/links.js.map +1 -1
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +5 -0
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/module-locals.d.ts +8 -0
- package/dist/cli/module-locals.d.ts.map +1 -0
- package/dist/cli/module-locals.js +9 -0
- package/dist/cli/module-locals.js.map +1 -0
- package/dist/cli/navigation.d.ts +2 -0
- package/dist/cli/navigation.d.ts.map +1 -1
- package/dist/cli/navigation.js +3 -5
- package/dist/cli/navigation.js.map +1 -1
- package/dist/cli/options.d.ts.map +1 -1
- package/dist/cli/options.js +6 -1
- package/dist/cli/options.js.map +1 -1
- package/dist/cli/pretty.d.ts +1 -0
- package/dist/cli/pretty.d.ts.map +1 -1
- package/dist/cli/pretty.js +6 -8
- package/dist/cli/pretty.js.map +1 -1
- package/dist/cli/progress.d.ts.map +1 -1
- package/dist/cli/progress.js +42 -16
- package/dist/cli/progress.js.map +1 -1
- package/dist/cli/refactor-plan.js +2 -5
- package/dist/cli/refactor-plan.js.map +1 -1
- package/dist/cli/rename-preview.js +2 -5
- package/dist/cli/rename-preview.js.map +1 -1
- package/dist/cli/review.d.ts +6 -16
- package/dist/cli/review.d.ts.map +1 -1
- package/dist/cli/review.js +2 -0
- package/dist/cli/review.js.map +1 -1
- package/dist/cli/server.d.ts.map +1 -1
- package/dist/cli/server.js +9 -10
- package/dist/cli/server.js.map +1 -1
- package/dist/cli/type-hierarchy.js +8 -8
- package/dist/cli/type-hierarchy.js.map +1 -1
- package/dist/cli/viewer.js +1 -1
- package/dist/cli/viewer.js.map +1 -1
- package/dist/document-links/asciidoc.d.ts.map +1 -1
- package/dist/document-links/asciidoc.js +4 -16
- package/dist/document-links/asciidoc.js.map +1 -1
- package/dist/document-links/check.d.ts.map +1 -1
- package/dist/document-links/check.js +1 -3
- package/dist/document-links/check.js.map +1 -1
- package/dist/document-links/html.d.ts.map +1 -1
- package/dist/document-links/html.js +6 -44
- package/dist/document-links/html.js.map +1 -1
- package/dist/document-links/markdown.d.ts.map +1 -1
- package/dist/document-links/markdown.js +4 -13
- package/dist/document-links/markdown.js.map +1 -1
- package/dist/document-links/rst.d.ts.map +1 -1
- package/dist/document-links/rst.js +7 -31
- package/dist/document-links/rst.js.map +1 -1
- package/dist/document-links/sfc.d.ts.map +1 -1
- package/dist/document-links/sfc.js +2 -5
- package/dist/document-links/sfc.js.map +1 -1
- package/dist/document-links/shared.d.ts +2 -0
- package/dist/document-links/shared.d.ts.map +1 -1
- package/dist/document-links/shared.js +13 -0
- package/dist/document-links/shared.js.map +1 -1
- package/dist/drift/artifact.d.ts.map +1 -1
- package/dist/drift/artifact.js +4 -27
- package/dist/drift/artifact.js.map +1 -1
- package/dist/drift/git.d.ts.map +1 -1
- package/dist/drift/git.js +10 -2
- package/dist/drift/git.js.map +1 -1
- package/dist/drift/graph-edges.d.ts +4 -0
- package/dist/drift/graph-edges.d.ts.map +1 -0
- package/dist/drift/graph-edges.js +15 -0
- package/dist/drift/graph-edges.js.map +1 -0
- package/dist/drift/snapshot.d.ts.map +1 -1
- package/dist/drift/snapshot.js +2 -17
- package/dist/drift/snapshot.js.map +1 -1
- package/dist/duplicates/candidates.d.ts +61 -0
- package/dist/duplicates/candidates.d.ts.map +1 -0
- package/dist/duplicates/candidates.js +873 -0
- package/dist/duplicates/candidates.js.map +1 -0
- package/dist/duplicates/progress.d.ts +17 -0
- package/dist/duplicates/progress.d.ts.map +1 -0
- package/dist/duplicates/progress.js +57 -0
- package/dist/duplicates/progress.js.map +1 -0
- package/dist/duplicates/scoring.d.ts +9 -40
- package/dist/duplicates/scoring.d.ts.map +1 -1
- package/dist/duplicates/scoring.js +34 -414
- package/dist/duplicates/scoring.js.map +1 -1
- package/dist/duplicates/types.d.ts +30 -1
- package/dist/duplicates/types.d.ts.map +1 -1
- package/dist/duplicates/unit-cache.d.ts +11 -1
- package/dist/duplicates/unit-cache.d.ts.map +1 -1
- package/dist/duplicates/unit-cache.js +20 -2
- package/dist/duplicates/unit-cache.js.map +1 -1
- package/dist/duplicates/units.d.ts +4 -2
- package/dist/duplicates/units.d.ts.map +1 -1
- package/dist/duplicates/units.js +47 -25
- package/dist/duplicates/units.js.map +1 -1
- package/dist/duplicates.d.ts +1 -0
- package/dist/duplicates.d.ts.map +1 -1
- package/dist/duplicates.js +116 -62
- package/dist/duplicates.js.map +1 -1
- package/dist/graph-builder.d.ts +1 -1
- package/dist/graph-builder.d.ts.map +1 -1
- package/dist/graph-builder.js +3 -2
- package/dist/graph-builder.js.map +1 -1
- package/dist/graph-edge-collector.d.ts +3 -1
- package/dist/graph-edge-collector.d.ts.map +1 -1
- package/dist/graph-edge-collector.js +38 -6
- package/dist/graph-edge-collector.js.map +1 -1
- package/dist/graphs/angularjs.d.ts.map +1 -1
- package/dist/graphs/angularjs.js +2 -9
- package/dist/graphs/angularjs.js.map +1 -1
- package/dist/graphs/edge-resolution.d.ts +4 -0
- package/dist/graphs/edge-resolution.d.ts.map +1 -1
- package/dist/graphs/edge-resolution.js +2 -0
- package/dist/graphs/edge-resolution.js.map +1 -1
- package/dist/graphs/external/manifests.d.ts.map +1 -1
- package/dist/graphs/external/manifests.js +1 -6
- package/dist/graphs/external/manifests.js.map +1 -1
- package/dist/graphs/specifiers.d.ts.map +1 -1
- package/dist/graphs/specifiers.js +47 -26
- package/dist/graphs/specifiers.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts +2 -0
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.js +9 -3
- package/dist/graphs/symbol-graph-detailed/edge-passes.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts +3 -3
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js +2 -26
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed.js +38 -1
- package/dist/graphs/symbol-graph-detailed.js.map +1 -1
- package/dist/impact/analyzer.d.ts.map +1 -1
- package/dist/impact/analyzer.js +23 -17
- package/dist/impact/analyzer.js.map +1 -1
- package/dist/impact/budgets.d.ts.map +1 -1
- package/dist/impact/budgets.js +3 -9
- package/dist/impact/budgets.js.map +1 -1
- package/dist/impact/call-compatibility.d.ts.map +1 -1
- package/dist/impact/call-compatibility.js +3 -12
- package/dist/impact/call-compatibility.js.map +1 -1
- package/dist/impact/context.d.ts +6 -0
- package/dist/impact/context.d.ts.map +1 -1
- package/dist/impact/context.js +2 -2
- package/dist/impact/context.js.map +1 -1
- package/dist/impact/direct.d.ts.map +1 -1
- package/dist/impact/direct.js +2 -6
- package/dist/impact/direct.js.map +1 -1
- package/dist/impact/map.d.ts +3 -1
- package/dist/impact/map.d.ts.map +1 -1
- package/dist/impact/map.js +12 -5
- package/dist/impact/map.js.map +1 -1
- package/dist/impact/providers/base.js +1 -1
- package/dist/impact/providers/base.js.map +1 -1
- package/dist/impact/reference-cache.d.ts.map +1 -1
- package/dist/impact/reference-cache.js +1 -24
- package/dist/impact/reference-cache.js.map +1 -1
- package/dist/impact/report-shared.d.ts +2 -1
- package/dist/impact/report-shared.d.ts.map +1 -1
- package/dist/impact/report-shared.js +10 -0
- package/dist/impact/report-shared.js.map +1 -1
- package/dist/impact/report-suggestions.d.ts.map +1 -1
- package/dist/impact/report-suggestions.js +2 -8
- package/dist/impact/report-suggestions.js.map +1 -1
- package/dist/impact/report.d.ts.map +1 -1
- package/dist/impact/report.js +2 -8
- package/dist/impact/report.js.map +1 -1
- package/dist/impact/streaming.d.ts.map +1 -1
- package/dist/impact/streaming.js +3 -8
- package/dist/impact/streaming.js.map +1 -1
- package/dist/indexer/build-cache/manifest.d.ts.map +1 -1
- package/dist/indexer/build-cache/manifest.js +10 -108
- package/dist/indexer/build-cache/manifest.js.map +1 -1
- package/dist/indexer/build-cache/module-cache.d.ts +6 -1
- package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache/module-cache.js +25 -2
- package/dist/indexer/build-cache/module-cache.js.map +1 -1
- package/dist/indexer/build-cache/options.d.ts +38 -1
- package/dist/indexer/build-cache/options.d.ts.map +1 -1
- package/dist/indexer/build-cache/options.js +38 -1
- package/dist/indexer/build-cache/options.js.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.js +48 -99
- package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
- package/dist/indexer/build-cache/reports.d.ts +1 -0
- package/dist/indexer/build-cache/reports.d.ts.map +1 -1
- package/dist/indexer/build-cache/reports.js +10 -0
- package/dist/indexer/build-cache/reports.js.map +1 -1
- package/dist/indexer/build-index.d.ts +2 -0
- package/dist/indexer/build-index.d.ts.map +1 -1
- package/dist/indexer/build-index.js +187 -57
- package/dist/indexer/build-index.js.map +1 -1
- package/dist/indexer/build-workers.d.ts +7 -1
- package/dist/indexer/build-workers.d.ts.map +1 -1
- package/dist/indexer/build-workers.js +12 -0
- package/dist/indexer/build-workers.js.map +1 -1
- package/dist/indexer/call-hierarchy.d.ts.map +1 -1
- package/dist/indexer/call-hierarchy.js +5 -17
- package/dist/indexer/call-hierarchy.js.map +1 -1
- package/dist/indexer/compilation-units.d.ts +32 -20
- package/dist/indexer/compilation-units.d.ts.map +1 -1
- package/dist/indexer/compilation-units.js +139 -145
- package/dist/indexer/compilation-units.js.map +1 -1
- package/dist/indexer/cpp-callables.d.ts.map +1 -1
- package/dist/indexer/cpp-callables.js +2 -7
- package/dist/indexer/cpp-callables.js.map +1 -1
- package/dist/indexer/import-types.d.ts +5 -0
- package/dist/indexer/import-types.d.ts.map +1 -1
- package/dist/indexer/import-types.js.map +1 -1
- package/dist/indexer/imports/binding-ranges.d.ts +5 -12
- package/dist/indexer/imports/binding-ranges.d.ts.map +1 -1
- package/dist/indexer/imports/binding-ranges.js +13 -19
- package/dist/indexer/imports/binding-ranges.js.map +1 -1
- package/dist/indexer/imports/context.d.ts +6 -0
- package/dist/indexer/imports/context.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.d.ts +28 -13
- package/dist/indexer/imports/import-binding-tables.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.js +179 -178
- package/dist/indexer/imports/import-binding-tables.js.map +1 -1
- package/dist/indexer/imports/js-text-imports.d.ts +0 -1
- package/dist/indexer/imports/js-text-imports.d.ts.map +1 -1
- package/dist/indexer/imports/js-text-imports.js +0 -176
- package/dist/indexer/imports/js-text-imports.js.map +1 -1
- package/dist/indexer/imports/language-specific.d.ts.map +1 -1
- package/dist/indexer/imports/language-specific.js +4 -8
- package/dist/indexer/imports/language-specific.js.map +1 -1
- package/dist/indexer/imports/python.d.ts +0 -1
- package/dist/indexer/imports/python.d.ts.map +1 -1
- package/dist/indexer/imports/python.js +1 -29
- package/dist/indexer/imports/python.js.map +1 -1
- package/dist/indexer/imports/text-import-extractors.d.ts +2 -9
- package/dist/indexer/imports/text-import-extractors.d.ts.map +1 -1
- package/dist/indexer/imports/text-import-extractors.js +0 -284
- package/dist/indexer/imports/text-import-extractors.js.map +1 -1
- package/dist/indexer/imports.d.ts +15 -0
- package/dist/indexer/imports.d.ts.map +1 -1
- package/dist/indexer/imports.js +82 -44
- package/dist/indexer/imports.js.map +1 -1
- package/dist/indexer/incremental-plan.d.ts +0 -1
- package/dist/indexer/incremental-plan.d.ts.map +1 -1
- package/dist/indexer/incremental-plan.js +6 -12
- package/dist/indexer/incremental-plan.js.map +1 -1
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts.map +1 -1
- package/dist/indexer/name-lookup-policies/implicit-self.js +25 -4
- package/dist/indexer/name-lookup-policies/implicit-self.js.map +1 -1
- package/dist/indexer/name-resolution-types.d.ts +5 -0
- package/dist/indexer/name-resolution-types.d.ts.map +1 -1
- package/dist/indexer/name-resolution.d.ts.map +1 -1
- package/dist/indexer/name-resolution.js +2 -0
- package/dist/indexer/name-resolution.js.map +1 -1
- package/dist/indexer/navigation-cpp.d.ts.map +1 -1
- package/dist/indexer/navigation-cpp.js +3 -5
- package/dist/indexer/navigation-cpp.js.map +1 -1
- package/dist/indexer/navigation-goto.d.ts +49 -3
- package/dist/indexer/navigation-goto.d.ts.map +1 -1
- package/dist/indexer/navigation-goto.js +500 -106
- package/dist/indexer/navigation-goto.js.map +1 -1
- package/dist/indexer/navigation-local.d.ts +7 -0
- package/dist/indexer/navigation-local.d.ts.map +1 -1
- package/dist/indexer/navigation-local.js +22 -2
- package/dist/indexer/navigation-local.js.map +1 -1
- package/dist/indexer/navigation-php.d.ts +0 -1
- package/dist/indexer/navigation-php.d.ts.map +1 -1
- package/dist/indexer/navigation-php.js +1 -7
- package/dist/indexer/navigation-php.js.map +1 -1
- package/dist/indexer/navigation-references.d.ts +1 -0
- package/dist/indexer/navigation-references.d.ts.map +1 -1
- package/dist/indexer/navigation-references.js +134 -23
- package/dist/indexer/navigation-references.js.map +1 -1
- package/dist/indexer/navigation-resolve.d.ts +22 -0
- package/dist/indexer/navigation-resolve.d.ts.map +1 -1
- package/dist/indexer/navigation-resolve.js +73 -8
- package/dist/indexer/navigation-resolve.js.map +1 -1
- package/dist/indexer/navigation.d.ts +4 -1
- package/dist/indexer/navigation.d.ts.map +1 -1
- package/dist/indexer/navigation.js +398 -95
- package/dist/indexer/navigation.js.map +1 -1
- package/dist/indexer/python-property-decorator.d.ts +11 -0
- package/dist/indexer/python-property-decorator.d.ts.map +1 -0
- package/dist/indexer/python-property-decorator.js +30 -0
- package/dist/indexer/python-property-decorator.js.map +1 -0
- package/dist/indexer/shared-owner-identity.d.ts +3 -1
- package/dist/indexer/shared-owner-identity.d.ts.map +1 -1
- package/dist/indexer/shared-owner-identity.js +40 -12
- package/dist/indexer/shared-owner-identity.js.map +1 -1
- package/dist/indexer/shared.d.ts +10 -0
- package/dist/indexer/shared.d.ts.map +1 -1
- package/dist/indexer/shared.js +8 -0
- package/dist/indexer/shared.js.map +1 -1
- package/dist/indexer/star-import-precedence.d.ts +5 -1
- package/dist/indexer/star-import-precedence.d.ts.map +1 -1
- package/dist/indexer/star-import-precedence.js +12 -19
- package/dist/indexer/star-import-precedence.js.map +1 -1
- package/dist/indexer/type-hierarchy.d.ts.map +1 -1
- package/dist/indexer/type-hierarchy.js +8 -9
- package/dist/indexer/type-hierarchy.js.map +1 -1
- package/dist/indexer/types.d.ts +49 -1
- package/dist/indexer/types.d.ts.map +1 -1
- package/dist/indexer/types.js.map +1 -1
- package/dist/indexer/workspace-symbols.d.ts.map +1 -1
- package/dist/indexer/workspace-symbols.js +3 -13
- package/dist/indexer/workspace-symbols.js.map +1 -1
- package/dist/installer/locks.d.ts +1 -0
- package/dist/installer/locks.d.ts.map +1 -1
- package/dist/installer/locks.js +4 -8
- package/dist/installer/locks.js.map +1 -1
- package/dist/installer/registry.d.ts.map +1 -1
- package/dist/installer/registry.js +9 -15
- package/dist/installer/registry.js.map +1 -1
- package/dist/languages/callable-arity.d.ts.map +1 -1
- package/dist/languages/callable-arity.js +1 -38
- package/dist/languages/callable-arity.js.map +1 -1
- package/dist/languages/definitions/csharp.d.ts.map +1 -1
- package/dist/languages/definitions/csharp.js +5 -13
- package/dist/languages/definitions/csharp.js.map +1 -1
- package/dist/languages/definitions/java.d.ts.map +1 -1
- package/dist/languages/definitions/java.js +4 -13
- package/dist/languages/definitions/java.js.map +1 -1
- package/dist/languages/definitions/kotlin.d.ts.map +1 -1
- package/dist/languages/definitions/kotlin.js +4 -8
- package/dist/languages/definitions/kotlin.js.map +1 -1
- package/dist/languages/definitions/python.d.ts.map +1 -1
- package/dist/languages/definitions/python.js +5 -3
- package/dist/languages/definitions/python.js.map +1 -1
- package/dist/languages/import-statement-parsers.d.ts +0 -30
- package/dist/languages/import-statement-parsers.d.ts.map +1 -1
- package/dist/languages/import-statement-parsers.js +1 -81
- package/dist/languages/import-statement-parsers.js.map +1 -1
- package/dist/languages/sfc.d.ts.map +1 -1
- package/dist/languages/sfc.js +8 -43
- package/dist/languages/sfc.js.map +1 -1
- package/dist/lifecycle/manifest.d.ts.map +1 -1
- package/dist/lifecycle/manifest.js +11 -29
- package/dist/lifecycle/manifest.js.map +1 -1
- package/dist/mcp/cancellation.d.ts +6 -0
- package/dist/mcp/cancellation.d.ts.map +1 -0
- package/dist/mcp/cancellation.js +11 -0
- package/dist/mcp/cancellation.js.map +1 -0
- package/dist/mcp/handlers.d.ts +54 -3
- package/dist/mcp/handlers.d.ts.map +1 -1
- package/dist/mcp/handlers.js +186 -42
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/mcp/http-transport.d.ts.map +1 -1
- package/dist/mcp/http-transport.js +9 -2
- package/dist/mcp/http-transport.js.map +1 -1
- package/dist/mcp/index-readiness.d.ts +114 -0
- package/dist/mcp/index-readiness.d.ts.map +1 -0
- package/dist/mcp/index-readiness.js +264 -0
- package/dist/mcp/index-readiness.js.map +1 -0
- package/dist/mcp/project-watcher.d.ts +125 -0
- package/dist/mcp/project-watcher.d.ts.map +1 -0
- package/dist/mcp/project-watcher.js +643 -0
- package/dist/mcp/project-watcher.js.map +1 -0
- package/dist/mcp/protocol.d.ts.map +1 -1
- package/dist/mcp/protocol.js +23 -10
- package/dist/mcp/protocol.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +11 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/stale-install.d.ts +13 -0
- package/dist/mcp/stale-install.d.ts.map +1 -0
- package/dist/mcp/stale-install.js +30 -0
- package/dist/mcp/stale-install.js.map +1 -0
- package/dist/native/byte-index.d.ts +1 -0
- package/dist/native/byte-index.d.ts.map +1 -1
- package/dist/native/byte-index.js +1 -1
- package/dist/native/byte-index.js.map +1 -1
- package/dist/native/contracts.d.ts +1 -0
- package/dist/native/contracts.d.ts.map +1 -1
- package/dist/native/native-backend-report.d.ts +2 -1
- package/dist/native/native-backend-report.d.ts.map +1 -1
- package/dist/native/native-backend-report.js +23 -0
- package/dist/native/native-backend-report.js.map +1 -1
- package/dist/native/tree-shape.d.ts +5 -7
- package/dist/native/tree-shape.d.ts.map +1 -1
- package/dist/native/tree-shape.js +11 -10
- package/dist/native/tree-shape.js.map +1 -1
- package/dist/presentation/bounds.d.ts +2 -0
- package/dist/presentation/bounds.d.ts.map +1 -1
- package/dist/presentation/bounds.js +8 -0
- package/dist/presentation/bounds.js.map +1 -1
- package/dist/review/candidates.d.ts.map +1 -1
- package/dist/review/candidates.js +4 -25
- package/dist/review/candidates.js.map +1 -1
- package/dist/review/deleted.js +1 -1
- package/dist/review/deleted.js.map +1 -1
- package/dist/review/report.d.ts.map +1 -1
- package/dist/review/report.js +4 -2
- package/dist/review/report.js.map +1 -1
- package/dist/review/summaries.d.ts.map +1 -1
- package/dist/review/summaries.js +14 -35
- package/dist/review/summaries.js.map +1 -1
- package/dist/review.d.ts +6 -0
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +5 -6
- package/dist/review.js.map +1 -1
- package/dist/scss/navigation.d.ts +13 -0
- package/dist/scss/navigation.d.ts.map +1 -0
- package/dist/scss/navigation.js +150 -0
- package/dist/scss/navigation.js.map +1 -0
- package/dist/scss/resolution.d.ts +190 -0
- package/dist/scss/resolution.d.ts.map +1 -0
- package/dist/scss/resolution.js +1802 -0
- package/dist/scss/resolution.js.map +1 -0
- package/dist/scss/syntax.d.ts +181 -0
- package/dist/scss/syntax.d.ts.map +1 -0
- package/dist/scss/syntax.js +1436 -0
- package/dist/scss/syntax.js.map +1 -0
- package/dist/sql/classify-sql-file.d.ts +7 -0
- package/dist/sql/classify-sql-file.d.ts.map +1 -1
- package/dist/sql/classify-sql-file.js +9 -0
- package/dist/sql/classify-sql-file.js.map +1 -1
- package/dist/sql/extract-facts.d.ts.map +1 -1
- package/dist/sql/extract-facts.js +517 -77
- package/dist/sql/extract-facts.js.map +1 -1
- package/dist/sql/lex.d.ts +100 -1
- package/dist/sql/lex.d.ts.map +1 -1
- package/dist/sql/lex.js +473 -11
- package/dist/sql/lex.js.map +1 -1
- package/dist/sql/lookup.d.ts +17 -0
- package/dist/sql/lookup.d.ts.map +1 -1
- package/dist/sql/lookup.js +21 -0
- package/dist/sql/lookup.js.map +1 -1
- package/dist/sql/navigation.d.ts +28 -1
- package/dist/sql/navigation.d.ts.map +1 -1
- package/dist/sql/navigation.js +474 -84
- package/dist/sql/navigation.js.map +1 -1
- package/dist/sql/object-mentions.d.ts +24 -0
- package/dist/sql/object-mentions.d.ts.map +1 -0
- package/dist/sql/object-mentions.js +233 -0
- package/dist/sql/object-mentions.js.map +1 -0
- package/dist/sql/review.d.ts +3 -0
- package/dist/sql/review.d.ts.map +1 -1
- package/dist/sql/review.js +15 -14
- package/dist/sql/review.js.map +1 -1
- package/dist/sql/routine-mentions.d.ts +61 -0
- package/dist/sql/routine-mentions.d.ts.map +1 -0
- package/dist/sql/routine-mentions.js +868 -0
- package/dist/sql/routine-mentions.js.map +1 -0
- package/dist/sql/source-graph.d.ts +18 -3
- package/dist/sql/source-graph.d.ts.map +1 -1
- package/dist/sql/source-graph.js +109 -32
- package/dist/sql/source-graph.js.map +1 -1
- package/dist/sqlite/raw-query-worker-pool.d.ts.map +1 -1
- package/dist/sqlite/raw-query-worker-pool.js +2 -0
- package/dist/sqlite/raw-query-worker-pool.js.map +1 -1
- package/dist/sqlite/types.d.ts +6 -5
- package/dist/sqlite/types.d.ts.map +1 -1
- package/dist/sqlite/write.d.ts.map +1 -1
- package/dist/sqlite/write.js.map +1 -1
- package/dist/sqlite-driver.d.ts +0 -1
- package/dist/sqlite-driver.d.ts.map +1 -1
- package/dist/sqlite-driver.js +0 -11
- package/dist/sqlite-driver.js.map +1 -1
- package/dist/terraform/navigation.d.ts.map +1 -1
- package/dist/terraform/navigation.js +1 -3
- package/dist/terraform/navigation.js.map +1 -1
- package/dist/terraform/syntax.d.ts.map +1 -1
- package/dist/terraform/syntax.js +1 -4
- package/dist/terraform/syntax.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/ast.d.ts +5 -0
- package/dist/util/ast.d.ts.map +1 -1
- package/dist/util/ast.js +10 -0
- package/dist/util/ast.js.map +1 -1
- package/dist/util/atomic-file.d.ts +22 -0
- package/dist/util/atomic-file.d.ts.map +1 -0
- package/dist/util/atomic-file.js +106 -0
- package/dist/util/atomic-file.js.map +1 -0
- package/dist/util/collections.d.ts +5 -0
- package/dist/util/collections.d.ts.map +1 -1
- package/dist/util/collections.js +26 -0
- package/dist/util/collections.js.map +1 -1
- package/dist/util/concurrency.d.ts +8 -0
- package/dist/util/concurrency.d.ts.map +1 -1
- package/dist/util/concurrency.js +16 -0
- package/dist/util/concurrency.js.map +1 -1
- package/dist/util/confined-file.d.ts.map +1 -1
- package/dist/util/confined-file.js +1 -3
- package/dist/util/confined-file.js.map +1 -1
- package/dist/util/csharp-tree.d.ts +65 -0
- package/dist/util/csharp-tree.d.ts.map +1 -0
- package/dist/util/csharp-tree.js +266 -0
- package/dist/util/csharp-tree.js.map +1 -0
- package/dist/util/errors.d.ts +1 -0
- package/dist/util/errors.d.ts.map +1 -1
- package/dist/util/errors.js +3 -0
- package/dist/util/errors.js.map +1 -1
- package/dist/util/graph-edges.d.ts +2 -0
- package/dist/util/graph-edges.d.ts.map +1 -1
- package/dist/util/graph-edges.js +11 -0
- package/dist/util/graph-edges.js.map +1 -1
- package/dist/util/guards.d.ts +1 -0
- package/dist/util/guards.d.ts.map +1 -1
- package/dist/util/guards.js +6 -0
- package/dist/util/guards.js.map +1 -1
- package/dist/util/identifiers.d.ts +0 -24
- package/dist/util/identifiers.d.ts.map +1 -1
- package/dist/util/identifiers.js +0 -24
- package/dist/util/identifiers.js.map +1 -1
- package/dist/util/jvm-tree.d.ts +58 -0
- package/dist/util/jvm-tree.d.ts.map +1 -0
- package/dist/util/jvm-tree.js +153 -0
- package/dist/util/jvm-tree.js.map +1 -0
- package/dist/util/package-exports.d.ts +0 -22
- package/dist/util/package-exports.d.ts.map +1 -1
- package/dist/util/package-exports.js +25 -5
- package/dist/util/package-exports.js.map +1 -1
- package/dist/util/paths.d.ts +1 -0
- package/dist/util/paths.d.ts.map +1 -1
- package/dist/util/paths.js +10 -4
- package/dist/util/paths.js.map +1 -1
- package/dist/util/resolution/csharp.d.ts +20 -8
- package/dist/util/resolution/csharp.d.ts.map +1 -1
- package/dist/util/resolution/csharp.js +126 -127
- package/dist/util/resolution/csharp.js.map +1 -1
- package/dist/util/resolution/files.d.ts +6 -0
- package/dist/util/resolution/files.d.ts.map +1 -1
- package/dist/util/resolution/files.js +74 -21
- package/dist/util/resolution/files.js.map +1 -1
- package/dist/util/resolution/jvm.d.ts +21 -1
- package/dist/util/resolution/jvm.d.ts.map +1 -1
- package/dist/util/resolution/jvm.js +168 -100
- package/dist/util/resolution/jvm.js.map +1 -1
- package/dist/util/resolution/project-symbols.d.ts +1 -1
- package/dist/util/resolution/project-symbols.d.ts.map +1 -1
- package/dist/util/resolution/project-symbols.js.map +1 -1
- package/dist/util/resolution/specifier-targets.d.ts +4 -0
- package/dist/util/resolution/specifier-targets.d.ts.map +1 -1
- package/dist/util/resolution/specifier-targets.js +11 -3
- package/dist/util/resolution/specifier-targets.js.map +1 -1
- package/dist/util/resolution.d.ts.map +1 -1
- package/dist/util/resolution.js +21 -7
- package/dist/util/resolution.js.map +1 -1
- package/dist/util/syntax-tree.d.ts +16 -0
- package/dist/util/syntax-tree.d.ts.map +1 -0
- package/dist/util/syntax-tree.js +34 -0
- package/dist/util/syntax-tree.js.map +1 -0
- package/dist/util/text-scanning.d.ts +6 -0
- package/dist/util/text-scanning.d.ts.map +1 -0
- package/dist/util/text-scanning.js +35 -0
- package/dist/util/text-scanning.js.map +1 -0
- package/dist/util/trivia-tables.d.ts +13 -8
- package/dist/util/trivia-tables.d.ts.map +1 -1
- package/dist/util/trivia-tables.js +13 -72
- package/dist/util/trivia-tables.js.map +1 -1
- package/dist/util/trivia.d.ts +10 -3
- package/dist/util/trivia.d.ts.map +1 -1
- package/dist/util/trivia.js +209 -79
- package/dist/util/trivia.js.map +1 -1
- package/dist/util/windows-process-drain.d.ts.map +1 -1
- package/dist/util/windows-process-drain.js.map +1 -1
- package/dist/util/worker-threads.d.ts +16 -0
- package/dist/util/worker-threads.d.ts.map +1 -1
- package/dist/util/worker-threads.js +118 -0
- package/dist/util/worker-threads.js.map +1 -1
- package/dist/worker/native-worker-pool.d.ts.map +1 -1
- package/dist/worker/native-worker-pool.js +2 -1
- package/dist/worker/native-worker-pool.js.map +1 -1
- package/docs/mcp.md +82 -8
- package/package.json +7 -7
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import fsp from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import picomatch from "picomatch";
|
|
5
|
+
import { AGENT_FRESHNESS_CHECK_INTERVAL_MS } from "../agent/session.js";
|
|
6
|
+
import { PROJECT_CACHE_DIRECTORY } from "../indexer/build-cache/location.js";
|
|
7
|
+
import { isRelativePathInside } from "../util/discovery-path.js";
|
|
8
|
+
import { errorMessage, isFileSystemErrorCode, isMissingPathError } from "../util/errors.js";
|
|
9
|
+
import { fileIdentityKey, normalizePath } from "../util/paths.js";
|
|
10
|
+
import { DEFAULT_PROJECT_FILE_IGNORES } from "../util/project-files.js";
|
|
11
|
+
import { DEFAULT_MCP_STDIO_IDLE_TIMEOUT_MS } from "./stdio-lifecycle.js";
|
|
12
|
+
/** Quiet time after the last file event before the burst counts as settled. */
|
|
13
|
+
export const DEFAULT_MCP_WATCH_DEBOUNCE_MS = 500;
|
|
14
|
+
/** Idle time without a tool call before the watcher pauses. */
|
|
15
|
+
export const DEFAULT_MCP_WATCH_IDLE_MS = DEFAULT_MCP_STDIO_IDLE_TIMEOUT_MS;
|
|
16
|
+
/**
|
|
17
|
+
* Minimum time from the end of one background refresh to the start of the next. Defaults to the session's own
|
|
18
|
+
* freshness-check interval.
|
|
19
|
+
*/
|
|
20
|
+
export const DEFAULT_MCP_WATCH_MIN_REFRESH_INTERVAL_MS = AGENT_FRESHNESS_CHECK_INTERVAL_MS;
|
|
21
|
+
const MAX_WATCH_TIMER_MS = 2_147_483_647;
|
|
22
|
+
// What `checkFreshness` reports when it cannot verify the configuration (src/agent/session.ts). The session
|
|
23
|
+
// retains its snapshot then, so the background refresh must not replace it.
|
|
24
|
+
const CONFIGURATION_UNCHECKED_REASON = "session configuration could not be checked";
|
|
25
|
+
// Directories whose contents never reach the index: what default discovery ignores (dependencies, build output,
|
|
26
|
+
// virtual environments, and Codegraph's own state). Discovery and the watcher share one list, so a write that no
|
|
27
|
+
// query can see never clears the session's freshness result. The cache directory is named on top of that list
|
|
28
|
+
// because the server itself writes there.
|
|
29
|
+
const isDiscoveryIgnored = picomatch([...DEFAULT_PROJECT_FILE_IGNORES, `**/${PROJECT_CACHE_DIRECTORY}/**`], {
|
|
30
|
+
dot: true,
|
|
31
|
+
});
|
|
32
|
+
// Per-directory mode keeps a watch for as long as its directory exists, although no file list names the directory: for
|
|
33
|
+
// one that an event reported, and for one that a list named until its last source file was deleted. The watches above
|
|
34
|
+
// such a directory stay too. This many watches that no file list names stay open at once, those above included, so
|
|
35
|
+
// that a build that creates or empties thousands of directories cannot use up the inotify watches that every process
|
|
36
|
+
// of the user shares. Past the limit an event opens no watch, and the next refresh closes the watches that were opened
|
|
37
|
+
// longest ago until the rest fit.
|
|
38
|
+
const MAX_RETAINED_DIRECTORIES = 128;
|
|
39
|
+
function startTimer(callback, ms) {
|
|
40
|
+
const timer = setTimeout(callback, ms);
|
|
41
|
+
timer.unref();
|
|
42
|
+
return timer;
|
|
43
|
+
}
|
|
44
|
+
/** True when a path relative to the project root is, or lies in, a directory that discovery ignores. */
|
|
45
|
+
function isIgnoredPath(relativePath) {
|
|
46
|
+
return isDiscoveryIgnored(normalizePath(relativePath));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* `target` with every link in it resolved. What does not exist yet, such as a cache directory before its first write,
|
|
50
|
+
* keeps its spelling below the nearest ancestor that does. A path that cannot be resolved stays as it is.
|
|
51
|
+
*/
|
|
52
|
+
function resolvePhysicalPath(target) {
|
|
53
|
+
let existing = target;
|
|
54
|
+
for (;;) {
|
|
55
|
+
try {
|
|
56
|
+
return path.join(fs.realpathSync.native(existing), path.relative(existing, target));
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
const parent = path.dirname(existing);
|
|
60
|
+
if (!isMissingPathError(error) || parent === existing)
|
|
61
|
+
return target;
|
|
62
|
+
existing = parent;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Matches the paths relative to `root` that never reach the index: what discovery ignores, and the subtree of a cache
|
|
68
|
+
* directory that was moved into the project (`--cache-dir`, `CODEGRAPH_CACHE_DIR`), which the server writes to itself.
|
|
69
|
+
* A cache directory outside the project, or inside a directory that discovery ignores anyway, adds nothing.
|
|
70
|
+
* Whether the cache directory lies inside the project is decided on physical paths, because either one can be spelled
|
|
71
|
+
* through a link: a root that is a symlink or junction, or a cache directory that names the project by its physical
|
|
72
|
+
* path. The events name their paths relative to the root as it was given, and the match is made on those.
|
|
73
|
+
*/
|
|
74
|
+
function createIgnoredPathMatcher(root, cacheDirectory) {
|
|
75
|
+
if (!cacheDirectory)
|
|
76
|
+
return isIgnoredPath;
|
|
77
|
+
const cacheRelative = path.relative(resolvePhysicalPath(root), resolvePhysicalPath(path.resolve(root, cacheDirectory)));
|
|
78
|
+
if (!isRelativePathInside(cacheRelative) || isIgnoredPath(cacheRelative))
|
|
79
|
+
return isIgnoredPath;
|
|
80
|
+
// The identity key has the file system's case rules and `/` separators, as the events name paths in either form.
|
|
81
|
+
const cacheKey = fileIdentityKey(cacheRelative);
|
|
82
|
+
return (relativePath) => {
|
|
83
|
+
if (isIgnoredPath(relativePath))
|
|
84
|
+
return true;
|
|
85
|
+
const key = fileIdentityKey(relativePath);
|
|
86
|
+
return key === cacheKey || key.startsWith(`${cacheKey}/`);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function capacityErrorCode(error) {
|
|
90
|
+
if (isFileSystemErrorCode(error, "ENOSPC"))
|
|
91
|
+
return "ENOSPC";
|
|
92
|
+
if (isFileSystemErrorCode(error, "EMFILE"))
|
|
93
|
+
return "EMFILE";
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* True when a `rename` event names the watched directory itself: it was removed, moved, or replaced, and its watch
|
|
98
|
+
* may be dead. Neither system reports an `error` or `close` for that.
|
|
99
|
+
* - Windows names the directory by its absolute path (`\\?\C:\...`), over and over until the handle is closed.
|
|
100
|
+
* - Linux (inotify) names it by its own name, once or twice, and then drops the watch without a word.
|
|
101
|
+
* The entries of a watched directory arrive as relative names. The Linux form can also come from a change to the
|
|
102
|
+
* directory's own attributes (`chmod`, `touch`) or from an entry with the same name (`pkg/pkg`). That closes a live
|
|
103
|
+
* handle, which the next refresh opens again.
|
|
104
|
+
*/
|
|
105
|
+
function namesWatchedDirectory(directory, eventType, filename, reportsOwnName) {
|
|
106
|
+
if (eventType !== "rename" || !filename)
|
|
107
|
+
return false;
|
|
108
|
+
// Windows path rules on every host, so that a test on any system can send the Windows form. A relative entry name
|
|
109
|
+
// is never absolute in either flavor.
|
|
110
|
+
if (path.win32.isAbsolute(filename))
|
|
111
|
+
return true;
|
|
112
|
+
return reportsOwnName && filename === path.basename(directory);
|
|
113
|
+
}
|
|
114
|
+
async function isDirectory(directory) {
|
|
115
|
+
try {
|
|
116
|
+
return (await fsp.stat(directory)).isDirectory();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Adds `directory` and its ancestors up to the root to `directories`, stopping at the first one that `directories`
|
|
124
|
+
* already holds or that is ignored. When that would add more than `room` directories, it adds none and returns false.
|
|
125
|
+
*/
|
|
126
|
+
function addDirectoryWithAncestors(root, directories, directory, isIgnored, room = Infinity) {
|
|
127
|
+
const added = [];
|
|
128
|
+
let current = directory;
|
|
129
|
+
// A directory already in the set brought all its ancestors with it.
|
|
130
|
+
while (!directories.has(current)) {
|
|
131
|
+
const relative = path.relative(root, current);
|
|
132
|
+
if (!isRelativePathInside(relative) || isIgnored(relative))
|
|
133
|
+
break;
|
|
134
|
+
added.push(current);
|
|
135
|
+
current = path.dirname(current);
|
|
136
|
+
}
|
|
137
|
+
if (added.length > room)
|
|
138
|
+
return false;
|
|
139
|
+
for (const entry of added)
|
|
140
|
+
directories.add(entry);
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
/** The root, every directory that holds a listed file, and their ancestors up to the root. */
|
|
144
|
+
function collectWatchDirectories(root, files, isIgnored) {
|
|
145
|
+
const directories = new Set([root]);
|
|
146
|
+
for (const file of files) {
|
|
147
|
+
addDirectoryWithAncestors(root, directories, path.dirname(path.resolve(root, file)), isIgnored);
|
|
148
|
+
}
|
|
149
|
+
return directories;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Watches the project for file changes. Windows and macOS use one recursive handle on the root. Other platforms
|
|
153
|
+
* use one non-recursive handle per directory that holds a listed file, because Node walks the whole tree for
|
|
154
|
+
* `recursive: true` there, `node_modules` included, and can exhaust inotify watches.
|
|
155
|
+
* A directory that appears later gets its handle from the event that reports it, not from the next file list. No list
|
|
156
|
+
* names a directory that holds only a placeholder such as `.gitkeep`, yet its first source file must raise an event.
|
|
157
|
+
* The same goes for a directory that a list named for a while: once its last source file is deleted, a placeholder may
|
|
158
|
+
* remain, and a source file added again must raise an event. So a handle that no list names stays, with the handles of
|
|
159
|
+
* the directories above it, until its directory is removed. At most `MAX_RETAINED_DIRECTORIES` of them stay at once,
|
|
160
|
+
* and the refresh closes the ones that were opened longest ago when more qualify. A handle that was lost with its
|
|
161
|
+
* directory is closed (see `namesWatchedDirectory`), and `refreshDirectories` opens it again once the directory exists.
|
|
162
|
+
* The watcher is a hint: a missed event costs speed, never correctness, because the session's pull check stays
|
|
163
|
+
* the authority.
|
|
164
|
+
*/
|
|
165
|
+
export function startProjectWatcher(options) {
|
|
166
|
+
const root = path.resolve(options.root);
|
|
167
|
+
const isIgnored = createIgnoredPathMatcher(root, options.cacheDirectory);
|
|
168
|
+
const debounceMs = options.debounceMs ?? DEFAULT_MCP_WATCH_DEBOUNCE_MS;
|
|
169
|
+
const platform = options.platform ?? process.platform;
|
|
170
|
+
const recursive = platform === "win32" || platform === "darwin";
|
|
171
|
+
const watch = options.watch ?? fs.watch;
|
|
172
|
+
const log = options.log ?? ((message) => console.error(message));
|
|
173
|
+
const handles = new Map();
|
|
174
|
+
// Per-directory mode: the open handles that no file list names, because an event reported their directory, or a list
|
|
175
|
+
// named it until it stopped, or it lies above one of those. They stay while their directory exists, up to the limit.
|
|
176
|
+
const retainedDirectories = new Set();
|
|
177
|
+
// What the latest file list needs a watch for. The root is in every list.
|
|
178
|
+
let listedDirectories = new Set([root]);
|
|
179
|
+
// Reported directories whose watch is still to open, in the order the events arrived.
|
|
180
|
+
const pendingDirectories = new Set();
|
|
181
|
+
let debounceTimer;
|
|
182
|
+
let directoryRefresh;
|
|
183
|
+
let directoryRefreshAgain = false;
|
|
184
|
+
let walkingPending = false;
|
|
185
|
+
// The latest walk through `pendingDirectories`. A reconcile waits for it, to see every watch that events opened.
|
|
186
|
+
let pendingWalk = Promise.resolve();
|
|
187
|
+
let closed = false;
|
|
188
|
+
let disabled = false;
|
|
189
|
+
let failureLogged = false;
|
|
190
|
+
const settle = () => {
|
|
191
|
+
debounceTimer = undefined;
|
|
192
|
+
if (!closed)
|
|
193
|
+
options.onSettled();
|
|
194
|
+
};
|
|
195
|
+
const noteChange = () => {
|
|
196
|
+
options.onChange();
|
|
197
|
+
clearTimeout(debounceTimer);
|
|
198
|
+
debounceTimer = startTimer(settle, debounceMs);
|
|
199
|
+
};
|
|
200
|
+
const closeHandle = (directory, handle) => {
|
|
201
|
+
if (handles.get(directory) === handle) {
|
|
202
|
+
handles.delete(directory);
|
|
203
|
+
retainedDirectories.delete(directory);
|
|
204
|
+
}
|
|
205
|
+
handle.close();
|
|
206
|
+
};
|
|
207
|
+
const closeAll = () => {
|
|
208
|
+
pendingDirectories.clear();
|
|
209
|
+
for (const [directory, handle] of [...handles])
|
|
210
|
+
closeHandle(directory, handle);
|
|
211
|
+
};
|
|
212
|
+
// Out of watches or descriptors: stop watching and leave the session on its pull check.
|
|
213
|
+
const disable = (code) => {
|
|
214
|
+
if (disabled)
|
|
215
|
+
return;
|
|
216
|
+
disabled = true;
|
|
217
|
+
closeAll();
|
|
218
|
+
log(`[codegraph] file watching disabled: ${code}`);
|
|
219
|
+
};
|
|
220
|
+
const logFailureOnce = (error) => {
|
|
221
|
+
if (failureLogged)
|
|
222
|
+
return;
|
|
223
|
+
failureLogged = true;
|
|
224
|
+
log(`[codegraph] file watch failed: ${errorMessage(error)}`);
|
|
225
|
+
};
|
|
226
|
+
const openDirectory = (directory, recursiveWatch) => {
|
|
227
|
+
// Event names are relative to the watched directory; the ignore patterns are relative to the root.
|
|
228
|
+
const prefix = directory === root ? "" : `${path.relative(root, directory)}/`;
|
|
229
|
+
let handle;
|
|
230
|
+
try {
|
|
231
|
+
handle = watch(directory, { recursive: recursiveWatch }, (eventType, filename) => {
|
|
232
|
+
if (closed)
|
|
233
|
+
return;
|
|
234
|
+
// Before the ignore check: this name is the directory's own path, which may pass through an ignored name.
|
|
235
|
+
if (namesWatchedDirectory(directory, eventType, filename, !recursiveWatch)) {
|
|
236
|
+
closeHandle(directory, handle);
|
|
237
|
+
noteChange();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (filename && isIgnored(prefix + filename))
|
|
241
|
+
return;
|
|
242
|
+
noteChange();
|
|
243
|
+
if (!recursive && eventType === "rename" && filename)
|
|
244
|
+
queueDirectory(path.join(directory, filename));
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
// A directory that vanished before its watch opened is seen again, or gone, at the next refresh.
|
|
249
|
+
const code = capacityErrorCode(error);
|
|
250
|
+
if (code)
|
|
251
|
+
disable(code);
|
|
252
|
+
else
|
|
253
|
+
logFailureOnce(error);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
handle.unref();
|
|
257
|
+
handle.on("error", (error) => {
|
|
258
|
+
if (closed)
|
|
259
|
+
return;
|
|
260
|
+
const code = capacityErrorCode(error);
|
|
261
|
+
if (code) {
|
|
262
|
+
disable(code);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
closeHandle(directory, handle);
|
|
266
|
+
logFailureOnce(error);
|
|
267
|
+
}
|
|
268
|
+
// The lost handle may have hidden events, so the error counts as a change.
|
|
269
|
+
noteChange();
|
|
270
|
+
});
|
|
271
|
+
handles.set(directory, handle);
|
|
272
|
+
if (!listedDirectories.has(directory))
|
|
273
|
+
retainedDirectories.add(directory);
|
|
274
|
+
};
|
|
275
|
+
// True when a watch for `directory` would be one more that no file list names, beyond the limit on those.
|
|
276
|
+
const retainedLimitReached = (directory) => !listedDirectories.has(directory) && retainedDirectories.size >= MAX_RETAINED_DIRECTORIES;
|
|
277
|
+
// Watches a directory that an event reported, if it still is one. A `mkdir -p` or a copy may have filled it before
|
|
278
|
+
// this watch opened, which raised no event, so the directories inside it are queued too.
|
|
279
|
+
const watchReportedDirectory = async (directory) => {
|
|
280
|
+
const relative = path.relative(root, directory);
|
|
281
|
+
if (!isRelativePathInside(relative) || isIgnored(relative) || handles.has(directory))
|
|
282
|
+
return;
|
|
283
|
+
if (retainedLimitReached(directory))
|
|
284
|
+
return;
|
|
285
|
+
let stats;
|
|
286
|
+
try {
|
|
287
|
+
stats = await fsp.lstat(directory);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// Gone again: the next refresh sees the final state.
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
// A link is not followed, as in discovery. A refresh may have filled the limit while the directory was checked.
|
|
294
|
+
if (!stats.isDirectory() || closed || disabled || handles.has(directory) || retainedLimitReached(directory))
|
|
295
|
+
return;
|
|
296
|
+
openDirectory(directory, false);
|
|
297
|
+
if (!handles.has(directory))
|
|
298
|
+
return;
|
|
299
|
+
let entries;
|
|
300
|
+
try {
|
|
301
|
+
entries = await fsp.readdir(directory, { withFileTypes: true });
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
for (const entry of entries) {
|
|
307
|
+
if (entry.isDirectory())
|
|
308
|
+
queueDirectory(path.join(directory, entry.name));
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
const watchPendingDirectories = async () => {
|
|
312
|
+
try {
|
|
313
|
+
// The set grows while it is walked: a directory's subdirectories join the queue behind the ones already in it.
|
|
314
|
+
for (const directory of pendingDirectories) {
|
|
315
|
+
pendingDirectories.delete(directory);
|
|
316
|
+
if (closed || disabled)
|
|
317
|
+
return;
|
|
318
|
+
await watchReportedDirectory(directory);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
walkingPending = false;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
const queueDirectory = (directory) => {
|
|
326
|
+
if (closed || disabled || handles.has(directory))
|
|
327
|
+
return;
|
|
328
|
+
pendingDirectories.add(directory);
|
|
329
|
+
if (walkingPending)
|
|
330
|
+
return;
|
|
331
|
+
walkingPending = true;
|
|
332
|
+
pendingWalk = watchPendingDirectories().catch((error) => {
|
|
333
|
+
logFailureOnce(error);
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
const applyDirectories = async (files) => {
|
|
337
|
+
const wanted = collectWatchDirectories(root, files, isIgnored);
|
|
338
|
+
// A watch that the list no longer names stays while its directory exists: a source file that is added there later
|
|
339
|
+
// raises an event on no other watch. No more than MAX_RETAINED_DIRECTORIES of them can stay, the newest first, so
|
|
340
|
+
// the check stops after that many directories that still exist, and the rest are rejected unseen.
|
|
341
|
+
const rejected = new Map();
|
|
342
|
+
let present = 0;
|
|
343
|
+
for (const [directory, handle] of [...handles].reverse()) {
|
|
344
|
+
if (wanted.has(directory))
|
|
345
|
+
continue;
|
|
346
|
+
if (present < MAX_RETAINED_DIRECTORIES && (await isDirectory(directory)))
|
|
347
|
+
present += 1;
|
|
348
|
+
else
|
|
349
|
+
rejected.set(directory, handle);
|
|
350
|
+
if (closed || disabled)
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
// Nothing awaits from here on, so no event opens or closes a watch before this ends. A watch that an event opened
|
|
354
|
+
// while the directories were looked at is not rejected, and competes as the newest.
|
|
355
|
+
// Watched even while no handle is open: what the list needs, and what stays with the directories above it.
|
|
356
|
+
const required = new Set(wanted);
|
|
357
|
+
for (const [directory, handle] of [...handles].reverse()) {
|
|
358
|
+
if (wanted.has(directory) || rejected.get(directory) === handle)
|
|
359
|
+
continue;
|
|
360
|
+
// A directory stays with the ones above it that nothing else keeps, or not at all: all of them count.
|
|
361
|
+
const room = MAX_RETAINED_DIRECTORIES - (required.size - wanted.size);
|
|
362
|
+
addDirectoryWithAncestors(root, required, directory, isIgnored, room);
|
|
363
|
+
}
|
|
364
|
+
listedDirectories = wanted;
|
|
365
|
+
retainedDirectories.clear();
|
|
366
|
+
for (const [directory, handle] of [...handles]) {
|
|
367
|
+
if (!required.has(directory))
|
|
368
|
+
closeHandle(directory, handle);
|
|
369
|
+
else if (!wanted.has(directory))
|
|
370
|
+
retainedDirectories.add(directory);
|
|
371
|
+
}
|
|
372
|
+
for (const directory of required) {
|
|
373
|
+
if (closed || disabled)
|
|
374
|
+
return;
|
|
375
|
+
if (!handles.has(directory))
|
|
376
|
+
openDirectory(directory, false);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const reconcileDirectories = async () => {
|
|
380
|
+
if (recursive) {
|
|
381
|
+
// The root handle is lost only with its own directory. Watch it again once that exists.
|
|
382
|
+
if (handles.has(root) || !(await isDirectory(root)) || closed || disabled)
|
|
383
|
+
return;
|
|
384
|
+
openDirectory(root, true);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const files = await options.listFiles();
|
|
388
|
+
await pendingWalk;
|
|
389
|
+
if (closed || disabled)
|
|
390
|
+
return;
|
|
391
|
+
await applyDirectories(files);
|
|
392
|
+
};
|
|
393
|
+
const refreshDirectories = () => {
|
|
394
|
+
if (closed || disabled)
|
|
395
|
+
return Promise.resolve();
|
|
396
|
+
// One scan at a time. A call that arrives meanwhile shares it and makes it read the files once more.
|
|
397
|
+
if (directoryRefresh) {
|
|
398
|
+
directoryRefreshAgain = true;
|
|
399
|
+
return directoryRefresh;
|
|
400
|
+
}
|
|
401
|
+
directoryRefresh = (async () => {
|
|
402
|
+
try {
|
|
403
|
+
do {
|
|
404
|
+
directoryRefreshAgain = false;
|
|
405
|
+
await reconcileDirectories();
|
|
406
|
+
} while (directoryRefreshAgain && !closed && !disabled);
|
|
407
|
+
}
|
|
408
|
+
finally {
|
|
409
|
+
directoryRefresh = undefined;
|
|
410
|
+
}
|
|
411
|
+
})();
|
|
412
|
+
return directoryRefresh;
|
|
413
|
+
};
|
|
414
|
+
const close = () => {
|
|
415
|
+
if (closed)
|
|
416
|
+
return;
|
|
417
|
+
closed = true;
|
|
418
|
+
clearTimeout(debounceTimer);
|
|
419
|
+
debounceTimer = undefined;
|
|
420
|
+
closeAll();
|
|
421
|
+
};
|
|
422
|
+
if (recursive) {
|
|
423
|
+
openDirectory(root, true);
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
openDirectory(root, false);
|
|
427
|
+
refreshDirectories().catch((error) => {
|
|
428
|
+
logFailureOnce(error);
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
return { refreshDirectories, close };
|
|
432
|
+
}
|
|
433
|
+
/** Throws unless `value` is a whole number of milliseconds that a timer accepts. */
|
|
434
|
+
export function assertMcpWatchMs(name, value) {
|
|
435
|
+
if (value === undefined)
|
|
436
|
+
return;
|
|
437
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > MAX_WATCH_TIMER_MS) {
|
|
438
|
+
throw new RangeError(`${name} must be a whole number of milliseconds from 0 through ${MAX_WATCH_TIMER_MS}.`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Turns file events into background refreshes of an MCP-owned session.
|
|
443
|
+
* - Every event clears the session's cached freshness result at once (`markChanged`). It also cancels the refresh
|
|
444
|
+
* and the rerun that an earlier burst scheduled, so that none starts while events still arrive. A refresh that
|
|
445
|
+
* already waits for its turn of `runExclusive` gives the turn up when it comes: the burst that the event belongs to
|
|
446
|
+
* starts its own refresh when it settles.
|
|
447
|
+
* - A settled burst starts a refresh, no sooner than `minRefreshIntervalMs` after the previous one ended.
|
|
448
|
+
* - One refresh runs at a time. A settled burst during it sets one rerun flag. Its freshness check, invalidation, and
|
|
449
|
+
* rebuild run in one turn of `runExclusive`, so they never overlap an explicit refresh or an artifact build. Every
|
|
450
|
+
* read of the file list for the per-directory watches takes a turn as well.
|
|
451
|
+
* - After `idleMs` without `noteActivity` the watches close, and a refresh that waits for its turn gives it up. The
|
|
452
|
+
* next `noteActivity` reopens them and calls `markChanged`, so the pull check sees every edit made during the pause.
|
|
453
|
+
* - `close` ends the watching for good. The refresh that is running finishes, and so does every read of the file list
|
|
454
|
+
* that has started. `close` returns a promise for them, so that the owner of the session can release the session
|
|
455
|
+
* after them. A read that still waits for its turn gives it up.
|
|
456
|
+
*/
|
|
457
|
+
export function createWatchController(session, options) {
|
|
458
|
+
const minRefreshIntervalMs = options.minRefreshIntervalMs ?? DEFAULT_MCP_WATCH_MIN_REFRESH_INTERVAL_MS;
|
|
459
|
+
const idleMs = options.idleMs ?? DEFAULT_MCP_WATCH_IDLE_MS;
|
|
460
|
+
const log = options.log ?? ((message) => console.error(message));
|
|
461
|
+
let watcher;
|
|
462
|
+
let idleTimer;
|
|
463
|
+
let refreshTimer;
|
|
464
|
+
let lastRefreshEndedAt;
|
|
465
|
+
// The refresh that is running, if any. Only one runs at a time, and `close` waits for it.
|
|
466
|
+
let activeRefresh;
|
|
467
|
+
// The reads of the file list that took their turn of `runExclusive` and have not ended. `close` waits for them too.
|
|
468
|
+
const activeReads = new Set();
|
|
469
|
+
let rerun = false;
|
|
470
|
+
let closed = false;
|
|
471
|
+
// Bumped by every event and by `pause`. A refresh keeps the value it started under, and gives up its turn of
|
|
472
|
+
// `runExclusive` when the value has moved by the time the turn comes: an event reopened the burst, so the refresh
|
|
473
|
+
// that its settling starts is the one that counts, or the watching ended.
|
|
474
|
+
let turnToken = 0;
|
|
475
|
+
// Bumped when a watcher opens and when it pauses, so that a read of the file list that waited for its turn can tell
|
|
476
|
+
// whether the watcher that asked for it is still the open one.
|
|
477
|
+
let watcherToken = 0;
|
|
478
|
+
const scheduleRefresh = () => {
|
|
479
|
+
if (refreshTimer)
|
|
480
|
+
return;
|
|
481
|
+
// From the end, not the start: a refresh that outlasts the interval must not be followed by another at once.
|
|
482
|
+
const waitMs = lastRefreshEndedAt === undefined ? 0 : Math.max(0, lastRefreshEndedAt + minRefreshIntervalMs - Date.now());
|
|
483
|
+
if (!waitMs) {
|
|
484
|
+
startRefresh();
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
refreshTimer = startTimer(() => {
|
|
488
|
+
refreshTimer = undefined;
|
|
489
|
+
startRefresh();
|
|
490
|
+
}, waitMs);
|
|
491
|
+
};
|
|
492
|
+
// Runs in one turn of `runExclusive`, so that no other refresh replaces the session between the check and the rebuild.
|
|
493
|
+
const checkAndRebuild = async () => {
|
|
494
|
+
const freshness = session.checkFreshness
|
|
495
|
+
? await session.checkFreshness()
|
|
496
|
+
: { state: "fresh" };
|
|
497
|
+
// The server closed while the check ran: a rebuild now would keep the project busy after its client left.
|
|
498
|
+
if (closed)
|
|
499
|
+
return;
|
|
500
|
+
// Nobody waits in the background, so the auto-refresh limits do not apply to a stale result. A session that
|
|
501
|
+
// cannot verify its configuration keeps its snapshot instead, as it does without a watcher.
|
|
502
|
+
const rebuildStale = freshness.state === "stale" && freshness.reason !== CONFIGURATION_UNCHECKED_REASON;
|
|
503
|
+
if (rebuildStale)
|
|
504
|
+
session.invalidate();
|
|
505
|
+
if (rebuildStale || freshness.state === "refreshed") {
|
|
506
|
+
await session.loadProject({ symbolGraph: "skip" });
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
// Never rejects: a failure is logged. Resolves to whether the refresh took its turn.
|
|
510
|
+
const refreshSession = async (current, token) => {
|
|
511
|
+
let tookTurn = false;
|
|
512
|
+
try {
|
|
513
|
+
await options.runExclusive(async () => {
|
|
514
|
+
// The turn comes after every refresh and build that held the session, however long those take. Events since
|
|
515
|
+
// this refresh started reopened its burst, and a pause or a close ended the watching, so it is not due now.
|
|
516
|
+
if (token !== turnToken)
|
|
517
|
+
return;
|
|
518
|
+
tookTurn = true;
|
|
519
|
+
await checkAndRebuild();
|
|
520
|
+
});
|
|
521
|
+
// After every refresh, so a directory that a query already rebuilt around still gets its watch.
|
|
522
|
+
if (tookTurn)
|
|
523
|
+
await current?.refreshDirectories();
|
|
524
|
+
}
|
|
525
|
+
catch (error) {
|
|
526
|
+
log(`[codegraph] watch refresh failed: ${errorMessage(error)}`);
|
|
527
|
+
}
|
|
528
|
+
return tookTurn;
|
|
529
|
+
};
|
|
530
|
+
const startRefresh = () => {
|
|
531
|
+
rerun = false;
|
|
532
|
+
const refresh = refreshSession(watcher, turnToken).then((tookTurn) => {
|
|
533
|
+
// A refresh that gave up its turn did nothing, so it does not push the next one back.
|
|
534
|
+
if (tookTurn)
|
|
535
|
+
lastRefreshEndedAt = Date.now();
|
|
536
|
+
if (activeRefresh === refresh)
|
|
537
|
+
activeRefresh = undefined;
|
|
538
|
+
if (rerun && watcher)
|
|
539
|
+
scheduleRefresh();
|
|
540
|
+
});
|
|
541
|
+
activeRefresh = refresh;
|
|
542
|
+
};
|
|
543
|
+
const onSettled = () => {
|
|
544
|
+
if (!watcher)
|
|
545
|
+
return;
|
|
546
|
+
if (activeRefresh) {
|
|
547
|
+
rerun = true;
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
scheduleRefresh();
|
|
551
|
+
};
|
|
552
|
+
// The burst that this event belongs to schedules its own refresh when it settles. One that an earlier burst armed
|
|
553
|
+
// would start in the middle of this one, so it goes now: its timer is cancelled, and a refresh that already waits
|
|
554
|
+
// for its turn gives the turn up.
|
|
555
|
+
const onChange = () => {
|
|
556
|
+
clearTimeout(refreshTimer);
|
|
557
|
+
refreshTimer = undefined;
|
|
558
|
+
rerun = false;
|
|
559
|
+
turnToken += 1;
|
|
560
|
+
session.markChanged?.();
|
|
561
|
+
};
|
|
562
|
+
const pause = () => {
|
|
563
|
+
clearTimeout(idleTimer);
|
|
564
|
+
clearTimeout(refreshTimer);
|
|
565
|
+
idleTimer = undefined;
|
|
566
|
+
refreshTimer = undefined;
|
|
567
|
+
rerun = false;
|
|
568
|
+
turnToken += 1;
|
|
569
|
+
watcherToken += 1;
|
|
570
|
+
watcher?.close();
|
|
571
|
+
watcher = undefined;
|
|
572
|
+
};
|
|
573
|
+
const armIdleTimer = () => {
|
|
574
|
+
clearTimeout(idleTimer);
|
|
575
|
+
idleTimer = undefined;
|
|
576
|
+
if (idleMs)
|
|
577
|
+
idleTimer = startTimer(pause, idleMs);
|
|
578
|
+
};
|
|
579
|
+
// Reads the file list for the directory scans of one watcher, in a turn of `runExclusive`. The first read of a session
|
|
580
|
+
// that nothing has loaded discovers the project and records the file signatures that the freshness check compares
|
|
581
|
+
// with. A read that ran beside `refresh_index` and ended last would replace the signatures of the snapshot that the
|
|
582
|
+
// rebuild produced. One that ended after the owner released the session would record them into the released session,
|
|
583
|
+
// so a read that runs stays in `activeReads` until it ends.
|
|
584
|
+
const readFiles = async (token) => {
|
|
585
|
+
let files = [];
|
|
586
|
+
await options.runExclusive(async () => {
|
|
587
|
+
// The watcher paused or closed while the read waited. Nothing uses the list, and a session that its owner
|
|
588
|
+
// released must not load the project again.
|
|
589
|
+
if (token !== watcherToken)
|
|
590
|
+
return;
|
|
591
|
+
// Tracked in the tick of the check, so that a `close` right after it finds every read that was not skipped.
|
|
592
|
+
const read = (async () => (await session.listFiles?.()) ?? [])();
|
|
593
|
+
activeReads.add(read);
|
|
594
|
+
try {
|
|
595
|
+
files = await read;
|
|
596
|
+
}
|
|
597
|
+
finally {
|
|
598
|
+
activeReads.delete(read);
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
return files;
|
|
602
|
+
};
|
|
603
|
+
const open = () => {
|
|
604
|
+
watcherToken += 1;
|
|
605
|
+
const token = watcherToken;
|
|
606
|
+
watcher = startProjectWatcher({
|
|
607
|
+
root: options.root,
|
|
608
|
+
cacheDirectory: options.cacheDirectory,
|
|
609
|
+
onChange,
|
|
610
|
+
onSettled,
|
|
611
|
+
listFiles: () => readFiles(token),
|
|
612
|
+
debounceMs: options.debounceMs,
|
|
613
|
+
platform: options.platform,
|
|
614
|
+
watch: options.watch,
|
|
615
|
+
log,
|
|
616
|
+
});
|
|
617
|
+
armIdleTimer();
|
|
618
|
+
};
|
|
619
|
+
const noteActivity = () => {
|
|
620
|
+
if (closed)
|
|
621
|
+
return;
|
|
622
|
+
if (watcher) {
|
|
623
|
+
armIdleTimer();
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
// First call after an idle pause or a lazy start: edits made while no watch was open left no event.
|
|
627
|
+
open();
|
|
628
|
+
session.markChanged?.();
|
|
629
|
+
};
|
|
630
|
+
const close = async () => {
|
|
631
|
+
if (!closed) {
|
|
632
|
+
closed = true;
|
|
633
|
+
pause();
|
|
634
|
+
}
|
|
635
|
+
// Neither the refresh nor a read can be cancelled, and either writes to the session until it ends. Their failures
|
|
636
|
+
// are logged where they happened, so none rejects here.
|
|
637
|
+
await Promise.allSettled([activeRefresh, ...activeReads]);
|
|
638
|
+
};
|
|
639
|
+
if (!options.startPaused)
|
|
640
|
+
open();
|
|
641
|
+
return { noteActivity, close };
|
|
642
|
+
}
|
|
643
|
+
//# sourceMappingURL=project-watcher.js.map
|