homegraph 1.2.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -11
- package/dist/bin/homegraph.js +298 -72
- package/dist/bin/homegraph.js.map +1 -1
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +117 -11
- package/dist/context/index.js.map +1 -1
- package/dist/db/index.d.ts +207 -6
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +461 -20
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +19 -1
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/queries.d.ts +157 -3
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +426 -9
- package/dist/db/queries.js.map +1 -1
- package/dist/db/schema.sql +32 -1
- package/dist/db/sqlite-adapter.d.ts +24 -17
- package/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/dist/db/sqlite-adapter.js +292 -36
- package/dist/db/sqlite-adapter.js.map +1 -1
- package/dist/db/wal-valve.d.ts +119 -0
- package/dist/db/wal-valve.d.ts.map +1 -0
- package/dist/db/wal-valve.js +290 -0
- package/dist/db/wal-valve.js.map +1 -0
- package/dist/directory.d.ts +9 -5
- package/dist/directory.d.ts.map +1 -1
- package/dist/directory.js +210 -19
- package/dist/directory.js.map +1 -1
- package/dist/extraction/arkts-batch-worker.js +3 -2
- package/dist/extraction/arkts-batch-worker.js.map +1 -1
- package/dist/extraction/cfml-extractor.d.ts +107 -0
- package/dist/extraction/cfml-extractor.d.ts.map +1 -0
- package/dist/extraction/cfml-extractor.js +494 -0
- package/dist/extraction/cfml-extractor.js.map +1 -0
- package/dist/extraction/default-ignore.d.ts +39 -0
- package/dist/extraction/default-ignore.d.ts.map +1 -0
- package/dist/extraction/default-ignore.js +221 -0
- package/dist/extraction/default-ignore.js.map +1 -0
- package/dist/extraction/grammars.d.ts +25 -1
- package/dist/extraction/grammars.d.ts.map +1 -1
- package/dist/extraction/grammars.js +177 -24
- package/dist/extraction/grammars.js.map +1 -1
- package/dist/extraction/index.d.ts +52 -14
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +521 -314
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/arkts.d.ts +90 -6
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +1579 -128
- package/dist/extraction/languages/arkts.js.map +1 -1
- package/dist/extraction/languages/c-cpp.d.ts +42 -0
- package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
- package/dist/extraction/languages/c-cpp.js +369 -4
- package/dist/extraction/languages/c-cpp.js.map +1 -1
- package/dist/extraction/languages/cfquery.d.ts +12 -0
- package/dist/extraction/languages/cfquery.d.ts.map +1 -0
- package/dist/extraction/languages/cfquery.js +28 -0
- package/dist/extraction/languages/cfquery.js.map +1 -0
- package/dist/extraction/languages/cfscript.d.ts +3 -0
- package/dist/extraction/languages/cfscript.d.ts.map +1 -0
- package/dist/extraction/languages/cfscript.js +73 -0
- package/dist/extraction/languages/cfscript.js.map +1 -0
- package/dist/extraction/languages/cobol.d.ts +33 -0
- package/dist/extraction/languages/cobol.d.ts.map +1 -0
- package/dist/extraction/languages/cobol.js +499 -0
- package/dist/extraction/languages/cobol.js.map +1 -0
- package/dist/extraction/languages/erlang.d.ts +3 -0
- package/dist/extraction/languages/erlang.d.ts.map +1 -0
- package/dist/extraction/languages/erlang.js +350 -0
- package/dist/extraction/languages/erlang.js.map +1 -0
- package/dist/extraction/languages/index.d.ts.map +1 -1
- package/dist/extraction/languages/index.js +16 -0
- package/dist/extraction/languages/index.js.map +1 -1
- package/dist/extraction/languages/nix.d.ts +3 -0
- package/dist/extraction/languages/nix.d.ts.map +1 -0
- package/dist/extraction/languages/nix.js +294 -0
- package/dist/extraction/languages/nix.js.map +1 -0
- package/dist/extraction/languages/solidity.d.ts +3 -0
- package/dist/extraction/languages/solidity.d.ts.map +1 -0
- package/dist/extraction/languages/solidity.js +293 -0
- package/dist/extraction/languages/solidity.js.map +1 -0
- package/dist/extraction/languages/terraform.d.ts +3 -0
- package/dist/extraction/languages/terraform.d.ts.map +1 -0
- package/dist/extraction/languages/terraform.js +641 -0
- package/dist/extraction/languages/terraform.js.map +1 -0
- package/dist/extraction/languages/vbnet.d.ts +11 -0
- package/dist/extraction/languages/vbnet.d.ts.map +1 -0
- package/dist/extraction/languages/vbnet.js +141 -0
- package/dist/extraction/languages/vbnet.js.map +1 -0
- package/dist/extraction/mybatis-extractor.d.ts +30 -10
- package/dist/extraction/mybatis-extractor.d.ts.map +1 -1
- package/dist/extraction/mybatis-extractor.js +140 -38
- package/dist/extraction/mybatis-extractor.js.map +1 -1
- package/dist/extraction/parse-pool.d.ts +27 -1
- package/dist/extraction/parse-pool.d.ts.map +1 -1
- package/dist/extraction/parse-pool.js +75 -7
- package/dist/extraction/parse-pool.js.map +1 -1
- package/dist/extraction/parse-worker.js +9 -2
- package/dist/extraction/parse-worker.js.map +1 -1
- package/dist/extraction/store-worker.d.ts +20 -0
- package/dist/extraction/store-worker.d.ts.map +1 -0
- package/dist/extraction/store-worker.js +102 -0
- package/dist/extraction/store-worker.js.map +1 -0
- package/dist/extraction/store-writer.d.ts +48 -0
- package/dist/extraction/store-writer.d.ts.map +1 -0
- package/dist/extraction/store-writer.js +167 -0
- package/dist/extraction/store-writer.js.map +1 -0
- package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
- package/dist/extraction/tree-sitter-helpers.js +1 -0
- package/dist/extraction/tree-sitter-helpers.js.map +1 -1
- package/dist/extraction/tree-sitter-types.d.ts +3 -1
- package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.d.ts +38 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.js +801 -10
- package/dist/extraction/tree-sitter.js.map +1 -1
- package/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-terraform.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
- package/dist/extraction/wasm-runtime-flags.d.ts +14 -4
- package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -1
- package/dist/extraction/wasm-runtime-flags.js +55 -7
- package/dist/extraction/wasm-runtime-flags.js.map +1 -1
- package/dist/graph/traversal.d.ts.map +1 -1
- package/dist/graph/traversal.js +1 -1
- package/dist/graph/traversal.js.map +1 -1
- package/dist/index.d.ts +74 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +436 -55
- package/dist/index.js.map +1 -1
- package/dist/installer/index.d.ts +42 -0
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +97 -2
- package/dist/installer/index.js.map +1 -1
- package/dist/mcp/daemon.d.ts +25 -3
- package/dist/mcp/daemon.d.ts.map +1 -1
- package/dist/mcp/daemon.js +62 -8
- package/dist/mcp/daemon.js.map +1 -1
- package/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
- package/dist/mcp/dynamic-boundaries.js +2 -1
- package/dist/mcp/dynamic-boundaries.js.map +1 -1
- package/dist/mcp/early-ppid.d.ts +26 -0
- package/dist/mcp/early-ppid.d.ts.map +1 -0
- package/dist/mcp/early-ppid.js +29 -0
- package/dist/mcp/early-ppid.js.map +1 -0
- package/dist/mcp/engine.d.ts +4 -5
- package/dist/mcp/engine.d.ts.map +1 -1
- package/dist/mcp/engine.js +27 -1
- package/dist/mcp/engine.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +41 -6
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/liveness-watchdog.d.ts +18 -1
- package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
- package/dist/mcp/liveness-watchdog.js +73 -13
- package/dist/mcp/liveness-watchdog.js.map +1 -1
- package/dist/mcp/memory-budget.d.ts +30 -0
- package/dist/mcp/memory-budget.d.ts.map +1 -0
- package/dist/mcp/memory-budget.js +123 -0
- package/dist/mcp/memory-budget.js.map +1 -0
- package/dist/mcp/proxy.d.ts.map +1 -1
- package/dist/mcp/proxy.js +88 -4
- package/dist/mcp/proxy.js.map +1 -1
- package/dist/mcp/query-cache.d.ts +35 -4
- package/dist/mcp/query-cache.d.ts.map +1 -1
- package/dist/mcp/query-cache.js +89 -19
- package/dist/mcp/query-cache.js.map +1 -1
- package/dist/mcp/query-pool.d.ts +46 -12
- package/dist/mcp/query-pool.d.ts.map +1 -1
- package/dist/mcp/query-pool.js +100 -23
- package/dist/mcp/query-pool.js.map +1 -1
- package/dist/mcp/query-worker.d.ts +2 -2
- package/dist/mcp/query-worker.js +2 -2
- package/dist/mcp/server-instructions.d.ts +4 -27
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +39 -84
- package/dist/mcp/server-instructions.js.map +1 -1
- package/dist/mcp/session.d.ts +14 -0
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +28 -1
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/startup-handshake.d.ts +44 -0
- package/dist/mcp/startup-handshake.d.ts.map +1 -0
- package/dist/mcp/startup-handshake.js +73 -0
- package/dist/mcp/startup-handshake.js.map +1 -0
- package/dist/mcp/tools.d.ts +104 -4
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +2432 -217
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/transport.d.ts.map +1 -1
- package/dist/mcp/transport.js +18 -0
- package/dist/mcp/transport.js.map +1 -1
- package/dist/project-config.d.ts +38 -0
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +101 -2
- package/dist/project-config.js.map +1 -1
- package/dist/resolution/c-fnptr-synthesizer.d.ts +2 -1
- package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
- package/dist/resolution/c-fnptr-synthesizer.js +178 -146
- package/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
- package/dist/resolution/callback-synthesizer.d.ts +54 -10
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/dist/resolution/callback-synthesizer.js +1299 -257
- package/dist/resolution/callback-synthesizer.js.map +1 -1
- package/dist/resolution/cooperative-yield.d.ts +10 -2
- package/dist/resolution/cooperative-yield.d.ts.map +1 -1
- package/dist/resolution/cooperative-yield.js +6 -4
- package/dist/resolution/cooperative-yield.js.map +1 -1
- package/dist/resolution/frameworks/cics.d.ts +20 -0
- package/dist/resolution/frameworks/cics.d.ts.map +1 -0
- package/dist/resolution/frameworks/cics.js +90 -0
- package/dist/resolution/frameworks/cics.js.map +1 -0
- package/dist/resolution/frameworks/go.d.ts.map +1 -1
- package/dist/resolution/frameworks/go.js +29 -6
- package/dist/resolution/frameworks/go.js.map +1 -1
- package/dist/resolution/frameworks/index.d.ts.map +1 -1
- package/dist/resolution/frameworks/index.js +3 -0
- package/dist/resolution/frameworks/index.js.map +1 -1
- package/dist/resolution/frameworks/java.d.ts.map +1 -1
- package/dist/resolution/frameworks/java.js +14 -6
- package/dist/resolution/frameworks/java.js.map +1 -1
- package/dist/resolution/frameworks/terraform.d.ts +38 -0
- package/dist/resolution/frameworks/terraform.d.ts.map +1 -0
- package/dist/resolution/frameworks/terraform.js +277 -0
- package/dist/resolution/frameworks/terraform.js.map +1 -0
- package/dist/resolution/goframe-synthesizer.d.ts +2 -1
- package/dist/resolution/goframe-synthesizer.d.ts.map +1 -1
- package/dist/resolution/goframe-synthesizer.js +8 -3
- package/dist/resolution/goframe-synthesizer.js.map +1 -1
- package/dist/resolution/import-resolver.d.ts +9 -3
- package/dist/resolution/import-resolver.d.ts.map +1 -1
- package/dist/resolution/import-resolver.js +295 -11
- package/dist/resolution/import-resolver.js.map +1 -1
- package/dist/resolution/index.d.ts +146 -8
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +1089 -131
- package/dist/resolution/index.js.map +1 -1
- package/dist/resolution/memory-budget.d.ts +48 -0
- package/dist/resolution/memory-budget.d.ts.map +1 -0
- package/dist/resolution/memory-budget.js +162 -0
- package/dist/resolution/memory-budget.js.map +1 -0
- package/dist/resolution/name-matcher.d.ts +12 -4
- package/dist/resolution/name-matcher.d.ts.map +1 -1
- package/dist/resolution/name-matcher.js +793 -130
- package/dist/resolution/name-matcher.js.map +1 -1
- package/dist/resolution/resolver-pool.d.ts +106 -0
- package/dist/resolution/resolver-pool.d.ts.map +1 -0
- package/dist/resolution/resolver-pool.js +344 -0
- package/dist/resolution/resolver-pool.js.map +1 -0
- package/dist/resolution/resolver-worker.d.ts +17 -0
- package/dist/resolution/resolver-worker.d.ts.map +1 -0
- package/dist/resolution/resolver-worker.js +141 -0
- package/dist/resolution/resolver-worker.js.map +1 -0
- package/dist/resolution/strip-comments.d.ts +1 -1
- package/dist/resolution/strip-comments.d.ts.map +1 -1
- package/dist/resolution/strip-comments.js +49 -0
- package/dist/resolution/strip-comments.js.map +1 -1
- package/dist/resolution/types.d.ts +32 -0
- package/dist/resolution/types.d.ts.map +1 -1
- package/dist/resolution/workspace-packages.d.ts +10 -0
- package/dist/resolution/workspace-packages.d.ts.map +1 -1
- package/dist/resolution/workspace-packages.js +142 -4
- package/dist/resolution/workspace-packages.js.map +1 -1
- package/dist/search/identifier-segments.d.ts +60 -0
- package/dist/search/identifier-segments.d.ts.map +1 -0
- package/dist/search/identifier-segments.js +176 -0
- package/dist/search/identifier-segments.js.map +1 -0
- package/dist/search/query-utils.d.ts +201 -0
- package/dist/search/query-utils.d.ts.map +1 -1
- package/dist/search/query-utils.js +919 -2
- package/dist/search/query-utils.js.map +1 -1
- package/dist/spec/build/diff-parser.d.ts +55 -0
- package/dist/spec/build/diff-parser.d.ts.map +1 -0
- package/dist/spec/build/diff-parser.js +205 -0
- package/dist/spec/build/diff-parser.js.map +1 -0
- package/dist/spec/build/git-scanner.d.ts +93 -0
- package/dist/spec/build/git-scanner.d.ts.map +1 -0
- package/dist/spec/build/git-scanner.js +254 -0
- package/dist/spec/build/git-scanner.js.map +1 -0
- package/dist/spec/build/pipeline.d.ts +54 -0
- package/dist/spec/build/pipeline.d.ts.map +1 -0
- package/dist/spec/build/pipeline.js +170 -0
- package/dist/spec/build/pipeline.js.map +1 -0
- package/dist/spec/build/scan.d.ts +48 -0
- package/dist/spec/build/scan.d.ts.map +1 -0
- package/dist/spec/build/scan.js +89 -0
- package/dist/spec/build/scan.js.map +1 -0
- package/dist/spec/build/scope-resolver.d.ts +50 -0
- package/dist/spec/build/scope-resolver.d.ts.map +1 -0
- package/dist/spec/build/scope-resolver.js +105 -0
- package/dist/spec/build/scope-resolver.js.map +1 -0
- package/dist/spec/build/spec-extractor.d.ts +74 -0
- package/dist/spec/build/spec-extractor.d.ts.map +1 -0
- package/dist/spec/build/spec-extractor.js +344 -0
- package/dist/spec/build/spec-extractor.js.map +1 -0
- package/dist/spec/config.d.ts +35 -1
- package/dist/spec/config.d.ts.map +1 -1
- package/dist/spec/config.js +30 -5
- package/dist/spec/config.js.map +1 -1
- package/dist/spec/db/fts.d.ts.map +1 -1
- package/dist/spec/db/fts.js +19 -50
- package/dist/spec/db/fts.js.map +1 -1
- package/dist/spec/db/index.d.ts +5 -3
- package/dist/spec/db/index.d.ts.map +1 -1
- package/dist/spec/db/index.js +16 -1
- package/dist/spec/db/index.js.map +1 -1
- package/dist/spec/db/persist.d.ts +45 -0
- package/dist/spec/db/persist.d.ts.map +1 -0
- package/dist/spec/db/persist.js +66 -0
- package/dist/spec/db/persist.js.map +1 -0
- package/dist/spec/db/relations.d.ts +80 -1
- package/dist/spec/db/relations.d.ts.map +1 -1
- package/dist/spec/db/relations.js +175 -4
- package/dist/spec/db/relations.js.map +1 -1
- package/dist/spec/db/schema.d.ts +4 -0
- package/dist/spec/db/schema.d.ts.map +1 -1
- package/dist/spec/db/schema.js +4 -0
- package/dist/spec/db/schema.js.map +1 -1
- package/dist/spec/db/schema.sql +5 -1
- package/dist/spec/db/spec-node.d.ts +5 -0
- package/dist/spec/db/spec-node.d.ts.map +1 -1
- package/dist/spec/db/spec-node.js +11 -5
- package/dist/spec/db/spec-node.js.map +1 -1
- package/dist/spec/db/sql-utils.d.ts +11 -0
- package/dist/spec/db/sql-utils.d.ts.map +1 -0
- package/dist/spec/db/sql-utils.js +19 -0
- package/dist/spec/db/sql-utils.js.map +1 -0
- package/dist/spec/evolve/cluster-context.d.ts +30 -0
- package/dist/spec/evolve/cluster-context.d.ts.map +1 -0
- package/dist/spec/evolve/cluster-context.js +78 -0
- package/dist/spec/evolve/cluster-context.js.map +1 -0
- package/dist/spec/evolve/commit-spec-analyzer.d.ts +51 -0
- package/dist/spec/evolve/commit-spec-analyzer.d.ts.map +1 -0
- package/dist/spec/evolve/commit-spec-analyzer.js +88 -0
- package/dist/spec/evolve/commit-spec-analyzer.js.map +1 -0
- package/dist/spec/evolve/commit-spec-persister.d.ts +53 -0
- package/dist/spec/evolve/commit-spec-persister.d.ts.map +1 -0
- package/dist/spec/evolve/commit-spec-persister.js +93 -0
- package/dist/spec/evolve/commit-spec-persister.js.map +1 -0
- package/dist/spec/evolve/impact-locator.d.ts +23 -7
- package/dist/spec/evolve/impact-locator.d.ts.map +1 -1
- package/dist/spec/evolve/impact-locator.js +58 -14
- package/dist/spec/evolve/impact-locator.js.map +1 -1
- package/dist/spec/evolve/pipeline.d.ts +62 -25
- package/dist/spec/evolve/pipeline.d.ts.map +1 -1
- package/dist/spec/evolve/pipeline.js +436 -457
- package/dist/spec/evolve/pipeline.js.map +1 -1
- package/dist/spec/evolve/spec-rewriter.d.ts +13 -13
- package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -1
- package/dist/spec/evolve/spec-rewriter.js +45 -45
- package/dist/spec/evolve/spec-rewriter.js.map +1 -1
- package/dist/spec/git/commits.d.ts +85 -0
- package/dist/spec/git/commits.d.ts.map +1 -0
- package/dist/spec/git/commits.js +218 -0
- package/dist/spec/git/commits.js.map +1 -0
- package/dist/spec/git/exec.d.ts +13 -0
- package/dist/spec/git/exec.d.ts.map +1 -0
- package/dist/spec/git/exec.js +19 -0
- package/dist/spec/git/exec.js.map +1 -0
- package/dist/spec/git/index.d.ts +9 -0
- package/dist/spec/git/index.d.ts.map +1 -0
- package/dist/spec/git/index.js +22 -0
- package/dist/spec/git/index.js.map +1 -0
- package/dist/spec/git-utils.d.ts +8 -0
- package/dist/spec/git-utils.d.ts.map +1 -0
- package/dist/spec/git-utils.js +14 -0
- package/dist/spec/git-utils.js.map +1 -0
- package/dist/spec/graph/index.d.ts +8 -0
- package/dist/spec/graph/index.d.ts.map +1 -0
- package/dist/spec/graph/index.js +17 -0
- package/dist/spec/graph/index.js.map +1 -0
- package/dist/spec/graph/queries.d.ts +6 -35
- package/dist/spec/graph/queries.d.ts.map +1 -1
- package/dist/spec/graph/queries.js +71 -248
- package/dist/spec/graph/queries.js.map +1 -1
- package/dist/spec/llm/agent-client.d.ts +59 -0
- package/dist/spec/llm/agent-client.d.ts.map +1 -0
- package/dist/spec/llm/agent-client.js +213 -0
- package/dist/spec/llm/agent-client.js.map +1 -0
- package/dist/spec/llm/agents/claude-code.d.ts +32 -0
- package/dist/spec/llm/agents/claude-code.d.ts.map +1 -0
- package/dist/spec/llm/agents/claude-code.js +125 -0
- package/dist/spec/llm/agents/claude-code.js.map +1 -0
- package/dist/spec/llm/agents/codex.d.ts +32 -0
- package/dist/spec/llm/agents/codex.d.ts.map +1 -0
- package/dist/spec/llm/agents/codex.js +145 -0
- package/dist/spec/llm/agents/codex.js.map +1 -0
- package/dist/spec/llm/agents/detect-utils.d.ts +28 -0
- package/dist/spec/llm/agents/detect-utils.d.ts.map +1 -0
- package/dist/spec/llm/agents/detect-utils.js +91 -0
- package/dist/spec/llm/agents/detect-utils.js.map +1 -0
- package/dist/spec/llm/agents/deveco-code.d.ts +33 -0
- package/dist/spec/llm/agents/deveco-code.d.ts.map +1 -0
- package/dist/spec/llm/agents/deveco-code.js +165 -0
- package/dist/spec/llm/agents/deveco-code.js.map +1 -0
- package/dist/spec/llm/agents/index.d.ts +31 -0
- package/dist/spec/llm/agents/index.d.ts.map +1 -0
- package/dist/spec/llm/agents/index.js +65 -0
- package/dist/spec/llm/agents/index.js.map +1 -0
- package/dist/spec/llm/agents/types.d.ts +76 -0
- package/dist/spec/llm/agents/types.d.ts.map +1 -0
- package/dist/spec/llm/agents/types.js +15 -0
- package/dist/spec/llm/agents/types.js.map +1 -0
- package/dist/spec/llm/client.d.ts +31 -11
- package/dist/spec/llm/client.d.ts.map +1 -1
- package/dist/spec/llm/client.js +143 -81
- package/dist/spec/llm/client.js.map +1 -1
- package/dist/spec/llm/factory.d.ts +41 -0
- package/dist/spec/llm/factory.d.ts.map +1 -0
- package/dist/spec/llm/factory.js +80 -0
- package/dist/spec/llm/factory.js.map +1 -0
- package/dist/spec/llm/prompts.d.ts +24 -8
- package/dist/spec/llm/prompts.d.ts.map +1 -1
- package/dist/spec/llm/prompts.js +100 -50
- package/dist/spec/llm/prompts.js.map +1 -1
- package/dist/spec/llm/retry.d.ts +53 -0
- package/dist/spec/llm/retry.d.ts.map +1 -0
- package/dist/spec/llm/retry.js +153 -0
- package/dist/spec/llm/retry.js.map +1 -0
- package/dist/spec/mine/clusterer.d.ts +63 -0
- package/dist/spec/mine/clusterer.d.ts.map +1 -0
- package/dist/spec/mine/clusterer.js +904 -0
- package/dist/spec/mine/clusterer.js.map +1 -0
- package/dist/spec/mine/clustering/features.d.ts +38 -0
- package/dist/spec/mine/clustering/features.d.ts.map +1 -0
- package/dist/spec/mine/clustering/features.js +108 -0
- package/dist/spec/mine/clustering/features.js.map +1 -0
- package/dist/spec/mine/clustering/index.d.ts +62 -0
- package/dist/spec/mine/clustering/index.d.ts.map +1 -0
- package/dist/spec/mine/clustering/index.js +259 -0
- package/dist/spec/mine/clustering/index.js.map +1 -0
- package/dist/spec/mine/clustering/leiden.d.ts +61 -0
- package/dist/spec/mine/clustering/leiden.d.ts.map +1 -0
- package/dist/spec/mine/clustering/leiden.js +489 -0
- package/dist/spec/mine/clustering/leiden.js.map +1 -0
- package/dist/spec/mine/clustering/text-similarity.d.ts +22 -0
- package/dist/spec/mine/clustering/text-similarity.d.ts.map +1 -0
- package/dist/spec/mine/clustering/text-similarity.js +98 -0
- package/dist/spec/mine/clustering/text-similarity.js.map +1 -0
- package/dist/spec/mine/generator.d.ts +54 -0
- package/dist/spec/mine/generator.d.ts.map +1 -0
- package/dist/spec/mine/generator.js +267 -0
- package/dist/spec/mine/generator.js.map +1 -0
- package/dist/spec/mine/persist.d.ts +37 -0
- package/dist/spec/mine/persist.d.ts.map +1 -0
- package/dist/spec/mine/persist.js +170 -0
- package/dist/spec/mine/persist.js.map +1 -0
- package/dist/spec/mine/pipeline.d.ts +41 -0
- package/dist/spec/mine/pipeline.d.ts.map +1 -0
- package/dist/spec/mine/pipeline.js +236 -0
- package/dist/spec/mine/pipeline.js.map +1 -0
- package/dist/spec/mine/progress-handler.d.ts +22 -0
- package/dist/spec/mine/progress-handler.d.ts.map +1 -0
- package/dist/spec/mine/progress-handler.js +108 -0
- package/dist/spec/mine/progress-handler.js.map +1 -0
- package/dist/spec/mine/progress.d.ts +23 -0
- package/dist/spec/mine/progress.d.ts.map +1 -0
- package/dist/spec/mine/progress.js +12 -0
- package/dist/spec/mine/progress.js.map +1 -0
- package/dist/spec/mine/scanner.d.ts +59 -0
- package/dist/spec/mine/scanner.d.ts.map +1 -0
- package/dist/spec/mine/scanner.js +383 -0
- package/dist/spec/mine/scanner.js.map +1 -0
- package/dist/spec/types.d.ts +32 -0
- package/dist/spec/types.d.ts.map +1 -1
- package/dist/spec/ui/index.d.ts +9 -0
- package/dist/spec/ui/index.d.ts.map +1 -0
- package/dist/spec/ui/index.js +16 -0
- package/dist/spec/ui/index.js.map +1 -0
- package/dist/spec/ui/progress-handler.d.ts +33 -0
- package/dist/spec/ui/progress-handler.d.ts.map +1 -0
- package/dist/spec/ui/progress-handler.js +132 -0
- package/dist/spec/ui/progress-handler.js.map +1 -0
- package/dist/spec/ui/progress.d.ts +24 -0
- package/dist/spec/ui/progress.d.ts.map +1 -0
- package/dist/spec/ui/progress.js +13 -0
- package/dist/spec/ui/progress.js.map +1 -0
- package/dist/spec/utils/fs.d.ts +45 -0
- package/dist/spec/utils/fs.d.ts.map +1 -0
- package/dist/spec/utils/fs.js +163 -0
- package/dist/spec/utils/fs.js.map +1 -0
- package/dist/spec/utils/index.d.ts +12 -0
- package/dist/spec/utils/index.d.ts.map +1 -0
- package/dist/spec/utils/index.js +26 -0
- package/dist/spec/utils/index.js.map +1 -0
- package/dist/spec/utils/meta.d.ts +32 -0
- package/dist/spec/utils/meta.d.ts.map +1 -0
- package/dist/spec/utils/meta.js +122 -0
- package/dist/spec/utils/meta.js.map +1 -0
- package/dist/spec/utils/truncate.d.ts +86 -0
- package/dist/spec/utils/truncate.d.ts.map +1 -0
- package/dist/spec/utils/truncate.js +172 -0
- package/dist/spec/utils/truncate.js.map +1 -0
- package/dist/spec/utils.d.ts +4 -16
- package/dist/spec/utils.d.ts.map +1 -1
- package/dist/spec/utils.js +6 -58
- package/dist/spec/utils.js.map +1 -1
- package/dist/sync/git-hooks.d.ts.map +1 -1
- package/dist/sync/git-hooks.js +2 -0
- package/dist/sync/git-hooks.js.map +1 -1
- package/dist/sync/watcher.d.ts +35 -6
- package/dist/sync/watcher.d.ts.map +1 -1
- package/dist/sync/watcher.js +127 -17
- package/dist/sync/watcher.js.map +1 -1
- package/dist/sync/worktree.d.ts.map +1 -1
- package/dist/sync/worktree.js +5 -0
- package/dist/sync/worktree.js.map +1 -1
- package/dist/telemetry/index.js +3 -3
- package/dist/telemetry/index.js.map +1 -1
- package/dist/types.d.ts +22 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -1
- package/dist/upgrade/index.d.ts +32 -0
- package/dist/upgrade/index.d.ts.map +1 -1
- package/dist/upgrade/index.js +162 -14
- package/dist/upgrade/index.js.map +1 -1
- package/dist/upgrade/remove-binary.d.ts +87 -0
- package/dist/upgrade/remove-binary.d.ts.map +1 -0
- package/dist/upgrade/remove-binary.js +289 -0
- package/dist/upgrade/remove-binary.js.map +1 -0
- package/dist/upgrade/update-check.d.ts +92 -0
- package/dist/upgrade/update-check.d.ts.map +1 -0
- package/dist/upgrade/update-check.js +258 -0
- package/dist/upgrade/update-check.js.map +1 -0
- package/package.json +6 -2
- package/scripts/agent-eval/run-all.sh +6 -0
- package/scripts/build-bundle.sh +5 -0
- package/scripts/npm-shim.js +8 -1
- package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/deveco_arm.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/setup.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/data/agents.json +0 -109
- package/scripts/exp_boundary_eval/data/experiments.json +0 -140
- package/scripts/qa_eval/README.md +0 -407
- package/scripts/qa_eval/_test_deveco_probe.py +0 -41
- package/scripts/qa_eval/agent_runner.py +0 -526
- package/scripts/qa_eval/data/.gitignore +0 -4
- package/scripts/qa_eval/data/test-set.jsonl +0 -2
- package/scripts/qa_eval/eval_metrics.py +0 -274
- package/scripts/qa_eval/external_agent.py +0 -976
- package/scripts/qa_eval/llm_config.py +0 -92
- package/scripts/qa_eval/memory_monitor.py +0 -132
- package/scripts/qa_eval/my_answer_accuracy.py +0 -187
- package/scripts/qa_eval/requirements.txt +0 -2
- package/scripts/qa_eval/run_pipeline.py +0 -804
- package/scripts/qa_eval/stats_efficiency.py +0 -279
- package/scripts/qa_eval/stats_scores.py +0 -207
package/dist/extraction/index.js
CHANGED
|
@@ -41,11 +41,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
41
41
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
42
|
};
|
|
43
43
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
-
exports.loadAllGrammars = exports.loadGrammarsForLanguages = exports.initGrammars = exports.getSupportedLanguages = exports.isGrammarLoaded = exports.isLanguageSupported = exports.isSourceFile = exports.detectLanguage = exports.extractFromSource = exports.ExtractionOrchestrator = exports.ScopeIgnore = void 0;
|
|
44
|
+
exports.loadAllGrammars = exports.loadGrammarsForLanguages = exports.initGrammars = exports.getSupportedLanguages = exports.isGrammarLoaded = exports.isLanguageSupported = exports.isSourceFile = exports.detectLanguage = exports.extractFromSource = exports.ExtractionOrchestrator = exports.ScopeIgnore = exports.buildDefaultIgnore = void 0;
|
|
45
45
|
exports.hashContent = hashContent;
|
|
46
|
-
exports.buildDefaultIgnore = buildDefaultIgnore;
|
|
47
46
|
exports.buildScopeIgnore = buildScopeIgnore;
|
|
48
47
|
exports.discoverEmbeddedRepoRoots = discoverEmbeddedRepoRoots;
|
|
48
|
+
exports.findUnindexedIgnoredRepos = findUnindexedIgnoredRepos;
|
|
49
49
|
exports.scanDirectory = scanDirectory;
|
|
50
50
|
exports.scanDirectoryAsync = scanDirectoryAsync;
|
|
51
51
|
const fs = __importStar(require("fs"));
|
|
@@ -56,12 +56,17 @@ const crypto = __importStar(require("crypto"));
|
|
|
56
56
|
const child_process_1 = require("child_process");
|
|
57
57
|
const tree_sitter_1 = require("./tree-sitter");
|
|
58
58
|
const parse_pool_1 = require("./parse-pool");
|
|
59
|
+
const store_writer_1 = require("./store-writer");
|
|
60
|
+
const cooperative_yield_1 = require("../resolution/cooperative-yield");
|
|
59
61
|
const grammars_1 = require("./grammars");
|
|
60
62
|
const project_config_1 = require("../project-config");
|
|
61
63
|
const directory_1 = require("../directory");
|
|
62
64
|
const errors_1 = require("../errors");
|
|
63
65
|
const utils_1 = require("../utils");
|
|
64
66
|
const ignore_1 = __importDefault(require("ignore"));
|
|
67
|
+
const default_ignore_1 = require("./default-ignore");
|
|
68
|
+
var default_ignore_2 = require("./default-ignore");
|
|
69
|
+
Object.defineProperty(exports, "buildDefaultIgnore", { enumerable: true, get: function () { return default_ignore_2.buildDefaultIgnore; } });
|
|
65
70
|
const frameworks_1 = require("../resolution/frameworks");
|
|
66
71
|
const context_1 = require("./context");
|
|
67
72
|
const arkts_1 = require("./languages/arkts");
|
|
@@ -108,176 +113,6 @@ function hashContent(content) {
|
|
|
108
113
|
* symbols. 1 MB covers essentially all hand-written source.
|
|
109
114
|
*/
|
|
110
115
|
const MAX_FILE_SIZE = 1024 * 1024;
|
|
111
|
-
/**
|
|
112
|
-
* Directory names that are dependency, build, cache, or tooling output across the
|
|
113
|
-
* languages/frameworks HomeGraph supports — curated from the canonical
|
|
114
|
-
* github/gitignore templates. Excluded by default so the graph reflects your code,
|
|
115
|
-
* not third-party noise, without requiring a `.gitignore` (issue #407). The
|
|
116
|
-
* exclusion applies uniformly (git or not, tracked or not); the only opt-in is an
|
|
117
|
-
* explicit `.gitignore` negation (e.g. `!vendor/`). First-party-prone or generic
|
|
118
|
-
* names (`packages`, `lib`, `app`, `bin`, `src`, `deps`, `env`, `tmp`, `storage`,
|
|
119
|
-
* `Library`) are deliberately NOT listed, to avoid ever hiding real source.
|
|
120
|
-
*
|
|
121
|
-
* Only dirs that actually contain *indexable source* (or are enormous) earn a slot
|
|
122
|
-
* — IDE/state dirs like `.idea`/`.vs` are omitted because HomeGraph indexes only
|
|
123
|
-
* recognized source extensions, so they produce no symbols regardless.
|
|
124
|
-
*/
|
|
125
|
-
const DEFAULT_IGNORE_DIRS = new Set([
|
|
126
|
-
// JS / TS — dependency directories
|
|
127
|
-
'node_modules', 'bower_components', 'jspm_packages', 'web_modules',
|
|
128
|
-
'.yarn', '.pnpm-store',
|
|
129
|
-
// JS / TS — framework & bundler build / cache / deploy output
|
|
130
|
-
'.next', '.nuxt', '.svelte-kit', '.turbo', '.vite', '.parcel-cache', '.angular',
|
|
131
|
-
'.docusaurus', 'storybook-static', '.vinxi', '.nitro', 'out-tsc',
|
|
132
|
-
'.vercel', '.netlify', '.wrangler',
|
|
133
|
-
// Build output (common across ecosystems)
|
|
134
|
-
'dist', 'build', 'out', '.output',
|
|
135
|
-
// Test / coverage
|
|
136
|
-
'coverage', '.nyc_output',
|
|
137
|
-
// Python
|
|
138
|
-
'__pycache__', '__pypackages__', '.venv', 'venv', '.pixi', '.pdm-build',
|
|
139
|
-
'.mypy_cache', '.pytest_cache', '.ruff_cache', '.tox', '.nox', '.hypothesis',
|
|
140
|
-
'.ipynb_checkpoints', '.eggs',
|
|
141
|
-
// Rust / JVM (Maven, Gradle, Scala)
|
|
142
|
-
'target', '.gradle',
|
|
143
|
-
// .NET
|
|
144
|
-
'obj',
|
|
145
|
-
// Vendored deps (Go, PHP/Composer, Ruby/Bundler)
|
|
146
|
-
'vendor',
|
|
147
|
-
// Swift / iOS
|
|
148
|
-
'.build', 'Pods', 'Carthage', 'DerivedData', '.swiftpm',
|
|
149
|
-
// Dart / Flutter
|
|
150
|
-
'.dart_tool', '.pub-cache',
|
|
151
|
-
// Native (Android NDK, C/C++ deps)
|
|
152
|
-
'.cxx', '.externalNativeBuild', 'vcpkg_installed',
|
|
153
|
-
// Scala tooling
|
|
154
|
-
'.bloop', '.metals',
|
|
155
|
-
// Lua / Luau (LuaRocks)
|
|
156
|
-
'lua_modules', '.luarocks',
|
|
157
|
-
// Delphi / RAD Studio IDE backups (duplicate .pas source — would double-count)
|
|
158
|
-
'__history', '__recovery',
|
|
159
|
-
// Generic cache
|
|
160
|
-
'.cache',
|
|
161
|
-
]);
|
|
162
|
-
/**
|
|
163
|
-
* Android resource directory types. A `res/` tree holds ONLY non-code resources —
|
|
164
|
-
* layouts, drawables, value bags (strings/colors/styles), menus, navigation
|
|
165
|
-
* graphs — split into one typed subdirectory per kind, optionally density/locale/
|
|
166
|
-
* version-qualified (`values-es`, `drawable-hdpi`, `layout-v21`, …). None of it
|
|
167
|
-
* yields an extractable code symbol, yet on an Android app it DOMINATES the tree
|
|
168
|
-
* (one report: 26k XML files = 97% of the project, 0 symbols), bloating the DB,
|
|
169
|
-
* slowing indexing, and skewing both the file count and `codegraph_explore`
|
|
170
|
-
* results (#1047). So these are excluded by default. The structure is
|
|
171
|
-
* self-identifying — a non-Android project has no `res/layout/` etc., so it's
|
|
172
|
-
* untouched — and the only XML that DOES produce symbols (MyBatis mappers) lives
|
|
173
|
-
* under `src/main/resources/`, never `res/`, so nothing useful is dropped.
|
|
174
|
-
* `res/raw/` is deliberately NOT here: it holds arbitrary bundled assets that can
|
|
175
|
-
* be code-ish (a `.sql` schema, a `.js`), so we leave it indexed. Override any of
|
|
176
|
-
* these with a `.gitignore` negation (e.g. `!res/values/`).
|
|
177
|
-
*/
|
|
178
|
-
const ANDROID_RES_TYPES = [
|
|
179
|
-
'anim', 'animator', 'color', 'drawable', 'font', 'layout',
|
|
180
|
-
'menu', 'mipmap', 'navigation', 'transition', 'values', 'xml',
|
|
181
|
-
];
|
|
182
|
-
/** Gitignore-style patterns for the `ignore` matcher: the dirs above plus a few globs. */
|
|
183
|
-
const DEFAULT_IGNORE_PATTERNS = [
|
|
184
|
-
...Array.from(DEFAULT_IGNORE_DIRS, (d) => `${d}/`),
|
|
185
|
-
'*.egg-info/', // Python packaging metadata
|
|
186
|
-
'cmake-build-*/', // CLion / CMake build trees
|
|
187
|
-
'bazel-*/', // Bazel output symlink trees
|
|
188
|
-
// Android resource dirs at any depth, with their qualifier variants (#1047).
|
|
189
|
-
...ANDROID_RES_TYPES.map((t) => `**/res/${t}*/`),
|
|
190
|
-
];
|
|
191
|
-
/** True if `buf` decodes as strict UTF-8 (no invalid byte sequences). */
|
|
192
|
-
function isValidUtf8(buf) {
|
|
193
|
-
try {
|
|
194
|
-
new TextDecoder('utf-8', { fatal: true }).decode(buf);
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Read a `.gitignore` and return patterns safe to hand to the `ignore` matcher —
|
|
203
|
-
* never throwing, even when the file isn't real gitignore text. Two failure
|
|
204
|
-
* modes, both seen in the wild (issue #682):
|
|
205
|
-
*
|
|
206
|
-
* - The file isn't valid UTF-8 — e.g. transparently encrypted in place by
|
|
207
|
-
* corporate DLP / endpoint-security software, leaving a UTF-16 header plus
|
|
208
|
-
* ciphertext. None of it is meaningful patterns, so the whole file is skipped.
|
|
209
|
-
* - The file is text but a single line can't be compiled to a regex by the
|
|
210
|
-
* `ignore` library — `\\[` and friends throw "Unterminated character class".
|
|
211
|
-
* Crucially the throw is LAZY (at match time, not `.add()`), so it would
|
|
212
|
-
* otherwise escape mid-scan. That one pattern is dropped; the rest are kept.
|
|
213
|
-
*
|
|
214
|
-
* Either way a warning that NAMES the file is logged (the reporter couldn't tell
|
|
215
|
-
* which `.gitignore` was at fault) and indexing continues instead of aborting.
|
|
216
|
-
* Returns '' when there's nothing usable.
|
|
217
|
-
*/
|
|
218
|
-
function readGitignorePatterns(giPath) {
|
|
219
|
-
let buf;
|
|
220
|
-
try {
|
|
221
|
-
buf = fs.readFileSync(giPath);
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
return ''; // unreadable (permissions / race) — treat as absent
|
|
225
|
-
}
|
|
226
|
-
// A NUL byte never appears in real gitignore text, and a fatal UTF-8 decode
|
|
227
|
-
// catches the rest. Such a file isn't ignore patterns at all.
|
|
228
|
-
if (buf.includes(0) || !isValidUtf8(buf)) {
|
|
229
|
-
(0, errors_1.logWarn)('Ignoring a .gitignore that is not valid UTF-8 text — it may have been encrypted ' +
|
|
230
|
-
'in place by endpoint-security software. Indexing continues without it.', { file: giPath });
|
|
231
|
-
return '';
|
|
232
|
-
}
|
|
233
|
-
const content = buf.toString('utf-8');
|
|
234
|
-
// Fast path: one `.ignores()` call forces the library to compile EVERY rule,
|
|
235
|
-
// so if it doesn't throw, the whole file is safe to use verbatim.
|
|
236
|
-
try {
|
|
237
|
-
(0, ignore_1.default)().add(content).ignores('.homegraph-probe');
|
|
238
|
-
return content;
|
|
239
|
-
}
|
|
240
|
-
catch {
|
|
241
|
-
// Fall through: a line is uncompilable — keep the good ones, drop the bad.
|
|
242
|
-
}
|
|
243
|
-
const kept = [];
|
|
244
|
-
let dropped = 0;
|
|
245
|
-
for (const line of content.split(/\r?\n/)) {
|
|
246
|
-
try {
|
|
247
|
-
(0, ignore_1.default)().add(line).ignores('.homegraph-probe');
|
|
248
|
-
kept.push(line);
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
dropped++;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (dropped > 0) {
|
|
255
|
-
(0, errors_1.logWarn)(`Skipped ${dropped} unparseable pattern(s) in a .gitignore; the rest are applied.`, { file: giPath });
|
|
256
|
-
}
|
|
257
|
-
return kept.join('\n');
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* An `ignore` matcher seeded with the built-in defaults, merged with the project's
|
|
261
|
-
* root .gitignore so a negation there (e.g. `!vendor/`) overrides a default. Shared
|
|
262
|
-
* by both enumeration paths so behavior is identical with or without git — and so
|
|
263
|
-
* the defaults apply to tracked files too (committing a dependency dir doesn't make
|
|
264
|
-
* it project code; the explicit `.gitignore` negation is the only opt-in).
|
|
265
|
-
*/
|
|
266
|
-
function buildDefaultIgnore(rootDir) {
|
|
267
|
-
const ig = (0, ignore_1.default)().add(DEFAULT_IGNORE_PATTERNS);
|
|
268
|
-
const rootGitignore = path.join(rootDir, '.gitignore');
|
|
269
|
-
if (fs.existsSync(rootGitignore))
|
|
270
|
-
ig.add(readGitignorePatterns(rootGitignore));
|
|
271
|
-
return ig;
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* Defaults-only ignore matcher (no root `.gitignore` merged). Used wherever the
|
|
275
|
-
* parent repo's own ignore rules must NOT apply — inside embedded child repos,
|
|
276
|
-
* whose gitignore semantics their own `git ls-files` already enforced (#514).
|
|
277
|
-
*/
|
|
278
|
-
function defaultsOnlyIgnore() {
|
|
279
|
-
return (0, ignore_1.default)().add(DEFAULT_IGNORE_PATTERNS);
|
|
280
|
-
}
|
|
281
116
|
/**
|
|
282
117
|
* Matcher for the project's `homegraph.json` `includeIgnored` patterns — the
|
|
283
118
|
* explicit opt-in to index embedded git repos living inside gitignored
|
|
@@ -304,6 +139,160 @@ function loadExcludeMatcher(rootDir) {
|
|
|
304
139
|
const patterns = (0, project_config_1.loadExcludePatterns)(rootDir);
|
|
305
140
|
return patterns.length > 0 ? (0, ignore_1.default)().add(patterns) : null;
|
|
306
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Matcher for the project's `homegraph.json` `include` patterns — first-party
|
|
144
|
+
* source to force INTO the index even when `.gitignore` drops it (the general
|
|
145
|
+
* whitelist `includeIgnored` never was — that one only revives *embedded git
|
|
146
|
+
* repos*). The case it exists for: a project under a second VCS (SVN/Perforce)
|
|
147
|
+
* `.gitignore`s its own real source so it stays out of Git, yet we still want it
|
|
148
|
+
* indexed. Returns `null` when nothing is force-included (the zero-config
|
|
149
|
+
* default → no overhead, no extra walk). Built once per scan/sync/scope
|
|
150
|
+
* operation from the scan root.
|
|
151
|
+
*/
|
|
152
|
+
function loadIncludeMatcher(rootDir) {
|
|
153
|
+
const patterns = (0, project_config_1.loadIncludePatterns)(rootDir);
|
|
154
|
+
return patterns.length > 0 ? (0, ignore_1.default)().add(patterns) : null;
|
|
155
|
+
}
|
|
156
|
+
/** Glob metacharacters that end the static (literal) prefix of an `include` pattern. */
|
|
157
|
+
const GLOB_META = /[*?[\]{}!]/;
|
|
158
|
+
/**
|
|
159
|
+
* The static directory prefix of each `include` pattern — the literal leading
|
|
160
|
+
* path up to the first glob segment — trailing-slashed, used to (a) walk only
|
|
161
|
+
* the opted-in subtrees in `collectIncludedFiles` and (b) let `ScopeIgnore` keep
|
|
162
|
+
* the watcher descending toward them. `Tools/` stays `Tools/`; a recursive
|
|
163
|
+
* `Tools/**` glob yields `Tools/`; `src/local/file.ts` yields `src/local/` (the
|
|
164
|
+
* file's dir); a pattern that starts with a glob (like a leading `**`) yields
|
|
165
|
+
* `''`, meaning "no static root — walk the whole tree". Duplicates and roots
|
|
166
|
+
* nested under a broader root are collapsed so each subtree is walked once.
|
|
167
|
+
*/
|
|
168
|
+
function includeStaticRoots(patterns) {
|
|
169
|
+
const roots = new Set();
|
|
170
|
+
for (const pattern of patterns) {
|
|
171
|
+
let p = pattern.replace(/^\/+/, '');
|
|
172
|
+
const trailingSlash = p.endsWith('/');
|
|
173
|
+
if (trailingSlash)
|
|
174
|
+
p = p.slice(0, -1);
|
|
175
|
+
const segs = p.split('/').filter(Boolean);
|
|
176
|
+
const lead = [];
|
|
177
|
+
for (const s of segs) {
|
|
178
|
+
if (GLOB_META.test(s))
|
|
179
|
+
break;
|
|
180
|
+
lead.push(s);
|
|
181
|
+
}
|
|
182
|
+
const hadWildcard = lead.length < segs.length;
|
|
183
|
+
// A wholly-literal pattern with no trailing slash names a file (or a dir we
|
|
184
|
+
// can't tell apart) — drop its last segment so we walk the containing dir
|
|
185
|
+
// and let the matcher pick the file. A trailing slash or a glob means the
|
|
186
|
+
// remaining `lead` is already the directory to walk.
|
|
187
|
+
if (!hadWildcard && !trailingSlash && lead.length > 0)
|
|
188
|
+
lead.pop();
|
|
189
|
+
if (lead.length === 0) {
|
|
190
|
+
roots.clear();
|
|
191
|
+
roots.add('');
|
|
192
|
+
return ['']; // a top-level glob forces a whole-tree walk; nothing narrower matters
|
|
193
|
+
}
|
|
194
|
+
roots.add(lead.join('/') + '/');
|
|
195
|
+
}
|
|
196
|
+
// Collapse roots nested under a broader one (e.g. drop `a/b/` if `a/` is present).
|
|
197
|
+
const all = [...roots];
|
|
198
|
+
return all.filter((r) => !all.some((other) => other !== r && r.startsWith(other)));
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Actively discover the source files an `include` whitelist forces in. `git
|
|
202
|
+
* ls-files` never lists gitignored files, so a filtered filesystem walk of just
|
|
203
|
+
* the opted-in subtrees (`includeStaticRoots`) is the only way to find them.
|
|
204
|
+
* Returns project-root-relative, normalized source-file paths.
|
|
205
|
+
*
|
|
206
|
+
* A file is collected when it MATCHES `include`, is NOT hit by `exclude` (an
|
|
207
|
+
* explicit exclude always wins), is a recognized source file, and does not live
|
|
208
|
+
* under a built-in default-ignored dir (`node_modules`, `dist`, …), `.git`, or
|
|
209
|
+
* HomeGraph's data dir — those are never resurfaced, mirroring `ScopeIgnore`.
|
|
210
|
+
* `.gitignore` is deliberately NOT consulted: overriding it is the whole point.
|
|
211
|
+
*/
|
|
212
|
+
function collectIncludedFiles(rootDir, include, exclude, roots, overrides) {
|
|
213
|
+
const out = new Set();
|
|
214
|
+
const defaults = (0, default_ignore_1.defaultsOnlyIgnore)();
|
|
215
|
+
const visited = new Set();
|
|
216
|
+
const consider = (abs, rel, isDir) => {
|
|
217
|
+
if (isDir) {
|
|
218
|
+
if (defaults.ignores(rel + '/'))
|
|
219
|
+
return; // never node_modules/dist/… via include
|
|
220
|
+
// An explicit `exclude` always wins over `include`; prune the whole subtree
|
|
221
|
+
// here so a large excluded dir (a committed frontend's own vendored deps,
|
|
222
|
+
// build output, …) is never walked — the per-file guard below still catches
|
|
223
|
+
// anything a directory pattern doesn't, so this is a pure efficiency win.
|
|
224
|
+
if (exclude && exclude.ignores(rel + '/'))
|
|
225
|
+
return;
|
|
226
|
+
walk(abs);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
if (defaults.ignores(rel))
|
|
230
|
+
return;
|
|
231
|
+
if (!include.ignores(rel))
|
|
232
|
+
return;
|
|
233
|
+
if (exclude && exclude.ignores(rel))
|
|
234
|
+
return;
|
|
235
|
+
if (!(0, grammars_1.isSourceFile)(rel, overrides))
|
|
236
|
+
return;
|
|
237
|
+
out.add(rel);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
function walk(absDir) {
|
|
241
|
+
let realDir;
|
|
242
|
+
try {
|
|
243
|
+
realDir = fs.realpathSync(absDir);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (visited.has(realDir))
|
|
249
|
+
return; // symlink-cycle guard
|
|
250
|
+
visited.add(realDir);
|
|
251
|
+
let entries;
|
|
252
|
+
try {
|
|
253
|
+
entries = fs.readdirSync(absDir, { withFileTypes: true });
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
for (const entry of entries) {
|
|
259
|
+
if (entry.name === '.git' || (0, directory_1.isHomeGraphDataDir)(entry.name))
|
|
260
|
+
continue;
|
|
261
|
+
const abs = path.join(absDir, entry.name);
|
|
262
|
+
const rel = (0, utils_1.normalizePath)(path.relative(rootDir, abs));
|
|
263
|
+
if (!rel || rel.startsWith('..'))
|
|
264
|
+
continue;
|
|
265
|
+
if (entry.isSymbolicLink()) {
|
|
266
|
+
try {
|
|
267
|
+
const st = fs.statSync(fs.realpathSync(abs));
|
|
268
|
+
consider(abs, rel, st.isDirectory());
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
// broken symlink — skip
|
|
272
|
+
}
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
consider(abs, rel, entry.isDirectory());
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
for (const root of roots) {
|
|
279
|
+
walk(root === '' ? rootDir : path.join(rootDir, root));
|
|
280
|
+
}
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* The included source files (`homegraph.json` `include`) for a scan root, or an
|
|
285
|
+
* empty set when nothing is force-included. Centralizes loading the matcher,
|
|
286
|
+
* roots, exclude, and overrides so both enumeration paths (git and filesystem
|
|
287
|
+
* walk) add the same files.
|
|
288
|
+
*/
|
|
289
|
+
function collectIncludedFilesForRoot(rootDir) {
|
|
290
|
+
const include = loadIncludeMatcher(rootDir);
|
|
291
|
+
if (!include)
|
|
292
|
+
return new Set();
|
|
293
|
+
const roots = includeStaticRoots((0, project_config_1.loadIncludePatterns)(rootDir));
|
|
294
|
+
return collectIncludedFiles(rootDir, include, loadExcludeMatcher(rootDir), roots, (0, project_config_1.loadExtensionOverrides)(rootDir));
|
|
295
|
+
}
|
|
307
296
|
/**
|
|
308
297
|
* `git ls-files --directory` collapses a wholly-untracked/ignored directory into
|
|
309
298
|
* one entry — and when the command's own cwd is such a directory (the indexed
|
|
@@ -389,7 +378,7 @@ function classifyGitDir(absDir) {
|
|
|
389
378
|
*/
|
|
390
379
|
function findNestedGitRepos(absDir, relPrefix) {
|
|
391
380
|
const found = [];
|
|
392
|
-
const defaults = defaultsOnlyIgnore();
|
|
381
|
+
const defaults = (0, default_ignore_1.defaultsOnlyIgnore)();
|
|
393
382
|
const queue = [
|
|
394
383
|
{ abs: absDir, rel: relPrefix, depth: 0 },
|
|
395
384
|
];
|
|
@@ -444,17 +433,31 @@ function findNestedGitRepos(absDir, relPrefix) {
|
|
|
444
433
|
class ScopeIgnore {
|
|
445
434
|
rootMatcher;
|
|
446
435
|
exclude;
|
|
436
|
+
include;
|
|
437
|
+
includeRoots;
|
|
447
438
|
embedded;
|
|
448
|
-
defaults = defaultsOnlyIgnore();
|
|
439
|
+
defaults = (0, default_ignore_1.defaultsOnlyIgnore)();
|
|
449
440
|
constructor(rootMatcher, embedded,
|
|
450
441
|
/**
|
|
451
|
-
* Project `
|
|
442
|
+
* Project `homegraph.json` `exclude` patterns (#999), matched against the
|
|
452
443
|
* full root-relative path. Wins over everything else — an explicit user
|
|
453
444
|
* exclude applies even to tracked files and even inside embedded repos.
|
|
454
445
|
*/
|
|
455
|
-
exclude = null
|
|
446
|
+
exclude = null,
|
|
447
|
+
/**
|
|
448
|
+
* Project `homegraph.json` `include` patterns — first-party source forced
|
|
449
|
+
* INTO the index despite `.gitignore`. When a path matches, it is NOT
|
|
450
|
+
* ignored (so the watcher watches it), overriding `.gitignore`/`rootMatcher`
|
|
451
|
+
* — but never `exclude` (checked first) and never a built-in default-ignored
|
|
452
|
+
* dir. `includeRoots` are the static prefixes so a gitignored ANCESTOR
|
|
453
|
+
* directory of an included subtree still isn't pruned by the directory
|
|
454
|
+
* walker/watcher.
|
|
455
|
+
*/
|
|
456
|
+
include = null, includeRoots = []) {
|
|
456
457
|
this.rootMatcher = rootMatcher;
|
|
457
458
|
this.exclude = exclude;
|
|
459
|
+
this.include = include;
|
|
460
|
+
this.includeRoots = includeRoots;
|
|
458
461
|
// Longest root first so paths in nested embedded repos hit the innermost matcher.
|
|
459
462
|
this.embedded = [...embedded].sort((a, b) => b.root.length - a.root.length);
|
|
460
463
|
}
|
|
@@ -464,6 +467,20 @@ class ScopeIgnore {
|
|
|
464
467
|
// everywhere, including ancestors of embedded repos.
|
|
465
468
|
if (this.exclude && this.exclude.ignores(rel))
|
|
466
469
|
return true;
|
|
470
|
+
// User `include`: force first-party source in despite `.gitignore`. Never
|
|
471
|
+
// resurfaces a built-in default-ignored dir (node_modules/dist/…), so an
|
|
472
|
+
// include pattern can't accidentally pull in dependency/build trees.
|
|
473
|
+
if (this.include && !this.defaults.ignores(rel)) {
|
|
474
|
+
if (rel.endsWith('/')) {
|
|
475
|
+
// A directory on (or leading to) an included subtree must stay walkable
|
|
476
|
+
// so the watcher/walker descends to reach the forced-in files.
|
|
477
|
+
if (this.includeRoots.some((r) => r.startsWith(rel) || rel.startsWith(r)))
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
else if (this.include.ignores(rel)) {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
467
484
|
for (const { root, matcher } of this.embedded) {
|
|
468
485
|
if (rel.startsWith(root)) {
|
|
469
486
|
const inner = rel.slice(root.length);
|
|
@@ -490,7 +507,8 @@ exports.ScopeIgnore = ScopeIgnore;
|
|
|
490
507
|
*/
|
|
491
508
|
function buildScopeIgnore(rootDir, embeddedRoots) {
|
|
492
509
|
const roots = embeddedRoots ? [...embeddedRoots] : discoverEmbeddedRepoRoots(rootDir);
|
|
493
|
-
|
|
510
|
+
const include = loadIncludeMatcher(rootDir);
|
|
511
|
+
return new ScopeIgnore((0, default_ignore_1.buildDefaultIgnore)(rootDir), roots.map((root) => ({ root, matcher: (0, default_ignore_1.buildDefaultIgnore)(path.join(rootDir, root)) })), loadExcludeMatcher(rootDir), include, include ? includeStaticRoots((0, project_config_1.loadIncludePatterns)(rootDir)) : []);
|
|
494
512
|
}
|
|
495
513
|
/**
|
|
496
514
|
* Whether an embedded repo found as a tracked gitlink (mode 160000, #1031/#1033)
|
|
@@ -503,7 +521,7 @@ function buildScopeIgnore(rootDir, embeddedRoots) {
|
|
|
503
521
|
* under `node_modules` is never project code; not even an explicit opt-in
|
|
504
522
|
* revives it (matches `findIgnoredEmbeddedRepos`).
|
|
505
523
|
* 2. The parent repo's own `.gitignore` covers its path and the project did
|
|
506
|
-
* NOT opt that path in via `
|
|
524
|
+
* NOT opt that path in via `homegraph.json` `includeIgnored`. The gitignore
|
|
507
525
|
* rule is the user's stated intent to keep that path out of scope, exactly
|
|
508
526
|
* as for an UNtracked embedded repo — respect it by default, opt back in
|
|
509
527
|
* with `includeIgnored` (#514, #970, #976).
|
|
@@ -538,7 +556,7 @@ function discoverEmbeddedRepoRoots(rootDir) {
|
|
|
538
556
|
return [];
|
|
539
557
|
}
|
|
540
558
|
const out = [];
|
|
541
|
-
const defaults = defaultsOnlyIgnore();
|
|
559
|
+
const defaults = (0, default_ignore_1.defaultsOnlyIgnore)();
|
|
542
560
|
const includeIgnored = loadIncludeIgnoredMatcher(rootDir);
|
|
543
561
|
const visit = (repoAbs, prefix) => {
|
|
544
562
|
const candidates = [];
|
|
@@ -558,7 +576,7 @@ function discoverEmbeddedRepoRoots(rootDir) {
|
|
|
558
576
|
// (#1031, #1033)
|
|
559
577
|
try {
|
|
560
578
|
const gitOpts = { cwd: repoAbs, encoding: 'utf-8', timeout: 30000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true };
|
|
561
|
-
const repoIgnore = buildDefaultIgnore(repoAbs);
|
|
579
|
+
const repoIgnore = (0, default_ignore_1.buildDefaultIgnore)(repoAbs);
|
|
562
580
|
for (const rel of listGitlinkRels(gitOpts)) {
|
|
563
581
|
const relDir = rel.endsWith('/') ? rel : rel + '/';
|
|
564
582
|
// A gitlink under a gitignored path is respected (not indexed) unless the
|
|
@@ -580,6 +598,50 @@ function discoverEmbeddedRepoRoots(rootDir) {
|
|
|
580
598
|
visit(rootDir, '');
|
|
581
599
|
return out;
|
|
582
600
|
}
|
|
601
|
+
/**
|
|
602
|
+
* Cap on how many skipped gitignored repos the CLI hint enumerates — a huge
|
|
603
|
+
* gitignored data dir full of clones must never turn the hint scan into a long
|
|
604
|
+
* walk. Enough to make the point; the caller says "+N more" past this.
|
|
605
|
+
*/
|
|
606
|
+
const UNINDEXED_IGNORED_REPO_HINT_CAP = 100;
|
|
607
|
+
/**
|
|
608
|
+
* The INVERSE of the gitignored side of {@link discoverEmbeddedRepoRoots}:
|
|
609
|
+
* nested git repositories under a gitignored directory that the project has NOT
|
|
610
|
+
* opted into via `homegraph.json` `includeIgnored`. These are real repos the
|
|
611
|
+
* default `init`/`index` deliberately skips because `.gitignore` excludes them
|
|
612
|
+
* (#970, #976) — most visibly the "super-repo `.gitignore`s its child repos"
|
|
613
|
+
* layout (#1156), where `init` at the parent correctly indexes ~nothing while
|
|
614
|
+
* `init` inside each child works. The CLI uses this to turn that silent empty
|
|
615
|
+
* index into an actionable hint: it names the skipped repos and offers to opt
|
|
616
|
+
* them in. Paths are `rootDir`-relative and trailing-slashed (valid
|
|
617
|
+
* `includeIgnored` patterns as-is). Returns `[]` for a non-git root (a
|
|
618
|
+
* filesystem walk already descends into nested repos there), skips built-in
|
|
619
|
+
* default-ignored dirs (`node_modules`, …), and is bounded so it never stalls
|
|
620
|
+
* on a giant ignored tree.
|
|
621
|
+
*/
|
|
622
|
+
function findUnindexedIgnoredRepos(rootDir) {
|
|
623
|
+
try {
|
|
624
|
+
(0, child_process_1.execFileSync)('git', ['rev-parse', '--git-dir'], { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
625
|
+
}
|
|
626
|
+
catch {
|
|
627
|
+
return [];
|
|
628
|
+
}
|
|
629
|
+
const defaults = (0, default_ignore_1.defaultsOnlyIgnore)();
|
|
630
|
+
const includeIgnored = loadIncludeIgnoredMatcher(rootDir);
|
|
631
|
+
const repos = [];
|
|
632
|
+
for (const dir of listIgnoredDirs(rootDir)) {
|
|
633
|
+
if (defaults.ignores(dir))
|
|
634
|
+
continue; // node_modules etc. — never project code
|
|
635
|
+
if (includeIgnored?.ignores((0, utils_1.normalizePath)(dir)))
|
|
636
|
+
continue; // already opted in — nothing to nag about
|
|
637
|
+
for (const repo of findNestedGitRepos(path.join(rootDir, dir), dir)) {
|
|
638
|
+
repos.push(repo);
|
|
639
|
+
if (repos.length >= UNINDEXED_IGNORED_REPO_HINT_CAP)
|
|
640
|
+
return repos;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return repos;
|
|
644
|
+
}
|
|
583
645
|
/**
|
|
584
646
|
* Discover embedded repos hidden by `repoDir`'s OWN gitignore rules: for each
|
|
585
647
|
* gitignored directory, search for nested `.git` roots. Returns repo paths
|
|
@@ -598,7 +660,7 @@ function discoverEmbeddedRepoRoots(rootDir) {
|
|
|
598
660
|
function findIgnoredEmbeddedRepos(repoDir, includeIgnored, prefix) {
|
|
599
661
|
if (!includeIgnored)
|
|
600
662
|
return [];
|
|
601
|
-
const defaults = defaultsOnlyIgnore();
|
|
663
|
+
const defaults = (0, default_ignore_1.defaultsOnlyIgnore)();
|
|
602
664
|
const repos = [];
|
|
603
665
|
for (const dir of listIgnoredDirs(repoDir)) {
|
|
604
666
|
if (defaults.ignores(dir))
|
|
@@ -710,7 +772,7 @@ function collectGitFiles(repoDir, prefix, files, embeddedRoots, includeIgnored =
|
|
|
710
772
|
const childDir = path.join(repoDir, rel);
|
|
711
773
|
// A git worktree surfaces here as an opaque untracked dir too — skip it,
|
|
712
774
|
// it's a duplicate working view of an already-indexed repo (#848).
|
|
713
|
-
if (classifyGitDir(childDir) === 'embedded' && !defaultsOnlyIgnore().ignores(rel)) {
|
|
775
|
+
if (classifyGitDir(childDir) === 'embedded' && !(0, default_ignore_1.defaultsOnlyIgnore)().ignores(rel)) {
|
|
714
776
|
embeddedRoots?.add((0, utils_1.normalizePath)(prefix + rel));
|
|
715
777
|
collectGitFiles(childDir, prefix + rel, files, embeddedRoots, includeIgnored);
|
|
716
778
|
}
|
|
@@ -727,8 +789,8 @@ function collectGitFiles(repoDir, prefix, files, embeddedRoots, includeIgnored =
|
|
|
727
789
|
// (an uninitialized submodule — empty dir, no `.git`) has nothing to index and
|
|
728
790
|
// is left alone, as is a submodule worktree (a duplicate view, #945). (#1031, #1033)
|
|
729
791
|
if (gitlinkRels.length > 0) {
|
|
730
|
-
const defaults = defaultsOnlyIgnore();
|
|
731
|
-
const repoIgnore = buildDefaultIgnore(repoDir);
|
|
792
|
+
const defaults = (0, default_ignore_1.defaultsOnlyIgnore)();
|
|
793
|
+
const repoIgnore = (0, default_ignore_1.buildDefaultIgnore)(repoDir);
|
|
732
794
|
for (const rel of gitlinkRels) {
|
|
733
795
|
const relDir = rel.endsWith('/') ? rel : rel + '/';
|
|
734
796
|
// A gitlink under a gitignored path is respected (not indexed) unless the
|
|
@@ -787,7 +849,11 @@ function getGitVisibleFiles(rootDir) {
|
|
|
787
849
|
// not the parent's: the parent's .gitignore hides the child repo from git,
|
|
788
850
|
// not from the index. (#514)
|
|
789
851
|
const ig = buildScopeIgnore(rootDir, embeddedRoots);
|
|
790
|
-
|
|
852
|
+
const visible = new Set([...files].filter((f) => !ig.ignores(f)));
|
|
853
|
+
// Force-include first-party source whitelisted in `homegraph.json` `include`.
|
|
854
|
+
for (const f of collectIncludedFilesForRoot(rootDir))
|
|
855
|
+
visible.add(f);
|
|
856
|
+
return visible;
|
|
791
857
|
}
|
|
792
858
|
catch {
|
|
793
859
|
return null;
|
|
@@ -832,7 +898,7 @@ function collectGitStatus(repoDir, prefix, out, overrides, includeIgnored = null
|
|
|
832
898
|
// repo-relative `rel` at each recursion level mirrors getGitVisibleFiles'
|
|
833
899
|
// ScopeIgnore: every embedded repo is judged by ITS OWN rules, never the
|
|
834
900
|
// parent's. (#766)
|
|
835
|
-
const ig = buildDefaultIgnore(repoDir);
|
|
901
|
+
const ig = (0, default_ignore_1.buildDefaultIgnore)(repoDir);
|
|
836
902
|
const untrackedDirs = [];
|
|
837
903
|
for (const line of output.split('\n')) {
|
|
838
904
|
if (line.length < 4)
|
|
@@ -955,7 +1021,7 @@ function scanDirectoryWalk(rootDir, onProgress) {
|
|
|
955
1021
|
// readGitignorePatterns is defensive: a non-UTF-8 (DLP-encrypted) or
|
|
956
1022
|
// uncompilable .gitignore is skipped/filtered with a warning, never thrown
|
|
957
1023
|
// (issue #682) — so the per-file `.ignores()` calls below can't crash.
|
|
958
|
-
const patterns = readGitignorePatterns(giPath);
|
|
1024
|
+
const patterns = (0, default_ignore_1.readGitignorePatterns)(giPath);
|
|
959
1025
|
return patterns ? { dir, ig: (0, ignore_1.default)().add(patterns) } : null;
|
|
960
1026
|
};
|
|
961
1027
|
const isIgnored = (fullPath, isDir, matchers) => {
|
|
@@ -1042,7 +1108,7 @@ function scanDirectoryWalk(rootDir, onProgress) {
|
|
|
1042
1108
|
}
|
|
1043
1109
|
// Seed a base matcher with the built-in default ignores (merged with the root
|
|
1044
1110
|
// .gitignore so a negation can override). Nested .gitignores still layer per-dir.
|
|
1045
|
-
const baseMatchers = [{ dir: rootDir, ig: buildDefaultIgnore(rootDir) }];
|
|
1111
|
+
const baseMatchers = [{ dir: rootDir, ig: (0, default_ignore_1.buildDefaultIgnore)(rootDir) }];
|
|
1046
1112
|
// Project `codegraph.json` `exclude` patterns (#999), rooted at the project so
|
|
1047
1113
|
// `isIgnored` matches them against root-relative paths — same coverage the
|
|
1048
1114
|
// git path gets via ScopeIgnore, for non-git projects.
|
|
@@ -1050,8 +1116,52 @@ function scanDirectoryWalk(rootDir, onProgress) {
|
|
|
1050
1116
|
if (exclude)
|
|
1051
1117
|
baseMatchers.push({ dir: rootDir, ig: exclude });
|
|
1052
1118
|
walk(rootDir, baseMatchers);
|
|
1119
|
+
// Force-include first-party source whitelisted in `homegraph.json` `include`.
|
|
1120
|
+
const included = collectIncludedFilesForRoot(rootDir);
|
|
1121
|
+
if (included.size > 0) {
|
|
1122
|
+
const seen = new Set(files);
|
|
1123
|
+
for (const f of included) {
|
|
1124
|
+
if (!seen.has(f)) {
|
|
1125
|
+
files.push(f);
|
|
1126
|
+
seen.add(f);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1053
1130
|
return files;
|
|
1054
1131
|
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Extraction orchestrator
|
|
1134
|
+
*/
|
|
1135
|
+
/**
|
|
1136
|
+
* Resurrect a resolution edge that is about to be dropped (its target symbol
|
|
1137
|
+
* was removed, renamed, or its whole file deleted) as the ORIGINAL unresolved
|
|
1138
|
+
* reference that created it, read from the refName/refKind stamp
|
|
1139
|
+
* `createEdges` writes into edge metadata. Inserted as status='pending', the
|
|
1140
|
+
* ref is consumed by the same sync's resolution sweep: it rebinds to an
|
|
1141
|
+
* alternative definition if one exists, or parks as status='failed' where the
|
|
1142
|
+
* #1240 retry finds it if the symbol later reappears.
|
|
1143
|
+
*
|
|
1144
|
+
* Returns null — drop silently, the pre-#1240 behavior — for edges without a
|
|
1145
|
+
* refName stamp (created before the stamp existed, or synthesized): rebuilding
|
|
1146
|
+
* a ref from the target's plain node name would strip the receiver/qualifier
|
|
1147
|
+
* context the original text carried (`h.greet` → `greet`) and could rebind
|
|
1148
|
+
* somewhere a full re-index never would. Silent beats wrong.
|
|
1149
|
+
*/
|
|
1150
|
+
function resurrectRefFromDroppedEdge(e) {
|
|
1151
|
+
const refName = e.metadata?.refName;
|
|
1152
|
+
if (typeof refName !== 'string' || refName.length === 0)
|
|
1153
|
+
return null;
|
|
1154
|
+
const refKind = typeof e.metadata?.refKind === 'string' ? e.metadata.refKind : e.kind;
|
|
1155
|
+
return {
|
|
1156
|
+
fromNodeId: e.source,
|
|
1157
|
+
referenceName: refName,
|
|
1158
|
+
referenceKind: refKind,
|
|
1159
|
+
line: e.line ?? 0,
|
|
1160
|
+
column: e.column ?? 0,
|
|
1161
|
+
filePath: e.sourceFilePath,
|
|
1162
|
+
language: e.sourceLanguage,
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1055
1165
|
/**
|
|
1056
1166
|
* Extraction orchestrator
|
|
1057
1167
|
*/
|
|
@@ -1144,7 +1254,7 @@ class ExtractionOrchestrator {
|
|
|
1144
1254
|
/**
|
|
1145
1255
|
* Index all files in the project
|
|
1146
1256
|
*/
|
|
1147
|
-
async indexAll(onProgress, signal, verbose) {
|
|
1257
|
+
async indexAll(onProgress, signal, verbose, walBackpressure, storeWriterOpts) {
|
|
1148
1258
|
await (0, grammars_1.initGrammars)();
|
|
1149
1259
|
const startTime = Date.now();
|
|
1150
1260
|
const errors = [];
|
|
@@ -1246,6 +1356,15 @@ class ExtractionOrchestrator {
|
|
|
1246
1356
|
// In-process fallback: load grammars locally and parse on the main thread.
|
|
1247
1357
|
await (0, grammars_1.loadGrammarsForLanguages)(neededLanguages);
|
|
1248
1358
|
}
|
|
1359
|
+
const storeWorkerPath = path.join(__dirname, 'store-worker.js');
|
|
1360
|
+
let storeWriter = null;
|
|
1361
|
+
if (storeWriterOpts &&
|
|
1362
|
+
process.env.HOMEGRAPH_NO_STORE_WORKER !== '1' &&
|
|
1363
|
+
fs.existsSync(storeWorkerPath)) {
|
|
1364
|
+
storeWriter = new store_writer_1.StoreWriter(storeWorkerPath, storeWriterOpts.dbPath, storeWriterOpts.fastInit);
|
|
1365
|
+
log('Store writer thread active');
|
|
1366
|
+
}
|
|
1367
|
+
const STORE_WRITER_WINDOW = 64;
|
|
1249
1368
|
/**
|
|
1250
1369
|
* Parse one file: on the pool when available (the promise REJECTS on a worker
|
|
1251
1370
|
* crash/timeout — the caller records it and the retry pass re-attempts), or
|
|
@@ -1275,12 +1394,21 @@ class ExtractionOrchestrator {
|
|
|
1275
1394
|
let nextSeq = 0; // file-order sequence assigned at dispatch
|
|
1276
1395
|
let nextToStore = 0; // cursor: next sequence to commit
|
|
1277
1396
|
let aborted = false;
|
|
1278
|
-
const
|
|
1397
|
+
const commitYield = (0, cooperative_yield_1.createYielder)();
|
|
1398
|
+
const storeResult = async (filePath, content, stats, result) => {
|
|
1279
1399
|
processed++;
|
|
1280
|
-
|
|
1400
|
+
const bp = walBackpressure?.();
|
|
1401
|
+
if (bp)
|
|
1402
|
+
await bp;
|
|
1281
1403
|
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1282
1404
|
const language = (0, grammars_1.detectLanguage)(filePath, content, overrides);
|
|
1283
|
-
|
|
1405
|
+
if (storeWriter) {
|
|
1406
|
+
storeWriter.send(this.buildFreshStoreBundle(filePath, content, language, stats, result));
|
|
1407
|
+
await storeWriter.waitBelow(STORE_WRITER_WINDOW);
|
|
1408
|
+
}
|
|
1409
|
+
else {
|
|
1410
|
+
await this.storeExtractionResult(filePath, content, language, stats, result, commitYield);
|
|
1411
|
+
}
|
|
1284
1412
|
}
|
|
1285
1413
|
if (result.errors.length > 0) {
|
|
1286
1414
|
for (const err of result.errors) {
|
|
@@ -1298,9 +1426,6 @@ class ExtractionOrchestrator {
|
|
|
1298
1426
|
filesErrored++;
|
|
1299
1427
|
}
|
|
1300
1428
|
else {
|
|
1301
|
-
// Files with no symbols but no errors (yaml, twig, properties) are
|
|
1302
|
-
// tracked at the file level — count them as indexed so the CLI doesn't
|
|
1303
|
-
// misleadingly report "No files found to index".
|
|
1304
1429
|
const lang = (0, grammars_1.detectLanguage)(filePath, content, overrides);
|
|
1305
1430
|
if ((0, grammars_1.isFileLevelOnlyLanguage)(lang)) {
|
|
1306
1431
|
filesIndexed++;
|
|
@@ -1322,22 +1447,28 @@ class ExtractionOrchestrator {
|
|
|
1322
1447
|
});
|
|
1323
1448
|
onProgress?.({ phase: 'parsing', current: processed, total });
|
|
1324
1449
|
};
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
// after the drain). storeResult / recordParseFailure run here single-threaded,
|
|
1328
|
-
// so shared counters and SQLite writes never race despite parallel parsing.
|
|
1450
|
+
let flushChain = Promise.resolve();
|
|
1451
|
+
let flushError = null;
|
|
1329
1452
|
const flushOrdered = () => {
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1453
|
+
flushChain = flushChain.then(async () => {
|
|
1454
|
+
if (aborted || flushError)
|
|
1455
|
+
return;
|
|
1456
|
+
try {
|
|
1457
|
+
while (completed.has(nextToStore)) {
|
|
1458
|
+
const item = completed.get(nextToStore);
|
|
1459
|
+
completed.delete(nextToStore);
|
|
1460
|
+
nextToStore++;
|
|
1461
|
+
if (item.ok)
|
|
1462
|
+
await storeResult(item.filePath, item.content, item.stats, item.result);
|
|
1463
|
+
else
|
|
1464
|
+
recordParseFailure(item.filePath, item.err);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
catch (err) {
|
|
1468
|
+
flushError = err;
|
|
1469
|
+
}
|
|
1470
|
+
});
|
|
1471
|
+
return flushChain;
|
|
1341
1472
|
};
|
|
1342
1473
|
// Dispatch one file's parse (parses run concurrently across the pool), tagged
|
|
1343
1474
|
// with its file-order sequence so flushOrdered commits results in order. The
|
|
@@ -1360,10 +1491,15 @@ class ExtractionOrchestrator {
|
|
|
1360
1491
|
// buffered), not just in-flight: a slow file sitting at the commit cursor
|
|
1361
1492
|
// lets later parses finish and buffer, which would otherwise grow without
|
|
1362
1493
|
// bound. Wait for parses to settle (each may advance the cursor) until the
|
|
1363
|
-
// window has room.
|
|
1364
|
-
//
|
|
1365
|
-
|
|
1366
|
-
|
|
1494
|
+
// window has room. When nothing is in flight but the window is still full,
|
|
1495
|
+
// the async commit chain is what's behind — await it so the cursor
|
|
1496
|
+
// advances (buffered items hold whole file contents, so this bound is
|
|
1497
|
+
// load-bearing for memory).
|
|
1498
|
+
while (nextSeq - nextToStore >= windowSize) {
|
|
1499
|
+
if (inFlight.size > 0)
|
|
1500
|
+
await Promise.race(inFlight);
|
|
1501
|
+
else
|
|
1502
|
+
await flushOrdered();
|
|
1367
1503
|
}
|
|
1368
1504
|
};
|
|
1369
1505
|
const firstEtsFile = files.find((f) => (0, grammars_1.detectLanguage)(f, undefined, overrides) === 'arkts');
|
|
@@ -1468,9 +1604,25 @@ class ExtractionOrchestrator {
|
|
|
1468
1604
|
// then commit any results the cursor hasn't reached yet.
|
|
1469
1605
|
if (!aborted) {
|
|
1470
1606
|
await Promise.all(inFlight);
|
|
1471
|
-
flushOrdered();
|
|
1607
|
+
await flushOrdered();
|
|
1608
|
+
if (flushError) {
|
|
1609
|
+
if (storeWriter)
|
|
1610
|
+
await storeWriter.close();
|
|
1611
|
+
throw flushError;
|
|
1612
|
+
}
|
|
1613
|
+
if (storeWriter) {
|
|
1614
|
+
try {
|
|
1615
|
+
await storeWriter.drain();
|
|
1616
|
+
}
|
|
1617
|
+
finally {
|
|
1618
|
+
await storeWriter.close();
|
|
1619
|
+
storeWriter = null;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1472
1622
|
}
|
|
1473
1623
|
if (signal?.aborted || aborted) {
|
|
1624
|
+
if (storeWriter)
|
|
1625
|
+
await storeWriter.close();
|
|
1474
1626
|
if (pool)
|
|
1475
1627
|
await pool.destroy();
|
|
1476
1628
|
return {
|
|
@@ -1478,6 +1630,7 @@ class ExtractionOrchestrator {
|
|
|
1478
1630
|
filesIndexed,
|
|
1479
1631
|
filesSkipped,
|
|
1480
1632
|
filesErrored,
|
|
1633
|
+
filesDiscovered: total,
|
|
1481
1634
|
nodesCreated: totalNodes,
|
|
1482
1635
|
edgesCreated: totalEdges,
|
|
1483
1636
|
errors: [{ message: 'Aborted', severity: 'error' }, ...errors],
|
|
@@ -1531,7 +1684,7 @@ class ExtractionOrchestrator {
|
|
|
1531
1684
|
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1532
1685
|
const language = (0, grammars_1.detectLanguage)(filePath, content, overrides);
|
|
1533
1686
|
const stats = await fsp.stat(path.join(this.rootDir, filePath));
|
|
1534
|
-
this.storeExtractionResult(filePath, content, language, stats, result);
|
|
1687
|
+
await this.storeExtractionResult(filePath, content, language, stats, result, (0, cooperative_yield_1.createYielder)());
|
|
1535
1688
|
const idx = errors.indexOf(errEntry);
|
|
1536
1689
|
if (idx >= 0)
|
|
1537
1690
|
errors.splice(idx, 1);
|
|
@@ -1579,7 +1732,7 @@ class ExtractionOrchestrator {
|
|
|
1579
1732
|
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1580
1733
|
const language = (0, grammars_1.detectLanguage)(filePath, fullContent, overrides);
|
|
1581
1734
|
const stats = await fsp.stat(path.join(this.rootDir, filePath));
|
|
1582
|
-
this.storeExtractionResult(filePath, fullContent, language, stats, result);
|
|
1735
|
+
await this.storeExtractionResult(filePath, fullContent, language, stats, result, (0, cooperative_yield_1.createYielder)());
|
|
1583
1736
|
const idx = errors.indexOf(errEntry);
|
|
1584
1737
|
if (idx >= 0)
|
|
1585
1738
|
errors.splice(idx, 1);
|
|
@@ -1600,6 +1753,7 @@ class ExtractionOrchestrator {
|
|
|
1600
1753
|
filesIndexed,
|
|
1601
1754
|
filesSkipped,
|
|
1602
1755
|
filesErrored,
|
|
1756
|
+
filesDiscovered: total,
|
|
1603
1757
|
nodesCreated: totalNodes,
|
|
1604
1758
|
edgesCreated: totalEdges,
|
|
1605
1759
|
errors,
|
|
@@ -1756,99 +1910,77 @@ class ExtractionOrchestrator {
|
|
|
1756
1910
|
const result = (0, tree_sitter_1.extractFromSource)(relativePath, content, language, frameworkNames);
|
|
1757
1911
|
// Store in database
|
|
1758
1912
|
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1759
|
-
this.storeExtractionResult(relativePath, content, language, stats, result);
|
|
1913
|
+
await this.storeExtractionResult(relativePath, content, language, stats, result, (0, cooperative_yield_1.createYielder)());
|
|
1760
1914
|
}
|
|
1761
1915
|
return result;
|
|
1762
1916
|
}
|
|
1763
1917
|
/**
|
|
1764
1918
|
* Store extraction result in database
|
|
1765
1919
|
*/
|
|
1766
|
-
storeExtractionResult(filePath, content, language, stats, result) {
|
|
1920
|
+
async storeExtractionResult(filePath, content, language, stats, result, onYield) {
|
|
1921
|
+
const STORE_CHUNK = 2000;
|
|
1767
1922
|
const contentHash = hashContent(content);
|
|
1768
|
-
// Check if file already exists and hasn't changed
|
|
1769
1923
|
const existingFile = this.queries.getFileByPath(filePath);
|
|
1770
1924
|
if (existingFile && existingFile.contentHash === contentHash) {
|
|
1771
|
-
return;
|
|
1772
|
-
}
|
|
1773
|
-
// Snapshot incoming cross-file edges BEFORE deleting this file's nodes.
|
|
1774
|
-
// `deleteFile` cascades to delete every edge whose source OR target is a
|
|
1775
|
-
// node in this file (edges.FK ... ON DELETE CASCADE). Edges whose SOURCE is
|
|
1776
|
-
// in this file are re-emitted by the extractor below, but edges whose SOURCE
|
|
1777
|
-
// is in a *different* (unchanged) file are not — they would be silently
|
|
1778
|
-
// dropped, which is issue #899: re-indexing a callee file severs `calls`/
|
|
1779
|
-
// `references` edges from callers that import it via module-attribute
|
|
1780
|
-
// access (`pkg.mod.fn(...)`).
|
|
1781
|
-
//
|
|
1782
|
-
// We snapshot the edge plus the target node's (name, kind) so we can
|
|
1783
|
-
// re-resolve to the re-indexed target's NEW id. Node ids are
|
|
1784
|
-
// `sha256(filePath:kind:name:line)`, so any line shift in the callee file
|
|
1785
|
-
// (e.g. a docstring-only edit above the symbol) changes every target id and
|
|
1786
|
-
// a naive re-insert by old id would silently drop every edge. Matching by
|
|
1787
|
-
// (filePath, kind, name) is stable across line shifts; if the symbol was
|
|
1788
|
-
// renamed/removed, no match is found and the edge stays dropped (correct).
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1789
1927
|
const crossFileIncomingEdges = existingFile
|
|
1790
1928
|
? this.queries.getCrossFileIncomingEdgesWithTarget(filePath)
|
|
1791
1929
|
: [];
|
|
1792
|
-
// Delete existing data for this file
|
|
1793
1930
|
if (existingFile) {
|
|
1794
1931
|
this.queries.deleteFile(filePath);
|
|
1795
1932
|
}
|
|
1796
|
-
// Filter out nodes with missing required fields before insertion.
|
|
1797
|
-
// This prevents FK violations when edges reference nodes that would
|
|
1798
|
-
// be silently skipped by insertNode() (see issue #42).
|
|
1799
1933
|
const validNodes = result.nodes.filter((n) => n.id && n.kind && n.name && n.filePath && n.language);
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1934
|
+
const insertedIds = new Set(validNodes.map((n) => n.id));
|
|
1935
|
+
const validEdges = result.edges.filter((e) => insertedIds.has(e.source) && insertedIds.has(e.target));
|
|
1936
|
+
const validRefs = result.unresolvedReferences
|
|
1937
|
+
.filter((ref) => insertedIds.has(ref.fromNodeId))
|
|
1938
|
+
.map((ref) => ({
|
|
1939
|
+
...ref,
|
|
1940
|
+
filePath: ref.filePath ?? filePath,
|
|
1941
|
+
language: ref.language ?? language,
|
|
1942
|
+
}));
|
|
1943
|
+
const fitsOneChunk = validNodes.length <= STORE_CHUNK &&
|
|
1944
|
+
validEdges.length <= STORE_CHUNK &&
|
|
1945
|
+
validRefs.length <= STORE_CHUNK;
|
|
1946
|
+
if (fitsOneChunk) {
|
|
1947
|
+
this.queries.storeFileBundle({
|
|
1948
|
+
nodes: validNodes,
|
|
1949
|
+
edges: validEdges,
|
|
1950
|
+
refs: validRefs,
|
|
1951
|
+
file: {
|
|
1952
|
+
path: filePath,
|
|
1953
|
+
contentHash,
|
|
1954
|
+
language,
|
|
1955
|
+
size: stats.size,
|
|
1956
|
+
modifiedAt: stats.mtimeMs,
|
|
1957
|
+
indexedAt: Date.now(),
|
|
1958
|
+
nodeCount: result.nodes.length,
|
|
1959
|
+
errors: result.errors.length > 0 ? result.errors : undefined,
|
|
1960
|
+
},
|
|
1961
|
+
});
|
|
1962
|
+
if (crossFileIncomingEdges.length > 0) {
|
|
1963
|
+
this.reattachCrossFileEdges(crossFileIncomingEdges, validNodes);
|
|
1810
1964
|
}
|
|
1965
|
+
return;
|
|
1811
1966
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
// shift in the callee file (e.g. a docstring-only edit above the symbol)
|
|
1816
|
-
// changes every target id and a naive re-insert by old id would drop them
|
|
1817
|
-
// all. `insertEdges` still filters to endpoints that exist, so edges whose
|
|
1818
|
-
// caller (source) was deleted, or whose callee (target) was renamed/removed
|
|
1819
|
-
// during the re-index (no match in `newTargetIds`), are dropped. This
|
|
1820
|
-
// closes the #899 edge-drop on `sync`.
|
|
1821
|
-
if (crossFileIncomingEdges.length > 0) {
|
|
1822
|
-
const newNodesByKindName = new Map();
|
|
1823
|
-
for (const n of validNodes) {
|
|
1824
|
-
newNodesByKindName.set(`${n.kind}\0${n.name}`, n.id);
|
|
1825
|
-
}
|
|
1826
|
-
const reinserted = [];
|
|
1827
|
-
for (const e of crossFileIncomingEdges) {
|
|
1828
|
-
const newTargetId = newNodesByKindName.get(`${e.targetKind}\0${e.targetName}`);
|
|
1829
|
-
if (newTargetId) {
|
|
1830
|
-
reinserted.push({ source: e.source, target: newTargetId, kind: e.kind, metadata: e.metadata, line: e.line, column: e.column, provenance: e.provenance });
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
if (reinserted.length > 0) {
|
|
1834
|
-
this.queries.insertEdges(reinserted);
|
|
1835
|
-
}
|
|
1967
|
+
for (let i = 0; i < validNodes.length; i += STORE_CHUNK) {
|
|
1968
|
+
this.queries.insertNodes(validNodes.slice(i, i + STORE_CHUNK));
|
|
1969
|
+
await onYield?.();
|
|
1836
1970
|
}
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
.filter((ref) => insertedIds.has(ref.fromNodeId))
|
|
1842
|
-
.map((ref) => ({
|
|
1843
|
-
...ref,
|
|
1844
|
-
filePath: ref.filePath ?? filePath,
|
|
1845
|
-
language: ref.language ?? language,
|
|
1846
|
-
}));
|
|
1847
|
-
if (refsWithContext.length > 0) {
|
|
1848
|
-
this.queries.insertUnresolvedRefsBatch(refsWithContext);
|
|
1971
|
+
if (validEdges.length > 0) {
|
|
1972
|
+
for (let i = 0; i < validEdges.length; i += STORE_CHUNK) {
|
|
1973
|
+
this.queries.insertEdges(validEdges.slice(i, i + STORE_CHUNK));
|
|
1974
|
+
await onYield?.();
|
|
1849
1975
|
}
|
|
1850
1976
|
}
|
|
1851
|
-
|
|
1977
|
+
if (crossFileIncomingEdges.length > 0) {
|
|
1978
|
+
this.reattachCrossFileEdges(crossFileIncomingEdges, validNodes);
|
|
1979
|
+
}
|
|
1980
|
+
for (let i = 0; i < validRefs.length; i += STORE_CHUNK) {
|
|
1981
|
+
this.queries.insertUnresolvedRefsBatch(validRefs.slice(i, i + STORE_CHUNK));
|
|
1982
|
+
await onYield?.();
|
|
1983
|
+
}
|
|
1852
1984
|
const fileRecord = {
|
|
1853
1985
|
path: filePath,
|
|
1854
1986
|
contentHash,
|
|
@@ -1861,6 +1993,46 @@ class ExtractionOrchestrator {
|
|
|
1861
1993
|
};
|
|
1862
1994
|
this.queries.upsertFile(fileRecord);
|
|
1863
1995
|
}
|
|
1996
|
+
buildFileRecord(filePath, content, language, stats, nodeCount, resultErrors) {
|
|
1997
|
+
return {
|
|
1998
|
+
path: filePath,
|
|
1999
|
+
contentHash: hashContent(content),
|
|
2000
|
+
language,
|
|
2001
|
+
size: stats.size,
|
|
2002
|
+
modifiedAt: stats.mtimeMs,
|
|
2003
|
+
indexedAt: Date.now(),
|
|
2004
|
+
nodeCount,
|
|
2005
|
+
errors: resultErrors.length > 0 ? resultErrors : undefined,
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
buildFreshStoreBundle(filePath, content, language, stats, result) {
|
|
2009
|
+
return (0, store_writer_1.finalizeStoreBundle)(result, filePath, language, this.buildFileRecord(filePath, content, language, stats, result.nodes.length, result.errors));
|
|
2010
|
+
}
|
|
2011
|
+
reattachCrossFileEdges(crossFileIncomingEdges, validNodes) {
|
|
2012
|
+
const newNodesByKindName = new Map();
|
|
2013
|
+
for (const n of validNodes) {
|
|
2014
|
+
newNodesByKindName.set(`${n.kind}\0${n.name}`, n.id);
|
|
2015
|
+
}
|
|
2016
|
+
const reinserted = [];
|
|
2017
|
+
const resurrected = [];
|
|
2018
|
+
for (const e of crossFileIncomingEdges) {
|
|
2019
|
+
const newTargetId = newNodesByKindName.get(`${e.targetKind}\0${e.targetName}`);
|
|
2020
|
+
if (newTargetId) {
|
|
2021
|
+
reinserted.push({ source: e.source, target: newTargetId, kind: e.kind, metadata: e.metadata, line: e.line, column: e.column, provenance: e.provenance });
|
|
2022
|
+
}
|
|
2023
|
+
else {
|
|
2024
|
+
const ref = resurrectRefFromDroppedEdge(e);
|
|
2025
|
+
if (ref)
|
|
2026
|
+
resurrected.push(ref);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
if (reinserted.length > 0) {
|
|
2030
|
+
this.queries.insertEdges(reinserted);
|
|
2031
|
+
}
|
|
2032
|
+
if (resurrected.length > 0) {
|
|
2033
|
+
this.queries.insertUnresolvedRefsBatch(resurrected);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
1864
2036
|
/**
|
|
1865
2037
|
* Sync the index with the current file state.
|
|
1866
2038
|
*
|
|
@@ -1869,7 +2041,7 @@ class ExtractionOrchestrator {
|
|
|
1869
2041
|
* changes. This works in non-git projects and catches committed changes from
|
|
1870
2042
|
* `git pull`/`checkout`/`merge`/`rebase` that `git status` cannot see.
|
|
1871
2043
|
*/
|
|
1872
|
-
async sync(onProgress) {
|
|
2044
|
+
async sync(onProgress, scopedPaths) {
|
|
1873
2045
|
await (0, grammars_1.initGrammars)(); // Initialize WASM runtime (grammars loaded lazily below)
|
|
1874
2046
|
const startTime = Date.now();
|
|
1875
2047
|
let filesChecked = 0;
|
|
@@ -1884,31 +2056,48 @@ class ExtractionOrchestrator {
|
|
|
1884
2056
|
total: 0,
|
|
1885
2057
|
});
|
|
1886
2058
|
const filesToIndex = [];
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
2059
|
+
let currentFiles;
|
|
2060
|
+
let trackedFiles;
|
|
2061
|
+
if (scopedPaths && scopedPaths.length > 0) {
|
|
2062
|
+
const unique = [...new Set(scopedPaths)];
|
|
2063
|
+
currentFiles = unique.filter((p) => fs.existsSync(path.join(this.rootDir, p)));
|
|
2064
|
+
trackedFiles = [];
|
|
2065
|
+
for (const p of unique) {
|
|
2066
|
+
const rec = this.queries.getFileByPath(p);
|
|
2067
|
+
if (rec)
|
|
2068
|
+
trackedFiles.push(rec);
|
|
2069
|
+
}
|
|
2070
|
+
filesChecked = unique.length;
|
|
2071
|
+
}
|
|
2072
|
+
else {
|
|
2073
|
+
currentFiles = await scanDirectoryAsync(this.rootDir);
|
|
2074
|
+
filesChecked = currentFiles.length;
|
|
2075
|
+
trackedFiles = this.queries.getAllFiles();
|
|
2076
|
+
}
|
|
1898
2077
|
const currentSet = new Set(currentFiles);
|
|
1899
|
-
const trackedFiles = this.queries.getAllFiles();
|
|
1900
2078
|
const trackedMap = new Map();
|
|
1901
2079
|
for (const f of trackedFiles) {
|
|
1902
2080
|
trackedMap.set(f.path, f);
|
|
1903
2081
|
}
|
|
1904
|
-
|
|
1905
|
-
// filesystem directly — `scanDirectory` (via `git ls-files`) still lists a
|
|
1906
|
-
// file deleted from disk but not yet staged, so set membership alone misses it.
|
|
1907
|
-
// `reconcileChecks` drives the cooperative yield shared with the adds/mods loop
|
|
1908
|
-
// below (see SYNC_RECONCILE_YIELD_INTERVAL / issue #905).
|
|
2082
|
+
const removedArkAnalyzerFiles = [];
|
|
1909
2083
|
let reconcileChecks = 0;
|
|
1910
2084
|
for (const tracked of trackedFiles) {
|
|
1911
2085
|
if (!currentSet.has(tracked.path) || !fs.existsSync(path.join(this.rootDir, tracked.path))) {
|
|
2086
|
+
if (tracked.path.startsWith('@')) {
|
|
2087
|
+
continue;
|
|
2088
|
+
}
|
|
2089
|
+
const incoming = this.queries.getCrossFileIncomingEdgesWithTarget(tracked.path);
|
|
2090
|
+
if (incoming.length > 0) {
|
|
2091
|
+
const resurrected = incoming
|
|
2092
|
+
.map((e) => resurrectRefFromDroppedEdge(e))
|
|
2093
|
+
.filter((r) => r !== null);
|
|
2094
|
+
if (resurrected.length > 0) {
|
|
2095
|
+
this.queries.insertUnresolvedRefsBatch(resurrected);
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
if ((0, arkts_1.isArkAnalyzerSourcePath)(tracked.path)) {
|
|
2099
|
+
removedArkAnalyzerFiles.push(tracked.path);
|
|
2100
|
+
}
|
|
1912
2101
|
this.queries.deleteFile(tracked.path);
|
|
1913
2102
|
filesRemoved++;
|
|
1914
2103
|
}
|
|
@@ -1918,10 +2107,12 @@ class ExtractionOrchestrator {
|
|
|
1918
2107
|
}
|
|
1919
2108
|
// Adds / modifications.
|
|
1920
2109
|
for (const filePath of currentFiles) {
|
|
1921
|
-
// Same cooperative yield as the removals loop — this is the other O(files)
|
|
1922
|
-
// synchronous-stat loop that wedges the main thread on a large repo (#905).
|
|
1923
|
-
// Yield at the top of the body so the `continue` fast-paths below still hit it.
|
|
1924
2110
|
if (++reconcileChecks % SYNC_RECONCILE_YIELD_INTERVAL === 0) {
|
|
2111
|
+
onProgress?.({
|
|
2112
|
+
phase: 'scanning',
|
|
2113
|
+
current: reconcileChecks,
|
|
2114
|
+
total: currentFiles.length,
|
|
2115
|
+
});
|
|
1925
2116
|
await new Promise((resolve) => setImmediate(resolve));
|
|
1926
2117
|
}
|
|
1927
2118
|
const fullPath = path.join(this.rootDir, filePath);
|
|
@@ -1978,7 +2169,19 @@ class ExtractionOrchestrator {
|
|
|
1978
2169
|
const total = filesToIndex.length;
|
|
1979
2170
|
const overrides = (0, project_config_1.loadExtensionOverrides)(this.rootDir);
|
|
1980
2171
|
const needsArkTS = filesToIndex.some((f) => (0, grammars_1.detectLanguage)(f, undefined, overrides) === 'arkts');
|
|
1981
|
-
|
|
2172
|
+
const arktsChangedForBatch = [
|
|
2173
|
+
...removedArkAnalyzerFiles,
|
|
2174
|
+
...filesToIndex.filter((f) => (0, arkts_1.isArkAnalyzerSourcePath)(f) ||
|
|
2175
|
+
f.endsWith('module.json5') ||
|
|
2176
|
+
f.endsWith('oh-package.json5') ||
|
|
2177
|
+
f === 'build-profile.json5' ||
|
|
2178
|
+
f.endsWith('/build-profile.json5')),
|
|
2179
|
+
];
|
|
2180
|
+
// Removals alone can still dirty a Harmony module (RTA for remaining files).
|
|
2181
|
+
const needsArkTSBatch = needsArkTS ||
|
|
2182
|
+
removedArkAnalyzerFiles.length > 0 ||
|
|
2183
|
+
arktsChangedForBatch.some((f) => f === 'build-profile.json5' || f.endsWith('/build-profile.json5'));
|
|
2184
|
+
if (needsArkTSBatch && arktsChangedForBatch.length > 0) {
|
|
1982
2185
|
(0, context_1.setArkTSBatchProgressCallback)((batchProgress) => {
|
|
1983
2186
|
onProgress?.({
|
|
1984
2187
|
phase: 'arkts-batch',
|
|
@@ -1988,8 +2191,12 @@ class ExtractionOrchestrator {
|
|
|
1988
2191
|
subphase: batchProgress.subphase,
|
|
1989
2192
|
});
|
|
1990
2193
|
});
|
|
1991
|
-
const firstEts = filesToIndex.find((f) => (0, grammars_1.detectLanguage)(f, undefined, overrides) === 'arkts')
|
|
1992
|
-
|
|
2194
|
+
const firstEts = filesToIndex.find((f) => (0, grammars_1.detectLanguage)(f, undefined, overrides) === 'arkts') ??
|
|
2195
|
+
arktsChangedForBatch.find((f) => (0, arkts_1.isArkAnalyzerSourcePath)(f)) ??
|
|
2196
|
+
arktsChangedForBatch[0];
|
|
2197
|
+
await (0, arkts_1.primeArkTSBatch)(this.rootDir, this.queries, firstEts, {
|
|
2198
|
+
changedFiles: arktsChangedForBatch,
|
|
2199
|
+
});
|
|
1993
2200
|
}
|
|
1994
2201
|
try {
|
|
1995
2202
|
for (let i = 0; i < filesToIndex.length; i++) {
|
|
@@ -2014,7 +2221,7 @@ class ExtractionOrchestrator {
|
|
|
2014
2221
|
}
|
|
2015
2222
|
}
|
|
2016
2223
|
finally {
|
|
2017
|
-
if (
|
|
2224
|
+
if (needsArkTSBatch) {
|
|
2018
2225
|
(0, context_1.setArkTSBatchProgressCallback)(null);
|
|
2019
2226
|
}
|
|
2020
2227
|
}
|