@saluzi/codegraph 0.1.0 → 0.2.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/LICENSE +21 -0
- package/README.md +885 -0
- package/dist/bin/codegraph.d.ts +2 -1
- package/dist/bin/codegraph.d.ts.map +1 -0
- package/dist/bin/codegraph.js +2248 -0
- package/dist/bin/codegraph.js.map +1 -0
- package/dist/bin/command-supervision.d.ts +49 -0
- package/dist/bin/command-supervision.d.ts.map +1 -0
- package/dist/bin/command-supervision.js +95 -0
- package/dist/bin/command-supervision.js.map +1 -0
- package/dist/bin/fatal-handler.d.ts +20 -0
- package/dist/bin/fatal-handler.d.ts.map +1 -0
- package/dist/bin/fatal-handler.js +118 -0
- package/dist/bin/fatal-handler.js.map +1 -0
- package/dist/bin/node-version-check.d.ts.map +1 -0
- package/dist/bin/node-version-check.js +79 -0
- package/dist/bin/node-version-check.js.map +1 -0
- package/dist/bin/uninstall.d.ts.map +1 -0
- package/dist/bin/uninstall.js +36 -0
- package/dist/bin/uninstall.js.map +1 -0
- package/dist/context/formatter.d.ts +6 -9
- package/dist/context/formatter.d.ts.map +1 -0
- package/dist/context/formatter.js +25 -6
- package/dist/context/formatter.js.map +1 -0
- package/dist/context/index.d.ts +102 -100
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +252 -6
- package/dist/context/index.js.map +1 -0
- package/dist/context/markers.d.ts +19 -0
- package/dist/context/markers.d.ts.map +1 -0
- package/dist/context/markers.js +22 -0
- package/dist/context/markers.js.map +1 -0
- package/dist/db/index.d.ts +185 -85
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +257 -14
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrations.d.ts +15 -18
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +82 -1
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/queries.d.ts +503 -281
- package/dist/db/queries.d.ts.map +1 -0
- package/dist/db/queries.js +644 -107
- package/dist/db/queries.js.map +1 -0
- package/dist/db/schema.sql +69 -26
- package/dist/db/sqlite-adapter.d.ts +26 -22
- package/dist/db/sqlite-adapter.d.ts.map +1 -0
- package/dist/db/sqlite-adapter.js +11 -35
- package/dist/db/sqlite-adapter.js.map +1 -0
- package/dist/db/wal-valve.d.ts +106 -0
- package/dist/db/wal-valve.d.ts.map +1 -0
- package/dist/db/wal-valve.js +208 -0
- package/dist/db/wal-valve.js.map +1 -0
- package/dist/directory.d.ts +140 -19
- package/dist/directory.d.ts.map +1 -0
- package/dist/directory.js +568 -35
- package/dist/directory.js.map +1 -0
- package/dist/errors.d.ts +32 -45
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js.map +1 -0
- package/dist/extraction/astro-extractor.d.ts +79 -0
- package/dist/extraction/astro-extractor.d.ts.map +1 -0
- package/dist/extraction/astro-extractor.js +320 -0
- package/dist/extraction/astro-extractor.js.map +1 -0
- 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/dfm-extractor.d.ts +17 -17
- package/dist/extraction/dfm-extractor.d.ts.map +1 -0
- package/dist/extraction/dfm-extractor.js.map +1 -0
- package/dist/extraction/extraction-version.d.ts +25 -0
- package/dist/extraction/extraction-version.d.ts.map +1 -0
- package/dist/extraction/extraction-version.js +28 -0
- package/dist/extraction/extraction-version.js.map +1 -0
- package/dist/extraction/function-ref.d.ts +118 -0
- package/dist/extraction/function-ref.d.ts.map +1 -0
- package/dist/extraction/function-ref.js +727 -0
- package/dist/extraction/function-ref.js.map +1 -0
- package/dist/extraction/generated-detection.d.ts +30 -0
- package/dist/extraction/generated-detection.d.ts.map +1 -0
- package/dist/extraction/generated-detection.js +83 -0
- package/dist/extraction/generated-detection.js.map +1 -0
- package/dist/extraction/grammars.d.ts +74 -30
- package/dist/extraction/grammars.d.ts.map +1 -0
- package/dist/extraction/grammars.js +289 -15
- package/dist/extraction/grammars.js.map +1 -0
- package/dist/extraction/index.d.ts +193 -109
- package/dist/extraction/index.d.ts.map +1 -0
- package/dist/extraction/index.js +1485 -362
- package/dist/extraction/index.js.map +1 -0
- package/dist/extraction/languages/arkts.d.ts +3 -0
- package/dist/extraction/languages/arkts.d.ts.map +1 -0
- package/dist/extraction/languages/arkts.js +127 -0
- package/dist/extraction/languages/arkts.js.map +1 -0
- package/dist/extraction/languages/c-cpp.d.ts +110 -4
- package/dist/extraction/languages/c-cpp.d.ts.map +1 -0
- package/dist/extraction/languages/c-cpp.js +713 -2
- package/dist/extraction/languages/c-cpp.js.map +1 -0
- 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/csharp.d.ts +25 -3
- package/dist/extraction/languages/csharp.d.ts.map +1 -0
- package/dist/extraction/languages/csharp.js +106 -3
- package/dist/extraction/languages/csharp.js.map +1 -0
- package/dist/extraction/languages/dart.d.ts +3 -3
- package/dist/extraction/languages/dart.d.ts.map +1 -0
- package/dist/extraction/languages/dart.js +183 -1
- package/dist/extraction/languages/dart.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/go.d.ts +3 -3
- package/dist/extraction/languages/go.d.ts.map +1 -0
- package/dist/extraction/languages/go.js +55 -2
- package/dist/extraction/languages/go.js.map +1 -0
- package/dist/extraction/languages/index.d.ts +4 -4
- package/dist/extraction/languages/index.d.ts.map +1 -0
- package/dist/extraction/languages/index.js +22 -0
- package/dist/extraction/languages/index.js.map +1 -0
- package/dist/extraction/languages/java.d.ts +3 -3
- package/dist/extraction/languages/java.d.ts.map +1 -0
- package/dist/extraction/languages/java.js +252 -1
- package/dist/extraction/languages/java.js.map +1 -0
- package/dist/extraction/languages/javascript.d.ts +3 -3
- package/dist/extraction/languages/javascript.d.ts.map +1 -0
- package/dist/extraction/languages/javascript.js +16 -0
- package/dist/extraction/languages/javascript.js.map +1 -0
- package/dist/extraction/languages/kotlin.d.ts +3 -3
- package/dist/extraction/languages/kotlin.d.ts.map +1 -0
- package/dist/extraction/languages/kotlin.js +126 -0
- package/dist/extraction/languages/kotlin.js.map +1 -0
- package/dist/extraction/languages/lua.d.ts +3 -3
- package/dist/extraction/languages/lua.d.ts.map +1 -0
- package/dist/extraction/languages/lua.js.map +1 -0
- package/dist/extraction/languages/luau.d.ts +3 -3
- package/dist/extraction/languages/luau.d.ts.map +1 -0
- package/dist/extraction/languages/luau.js.map +1 -0
- 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/objc.d.ts +3 -0
- package/dist/extraction/languages/objc.d.ts.map +1 -0
- package/dist/extraction/languages/objc.js +175 -0
- package/dist/extraction/languages/objc.js.map +1 -0
- package/dist/extraction/languages/pascal.d.ts +3 -3
- package/dist/extraction/languages/pascal.d.ts.map +1 -0
- package/dist/extraction/languages/pascal.js +11 -0
- package/dist/extraction/languages/pascal.js.map +1 -0
- package/dist/extraction/languages/php.d.ts +3 -3
- package/dist/extraction/languages/php.d.ts.map +1 -0
- package/dist/extraction/languages/php.js +90 -1
- package/dist/extraction/languages/php.js.map +1 -0
- package/dist/extraction/languages/python.d.ts +3 -3
- package/dist/extraction/languages/python.d.ts.map +1 -0
- package/dist/extraction/languages/python.js.map +1 -0
- package/dist/extraction/languages/r.d.ts +3 -0
- package/dist/extraction/languages/r.d.ts.map +1 -0
- package/dist/extraction/languages/r.js +314 -0
- package/dist/extraction/languages/r.js.map +1 -0
- package/dist/extraction/languages/ruby.d.ts +3 -3
- package/dist/extraction/languages/ruby.d.ts.map +1 -0
- package/dist/extraction/languages/ruby.js +35 -0
- package/dist/extraction/languages/ruby.js.map +1 -0
- package/dist/extraction/languages/rust.d.ts +3 -3
- package/dist/extraction/languages/rust.d.ts.map +1 -0
- package/dist/extraction/languages/rust.js +35 -2
- package/dist/extraction/languages/rust.js.map +1 -0
- package/dist/extraction/languages/scala.d.ts +3 -3
- package/dist/extraction/languages/scala.d.ts.map +1 -0
- package/dist/extraction/languages/scala.js +80 -10
- package/dist/extraction/languages/scala.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/swift.d.ts +3 -3
- package/dist/extraction/languages/swift.d.ts.map +1 -0
- package/dist/extraction/languages/swift.js +61 -0
- package/dist/extraction/languages/swift.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/typescript.d.ts +16 -3
- package/dist/extraction/languages/typescript.d.ts.map +1 -0
- package/dist/extraction/languages/typescript.js +38 -0
- package/dist/extraction/languages/typescript.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/liquid-extractor.d.ts +48 -41
- package/dist/extraction/liquid-extractor.d.ts.map +1 -0
- package/dist/extraction/liquid-extractor.js +53 -9
- package/dist/extraction/liquid-extractor.js.map +1 -0
- package/dist/extraction/mybatis-extractor.d.ts +68 -0
- package/dist/extraction/mybatis-extractor.d.ts.map +1 -0
- package/dist/extraction/mybatis-extractor.js +300 -0
- package/dist/extraction/mybatis-extractor.js.map +1 -0
- package/dist/extraction/parse-pool.d.ts +152 -0
- package/dist/extraction/parse-pool.d.ts.map +1 -0
- package/dist/extraction/parse-pool.js +387 -0
- package/dist/extraction/parse-pool.js.map +1 -0
- package/dist/extraction/parse-worker.d.ts +2 -2
- package/dist/extraction/parse-worker.d.ts.map +1 -0
- package/dist/extraction/parse-worker.js +13 -3
- package/dist/extraction/parse-worker.js.map +1 -0
- package/dist/extraction/razor-extractor.d.ts +42 -0
- package/dist/extraction/razor-extractor.d.ts.map +1 -0
- package/dist/extraction/razor-extractor.js +285 -0
- package/dist/extraction/razor-extractor.js.map +1 -0
- package/dist/extraction/svelte-extractor.d.ts +42 -42
- package/dist/extraction/svelte-extractor.d.ts.map +1 -0
- package/dist/extraction/svelte-extractor.js +6 -3
- package/dist/extraction/svelte-extractor.js.map +1 -0
- package/dist/extraction/tree-sitter-helpers.d.ts +7 -18
- package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -0
- package/dist/extraction/tree-sitter-helpers.js +60 -10
- package/dist/extraction/tree-sitter-helpers.js.map +1 -0
- package/dist/extraction/tree-sitter-types.d.ts +224 -157
- package/dist/extraction/tree-sitter-types.d.ts.map +1 -0
- package/dist/extraction/tree-sitter-types.js.map +1 -0
- package/dist/extraction/tree-sitter.d.ts +689 -221
- package/dist/extraction/tree-sitter.d.ts.map +1 -0
- package/dist/extraction/tree-sitter.js +4193 -117
- package/dist/extraction/tree-sitter.js.map +1 -0
- package/dist/extraction/vue-extractor.d.ts +40 -25
- package/dist/extraction/vue-extractor.d.ts.map +1 -0
- package/dist/extraction/vue-extractor.js +94 -3
- package/dist/extraction/vue-extractor.js.map +1 -0
- package/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-c_sharp.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-lua.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-scala.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 +6 -14
- package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
- package/dist/extraction/wasm-runtime-flags.js +1 -0
- package/dist/extraction/wasm-runtime-flags.js.map +1 -0
- package/dist/graph/index.d.ts +3 -3
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/queries.d.ts +96 -99
- package/dist/graph/queries.d.ts.map +1 -0
- package/dist/graph/queries.js +13 -40
- package/dist/graph/queries.js.map +1 -0
- package/dist/graph/traversal.d.ts +117 -127
- package/dist/graph/traversal.d.ts.map +1 -0
- package/dist/graph/traversal.js +92 -21
- package/dist/graph/traversal.js.map +1 -0
- package/dist/index.d.ts +615 -481
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +709 -18
- package/dist/index.js.map +1 -0
- package/dist/installer/config-writer.d.ts +13 -14
- package/dist/installer/config-writer.d.ts.map +1 -0
- package/dist/installer/config-writer.js +91 -0
- package/dist/installer/config-writer.js.map +1 -0
- package/dist/installer/index.d.ts +84 -82
- package/dist/installer/index.d.ts.map +1 -0
- package/dist/installer/index.js +619 -0
- package/dist/installer/index.js.map +1 -0
- package/dist/installer/instructions-template.d.ts +35 -24
- package/dist/installer/instructions-template.d.ts.map +1 -0
- package/dist/installer/instructions-template.js +53 -0
- package/dist/installer/instructions-template.js.map +1 -0
- package/dist/installer/targets/antigravity.d.ts +57 -0
- package/dist/installer/targets/antigravity.d.ts.map +1 -0
- package/dist/installer/targets/antigravity.js +308 -0
- package/dist/installer/targets/antigravity.js.map +1 -0
- package/dist/installer/targets/claude.d.ts +34 -27
- package/dist/installer/targets/claude.d.ts.map +1 -0
- package/dist/installer/targets/claude.js +454 -0
- package/dist/installer/targets/claude.js.map +1 -0
- package/dist/installer/targets/codex.d.ts +3 -3
- package/dist/installer/targets/codex.d.ts.map +1 -0
- package/dist/installer/targets/codex.js +185 -0
- package/dist/installer/targets/codex.js.map +1 -0
- package/dist/installer/targets/cursor.d.ts +3 -3
- package/dist/installer/targets/cursor.d.ts.map +1 -0
- package/dist/installer/targets/cursor.js +254 -0
- package/dist/installer/targets/cursor.js.map +1 -0
- package/dist/installer/targets/gemini.d.ts +26 -0
- package/dist/installer/targets/gemini.d.ts.map +1 -0
- package/dist/installer/targets/gemini.js +165 -0
- package/dist/installer/targets/gemini.js.map +1 -0
- package/dist/installer/targets/hermes.d.ts +3 -3
- package/dist/installer/targets/hermes.d.ts.map +1 -0
- package/dist/installer/targets/hermes.js +359 -0
- package/dist/installer/targets/hermes.js.map +1 -0
- package/dist/installer/targets/kiro.d.ts +27 -0
- package/dist/installer/targets/kiro.d.ts.map +1 -0
- package/dist/installer/targets/kiro.js +178 -0
- package/dist/installer/targets/kiro.js.map +1 -0
- package/dist/installer/targets/opencode.d.ts +12 -4
- package/dist/installer/targets/opencode.d.ts.map +1 -0
- package/dist/installer/targets/opencode.js +288 -0
- package/dist/installer/targets/opencode.js.map +1 -0
- package/dist/installer/targets/registry.d.ts +9 -12
- package/dist/installer/targets/registry.d.ts.map +1 -0
- package/dist/installer/targets/registry.js +91 -0
- package/dist/installer/targets/registry.js.map +1 -0
- package/dist/installer/targets/shared.d.ts +38 -29
- package/dist/installer/targets/shared.d.ts.map +1 -0
- package/dist/installer/targets/shared.js +264 -0
- package/dist/installer/targets/shared.js.map +1 -0
- package/dist/installer/targets/toml.d.ts +11 -23
- package/dist/installer/targets/toml.d.ts.map +1 -0
- package/dist/installer/targets/toml.js +147 -0
- package/dist/installer/targets/toml.js.map +1 -0
- package/dist/installer/targets/types.d.ts +62 -76
- package/dist/installer/targets/types.d.ts.map +1 -0
- package/dist/installer/targets/types.js +16 -0
- package/dist/installer/targets/types.js.map +1 -0
- package/dist/mcp/daemon-manager.d.ts +42 -0
- package/dist/mcp/daemon-manager.d.ts.map +1 -0
- package/dist/mcp/daemon-manager.js +129 -0
- package/dist/mcp/daemon-manager.js.map +1 -0
- package/dist/mcp/daemon-paths.d.ts +73 -0
- package/dist/mcp/daemon-paths.d.ts.map +1 -0
- package/dist/mcp/daemon-paths.js +165 -0
- package/dist/mcp/daemon-paths.js.map +1 -0
- package/dist/mcp/daemon-registry.d.ts +47 -0
- package/dist/mcp/daemon-registry.d.ts.map +1 -0
- package/dist/mcp/daemon-registry.js +233 -0
- package/dist/mcp/daemon-registry.js.map +1 -0
- package/dist/mcp/daemon.d.ts +290 -0
- package/dist/mcp/daemon.d.ts.map +1 -0
- package/dist/mcp/daemon.js +862 -0
- package/dist/mcp/daemon.js.map +1 -0
- package/dist/mcp/dynamic-boundaries.d.ts +41 -0
- package/dist/mcp/dynamic-boundaries.d.ts.map +1 -0
- package/dist/mcp/dynamic-boundaries.js +360 -0
- package/dist/mcp/dynamic-boundaries.js.map +1 -0
- 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 +122 -0
- package/dist/mcp/engine.d.ts.map +1 -0
- package/dist/mcp/engine.js +350 -0
- package/dist/mcp/engine.js.map +1 -0
- package/dist/mcp/index.d.ts +89 -74
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +506 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/liveness-watchdog.d.ts +35 -0
- package/dist/mcp/liveness-watchdog.d.ts.map +1 -0
- package/dist/mcp/liveness-watchdog.js +267 -0
- package/dist/mcp/liveness-watchdog.js.map +1 -0
- package/dist/mcp/ppid-watchdog.d.ts +62 -0
- package/dist/mcp/ppid-watchdog.d.ts.map +1 -0
- package/dist/mcp/ppid-watchdog.js +64 -0
- package/dist/mcp/ppid-watchdog.js.map +1 -0
- package/dist/mcp/proxy.d.ts +87 -0
- package/dist/mcp/proxy.d.ts.map +1 -0
- package/dist/mcp/proxy.js +667 -0
- package/dist/mcp/proxy.js.map +1 -0
- package/dist/mcp/query-pool.d.ts +108 -0
- package/dist/mcp/query-pool.d.ts.map +1 -0
- package/dist/mcp/query-pool.js +315 -0
- package/dist/mcp/query-pool.js.map +1 -0
- package/dist/mcp/query-worker.d.ts +24 -0
- package/dist/mcp/query-worker.d.ts.map +1 -0
- package/dist/mcp/query-worker.js +87 -0
- package/dist/mcp/query-worker.js.map +1 -0
- package/dist/mcp/server-instructions.d.ts +22 -8
- package/dist/mcp/server-instructions.d.ts.map +1 -0
- package/dist/mcp/server-instructions.js +106 -0
- package/dist/mcp/server-instructions.js.map +1 -0
- package/dist/mcp/session.d.ts +93 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +357 -0
- package/dist/mcp/session.js.map +1 -0
- 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/stdin-teardown.d.ts +27 -0
- package/dist/mcp/stdin-teardown.d.ts.map +1 -0
- package/dist/mcp/stdin-teardown.js +49 -0
- package/dist/mcp/stdin-teardown.js.map +1 -0
- package/dist/mcp/tools.d.ts +557 -213
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +4477 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/transport.d.ts +158 -87
- package/dist/mcp/transport.d.ts.map +1 -0
- package/dist/mcp/transport.js +377 -0
- package/dist/mcp/transport.js.map +1 -0
- package/dist/mcp/version.d.ts +19 -0
- package/dist/mcp/version.d.ts.map +1 -0
- package/dist/mcp/version.js +71 -0
- package/dist/mcp/version.js.map +1 -0
- package/dist/project-config.d.ts +94 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +374 -0
- package/dist/project-config.js.map +1 -0
- package/dist/resolution/c-fnptr-synthesizer.d.ts +6 -0
- package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -0
- package/dist/resolution/c-fnptr-synthesizer.js +1062 -0
- package/dist/resolution/c-fnptr-synthesizer.js.map +1 -0
- package/dist/resolution/callback-synthesizer.d.ts +15 -0
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -0
- package/dist/resolution/callback-synthesizer.js +3938 -0
- package/dist/resolution/callback-synthesizer.js.map +1 -0
- package/dist/resolution/cooperative-yield.d.ts +32 -0
- package/dist/resolution/cooperative-yield.d.ts.map +1 -0
- package/dist/resolution/cooperative-yield.js +42 -0
- package/dist/resolution/cooperative-yield.js.map +1 -0
- package/dist/resolution/frameworks/astro.d.ts +9 -0
- package/dist/resolution/frameworks/astro.d.ts.map +1 -0
- package/dist/resolution/frameworks/astro.js +169 -0
- package/dist/resolution/frameworks/astro.js.map +1 -0
- package/dist/resolution/frameworks/cargo-workspace.d.ts +3 -5
- package/dist/resolution/frameworks/cargo-workspace.d.ts.map +1 -0
- package/dist/resolution/frameworks/cargo-workspace.js.map +1 -0
- 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/csharp.d.ts +3 -3
- package/dist/resolution/frameworks/csharp.d.ts.map +1 -0
- package/dist/resolution/frameworks/csharp.js +36 -8
- package/dist/resolution/frameworks/csharp.js.map +1 -0
- package/dist/resolution/frameworks/drupal.d.ts +3 -3
- package/dist/resolution/frameworks/drupal.d.ts.map +1 -0
- package/dist/resolution/frameworks/drupal.js +44 -12
- package/dist/resolution/frameworks/drupal.js.map +1 -0
- package/dist/resolution/frameworks/expo-modules.d.ts +3 -0
- package/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
- package/dist/resolution/frameworks/expo-modules.js +148 -0
- package/dist/resolution/frameworks/expo-modules.js.map +1 -0
- package/dist/resolution/frameworks/express.d.ts +3 -3
- package/dist/resolution/frameworks/express.d.ts.map +1 -0
- package/dist/resolution/frameworks/express.js +102 -19
- package/dist/resolution/frameworks/express.js.map +1 -0
- package/dist/resolution/frameworks/fabric.d.ts +3 -0
- package/dist/resolution/frameworks/fabric.d.ts.map +1 -0
- package/dist/resolution/frameworks/fabric.js +354 -0
- package/dist/resolution/frameworks/fabric.js.map +1 -0
- package/dist/resolution/frameworks/go.d.ts +3 -3
- package/dist/resolution/frameworks/go.d.ts.map +1 -0
- package/dist/resolution/frameworks/go.js +6 -3
- package/dist/resolution/frameworks/go.js.map +1 -0
- package/dist/resolution/frameworks/goframe.d.ts +41 -0
- package/dist/resolution/frameworks/goframe.d.ts.map +1 -0
- package/dist/resolution/frameworks/goframe.js +112 -0
- package/dist/resolution/frameworks/goframe.js.map +1 -0
- package/dist/resolution/frameworks/index.d.ts +29 -31
- package/dist/resolution/frameworks/index.d.ts.map +1 -0
- package/dist/resolution/frameworks/index.js +39 -1
- package/dist/resolution/frameworks/index.js.map +1 -0
- package/dist/resolution/frameworks/java.d.ts +3 -3
- package/dist/resolution/frameworks/java.d.ts.map +1 -0
- package/dist/resolution/frameworks/java.js +352 -12
- package/dist/resolution/frameworks/java.js.map +1 -0
- package/dist/resolution/frameworks/laravel.d.ts +4 -4
- package/dist/resolution/frameworks/laravel.d.ts.map +1 -0
- package/dist/resolution/frameworks/laravel.js +17 -8
- package/dist/resolution/frameworks/laravel.js.map +1 -0
- package/dist/resolution/frameworks/nestjs.d.ts +3 -3
- package/dist/resolution/frameworks/nestjs.d.ts.map +1 -0
- package/dist/resolution/frameworks/nestjs.js +324 -0
- package/dist/resolution/frameworks/nestjs.js.map +1 -0
- package/dist/resolution/frameworks/play.d.ts +19 -0
- package/dist/resolution/frameworks/play.d.ts.map +1 -0
- package/dist/resolution/frameworks/play.js +111 -0
- package/dist/resolution/frameworks/play.js.map +1 -0
- package/dist/resolution/frameworks/python.d.ts +5 -5
- package/dist/resolution/frameworks/python.d.ts.map +1 -0
- package/dist/resolution/frameworks/python.js +138 -16
- package/dist/resolution/frameworks/python.js.map +1 -0
- package/dist/resolution/frameworks/react-native.d.ts +3 -0
- package/dist/resolution/frameworks/react-native.d.ts.map +1 -0
- package/dist/resolution/frameworks/react-native.js +410 -0
- package/dist/resolution/frameworks/react-native.js.map +1 -0
- package/dist/resolution/frameworks/react.d.ts +3 -3
- package/dist/resolution/frameworks/react.d.ts.map +1 -0
- package/dist/resolution/frameworks/react.js +122 -60
- package/dist/resolution/frameworks/react.js.map +1 -0
- package/dist/resolution/frameworks/ruby.d.ts +3 -3
- package/dist/resolution/frameworks/ruby.d.ts.map +1 -0
- package/dist/resolution/frameworks/ruby.js +106 -2
- package/dist/resolution/frameworks/ruby.js.map +1 -0
- package/dist/resolution/frameworks/rust.d.ts +3 -3
- package/dist/resolution/frameworks/rust.d.ts.map +1 -0
- package/dist/resolution/frameworks/rust.js +102 -5
- package/dist/resolution/frameworks/rust.js.map +1 -0
- package/dist/resolution/frameworks/svelte.d.ts +3 -3
- package/dist/resolution/frameworks/svelte.d.ts.map +1 -0
- package/dist/resolution/frameworks/svelte.js +5 -1
- package/dist/resolution/frameworks/svelte.js.map +1 -0
- package/dist/resolution/frameworks/swift-objc.d.ts +37 -0
- package/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
- package/dist/resolution/frameworks/swift-objc.js +252 -0
- package/dist/resolution/frameworks/swift-objc.js.map +1 -0
- package/dist/resolution/frameworks/swift.d.ts +5 -5
- package/dist/resolution/frameworks/swift.d.ts.map +1 -0
- package/dist/resolution/frameworks/swift.js +30 -6
- package/dist/resolution/frameworks/swift.js.map +1 -0
- 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/frameworks/vue.d.ts +3 -3
- package/dist/resolution/frameworks/vue.d.ts.map +1 -0
- package/dist/resolution/frameworks/vue.js +24 -27
- package/dist/resolution/frameworks/vue.js.map +1 -0
- package/dist/resolution/go-module.d.ts +26 -0
- package/dist/resolution/go-module.d.ts.map +1 -0
- package/dist/resolution/go-module.js +78 -0
- package/dist/resolution/go-module.js.map +1 -0
- package/dist/resolution/goframe-synthesizer.d.ts +29 -0
- package/dist/resolution/goframe-synthesizer.d.ts.map +1 -0
- package/dist/resolution/goframe-synthesizer.js +163 -0
- package/dist/resolution/goframe-synthesizer.js.map +1 -0
- package/dist/resolution/import-resolver.d.ts +53 -29
- package/dist/resolution/import-resolver.d.ts.map +1 -0
- package/dist/resolution/import-resolver.js +1319 -8
- package/dist/resolution/import-resolver.js.map +1 -0
- package/dist/resolution/index.d.ts +243 -103
- package/dist/resolution/index.d.ts.map +1 -0
- package/dist/resolution/index.js +1055 -53
- package/dist/resolution/index.js.map +1 -0
- package/dist/resolution/lru-cache.d.ts +9 -9
- package/dist/resolution/lru-cache.d.ts.map +1 -0
- package/dist/resolution/lru-cache.js.map +1 -0
- package/dist/resolution/name-matcher.d.ts +91 -29
- package/dist/resolution/name-matcher.d.ts.map +1 -0
- package/dist/resolution/name-matcher.js +1478 -32
- package/dist/resolution/name-matcher.js.map +1 -0
- package/dist/resolution/path-aliases.d.ts +23 -27
- package/dist/resolution/path-aliases.d.ts.map +1 -0
- package/dist/resolution/path-aliases.js.map +1 -0
- package/dist/resolution/strip-comments.d.ts +3 -16
- package/dist/resolution/strip-comments.d.ts.map +1 -0
- package/dist/resolution/strip-comments.js +51 -0
- package/dist/resolution/strip-comments.js.map +1 -0
- package/dist/resolution/swift-objc-bridge.d.ts +134 -0
- package/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
- package/dist/resolution/swift-objc-bridge.js +256 -0
- package/dist/resolution/swift-objc-bridge.js.map +1 -0
- package/dist/resolution/types.d.ts +217 -136
- package/dist/resolution/types.d.ts.map +1 -0
- package/dist/resolution/types.js.map +1 -0
- package/dist/resolution/workspace-packages.d.ts +58 -0
- package/dist/resolution/workspace-packages.d.ts.map +1 -0
- package/dist/resolution/workspace-packages.js +346 -0
- package/dist/resolution/workspace-packages.js.map +1 -0
- 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-parser.d.ts +14 -18
- package/dist/search/query-parser.d.ts.map +1 -0
- package/dist/search/query-parser.js.map +1 -0
- package/dist/search/query-utils.d.ts +45 -17
- package/dist/search/query-utils.d.ts.map +1 -0
- package/dist/search/query-utils.js +113 -47
- package/dist/search/query-utils.js.map +1 -0
- package/dist/sync/git-hooks.d.ts +13 -22
- package/dist/sync/git-hooks.d.ts.map +1 -0
- package/dist/sync/git-hooks.js +4 -0
- package/dist/sync/git-hooks.js.map +1 -0
- package/dist/sync/index.d.ts +6 -12
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +8 -1
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/watch-policy.d.ts +8 -11
- package/dist/sync/watch-policy.d.ts.map +1 -0
- package/dist/sync/watch-policy.js.map +1 -0
- package/dist/sync/watcher.d.ts +335 -63
- package/dist/sync/watcher.d.ts.map +1 -0
- package/dist/sync/watcher.js +709 -53
- package/dist/sync/watcher.js.map +1 -0
- package/dist/sync/worktree.d.ts +63 -0
- package/dist/sync/worktree.d.ts.map +1 -0
- package/dist/sync/worktree.js +182 -0
- package/dist/sync/worktree.js.map +1 -0
- package/dist/telemetry/index.d.ts +143 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +541 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/types.d.ts +308 -326
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/glyphs.d.ts +20 -20
- package/dist/ui/glyphs.d.ts.map +1 -0
- package/dist/ui/glyphs.js +78 -0
- package/dist/ui/glyphs.js.map +1 -0
- package/dist/ui/shimmer-progress.d.ts +7 -7
- package/dist/ui/shimmer-progress.d.ts.map +1 -0
- package/dist/ui/shimmer-progress.js +90 -0
- package/dist/ui/shimmer-progress.js.map +1 -0
- package/dist/ui/shimmer-worker.d.ts +2 -2
- package/dist/ui/shimmer-worker.d.ts.map +1 -0
- package/dist/ui/shimmer-worker.js +118 -0
- package/dist/ui/shimmer-worker.js.map +1 -0
- package/dist/ui/types.d.ts +14 -17
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +3 -0
- package/dist/ui/types.js.map +1 -0
- package/dist/upgrade/index.d.ts +164 -0
- package/dist/upgrade/index.d.ts.map +1 -0
- package/dist/upgrade/index.js +646 -0
- package/dist/upgrade/index.js.map +1 -0
- 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/dist/utils.d.ts +116 -123
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +83 -49
- package/dist/utils.js.map +1 -0
- package/package.json +42 -10
- package/scripts/add-lang/bench.sh +60 -0
- package/scripts/add-lang/check-grammar.mjs +75 -0
- package/scripts/add-lang/dump-ast.mjs +103 -0
- package/scripts/add-lang/verify-extraction.mjs +70 -0
- package/scripts/agent-eval/ab-adoption.sh +91 -0
- package/scripts/agent-eval/ab-hook.sh +86 -0
- package/scripts/agent-eval/ab-impl.sh +78 -0
- package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -0
- package/scripts/agent-eval/ab-sufficiency.sh +78 -0
- package/scripts/agent-eval/arms-F.sh +21 -0
- package/scripts/agent-eval/arms-matrix.sh +37 -0
- package/scripts/agent-eval/audit.sh +68 -0
- package/scripts/agent-eval/bench-readme.sh +28 -0
- package/scripts/agent-eval/bench-why-repo.sh +22 -0
- package/scripts/agent-eval/block-read-hook.sh +19 -0
- package/scripts/agent-eval/hook-settings.json +15 -0
- package/scripts/agent-eval/itrun.sh +120 -0
- package/scripts/agent-eval/offload-eval-3arm.sh +72 -0
- package/scripts/agent-eval/offload-eval-cost.mjs +133 -0
- package/scripts/agent-eval/offload-eval-effort.mjs +108 -0
- package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -0
- package/scripts/agent-eval/offload-eval-frontload.sh +47 -0
- package/scripts/agent-eval/offload-eval-ground-truth.json +18 -0
- package/scripts/agent-eval/offload-eval-hook.mjs +84 -0
- package/scripts/agent-eval/offload-eval-judge.mjs +103 -0
- package/scripts/agent-eval/offload-eval-matrix.sh +20 -0
- package/scripts/agent-eval/offload-eval-metrics.mjs +94 -0
- package/scripts/agent-eval/offload-eval-refs1.sh +50 -0
- package/scripts/agent-eval/offload-eval-setup.sh +24 -0
- package/scripts/agent-eval/offload-eval-styles.sh +72 -0
- package/scripts/agent-eval/offload-eval-summarize.mjs +68 -0
- package/scripts/agent-eval/offload-eval.md +76 -0
- package/scripts/agent-eval/parse-arms.mjs +116 -0
- package/scripts/agent-eval/parse-bench-readme.mjs +84 -0
- package/scripts/agent-eval/parse-run.mjs +45 -0
- package/scripts/agent-eval/parse-session.mjs +93 -0
- package/scripts/agent-eval/probe-context.mjs +21 -0
- package/scripts/agent-eval/probe-explore.mjs +40 -0
- package/scripts/agent-eval/probe-node.mjs +20 -0
- package/scripts/agent-eval/probe-sweep.mjs +119 -0
- package/scripts/agent-eval/probe-trace.mjs +20 -0
- package/scripts/agent-eval/redirect-read-hook.sh +38 -0
- package/scripts/agent-eval/repro-concurrent-explore.mjs +119 -0
- package/scripts/agent-eval/repro-daemon-clients.mjs +125 -0
- package/scripts/agent-eval/run-agent.sh +34 -0
- package/scripts/agent-eval/run-all.sh +75 -0
- package/scripts/agent-eval/run-arms.sh +56 -0
- package/scripts/agent-eval/seq-matrix.mjs +137 -0
- package/scripts/build-bundle.sh +123 -0
- package/scripts/extract-release-notes.mjs +130 -0
- package/scripts/local-install.sh +41 -0
- package/scripts/npm-sdk.js +75 -0
- package/scripts/npm-shim.js +275 -0
- package/scripts/pack-npm.sh +119 -0
- package/scripts/prepare-release.mjs +270 -0
- package/dist/extraction/wasm/tree-sitter-c.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cpp.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-dart.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-kotlin.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-php.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-ruby.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-rust.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-swift.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
- package/dist/installer/claude-md-template.d.ts +0 -19
package/dist/db/queries.js
CHANGED
|
@@ -9,6 +9,42 @@ exports.QueryBuilder = void 0;
|
|
|
9
9
|
const utils_1 = require("../utils");
|
|
10
10
|
const query_utils_1 = require("../search/query-utils");
|
|
11
11
|
const query_parser_1 = require("../search/query-parser");
|
|
12
|
+
const generated_detection_1 = require("../extraction/generated-detection");
|
|
13
|
+
const identifier_segments_1 = require("../search/identifier-segments");
|
|
14
|
+
/**
|
|
15
|
+
* Path-only heuristic for files that should not be candidates for
|
|
16
|
+
* "dominant file" detection: test/spec files and tool-generated files.
|
|
17
|
+
* Generated files (`*.pb.go`, `*.pulsar.go`, mock outputs, …) often
|
|
18
|
+
* have huge in-file edge counts that dwarf the real source — etcd's
|
|
19
|
+
* `rpc.pb.go` has 4× the in-file edges of `server.go`.
|
|
20
|
+
*/
|
|
21
|
+
function isLowValueFile(filePath) {
|
|
22
|
+
const lp = filePath.toLowerCase();
|
|
23
|
+
return (/(?:^|\/)(tests?|__tests?__|spec)\//.test(lp) ||
|
|
24
|
+
/_test\.go$/.test(lp) ||
|
|
25
|
+
/(?:^|\/)test_[^/]+\.py$/.test(lp) ||
|
|
26
|
+
/_test\.py$/.test(lp) ||
|
|
27
|
+
/_spec\.rb$/.test(lp) ||
|
|
28
|
+
/_test\.rb$/.test(lp) ||
|
|
29
|
+
/\.(test|spec)\.[jt]sx?$/.test(lp) ||
|
|
30
|
+
/(test|spec|tests)\.(java|kt|scala)$/.test(lp) ||
|
|
31
|
+
/(tests?|spec)\.cs$/.test(lp) ||
|
|
32
|
+
/tests?\.swift$/.test(lp) ||
|
|
33
|
+
/_test\.dart$/.test(lp) ||
|
|
34
|
+
(0, generated_detection_1.isGeneratedFile)(filePath));
|
|
35
|
+
}
|
|
36
|
+
const SQLITE_PARAM_CHUNK_SIZE = 500;
|
|
37
|
+
/**
|
|
38
|
+
* Last segment of a (possibly dotted/qualified) reference name — the part a
|
|
39
|
+
* new symbol's plain node name could match: 'util.greet' → 'greet',
|
|
40
|
+
* 'mod::fn' → 'fn', 'greet' → 'greet'. Written to unresolved_refs.name_tail
|
|
41
|
+
* when a ref is marked failed, so the #1240 retry lookup can match dotted
|
|
42
|
+
* refs against newly-added node names.
|
|
43
|
+
*/
|
|
44
|
+
function referenceNameTail(referenceName) {
|
|
45
|
+
const idx = Math.max(referenceName.lastIndexOf('.'), referenceName.lastIndexOf(':'));
|
|
46
|
+
return idx >= 0 ? referenceName.slice(idx + 1) : referenceName;
|
|
47
|
+
}
|
|
12
48
|
/**
|
|
13
49
|
* Convert database row to Node object
|
|
14
50
|
*/
|
|
@@ -33,6 +69,7 @@ function rowToNode(row) {
|
|
|
33
69
|
isAbstract: row.is_abstract === 1,
|
|
34
70
|
decorators: row.decorators ? (0, utils_1.safeJsonParse)(row.decorators, undefined) : undefined,
|
|
35
71
|
typeParameters: row.type_parameters ? (0, utils_1.safeJsonParse)(row.type_parameters, undefined) : undefined,
|
|
72
|
+
returnType: row.return_type ?? undefined,
|
|
36
73
|
updatedAt: row.updated_at,
|
|
37
74
|
};
|
|
38
75
|
}
|
|
@@ -70,14 +107,34 @@ function rowToFileRecord(row) {
|
|
|
70
107
|
*/
|
|
71
108
|
class QueryBuilder {
|
|
72
109
|
db;
|
|
110
|
+
// Project-name tokens (go.mod / package.json / repo dir), normalized. A query
|
|
111
|
+
// word matching one is dropped from path-relevance scoring — it names the
|
|
112
|
+
// whole project, not a symbol, so it carries no discriminative signal (#720).
|
|
113
|
+
// Set once by the CodeGraph instance; empty by default (no down-weighting).
|
|
114
|
+
projectNameTokens = new Set();
|
|
73
115
|
// Node cache for frequently accessed nodes (LRU-style, max 1000 entries)
|
|
74
116
|
nodeCache = new Map();
|
|
75
117
|
maxCacheSize = 1000;
|
|
76
118
|
// Prepared statements (lazily initialized)
|
|
77
119
|
stmts = {};
|
|
120
|
+
// Names whose segments were already written this session — skips re-splitting
|
|
121
|
+
// and re-inserting for the same-named nodes that repeat across files ("get",
|
|
122
|
+
// "render", …). Purely a write-path fast path; INSERT OR IGNORE is the
|
|
123
|
+
// correctness backstop. Bounded so a pathological repo can't grow it forever.
|
|
124
|
+
segmentedNames = new Set();
|
|
125
|
+
static MAX_SEGMENTED_NAMES = 65536;
|
|
78
126
|
constructor(db) {
|
|
79
127
|
this.db = db;
|
|
80
128
|
}
|
|
129
|
+
/** Set the normalized project-name tokens used to down-weight non-discriminative
|
|
130
|
+
* query words in path scoring (#720). Called once when the project opens. */
|
|
131
|
+
setProjectNameTokens(tokens) {
|
|
132
|
+
this.projectNameTokens = tokens;
|
|
133
|
+
}
|
|
134
|
+
/** The normalized project-name tokens (#720); empty if none were derived. */
|
|
135
|
+
getProjectNameTokens() {
|
|
136
|
+
return this.projectNameTokens;
|
|
137
|
+
}
|
|
81
138
|
// ===========================================================================
|
|
82
139
|
// Node Operations
|
|
83
140
|
// ===========================================================================
|
|
@@ -92,13 +149,13 @@ class QueryBuilder {
|
|
|
92
149
|
start_line, end_line, start_column, end_column,
|
|
93
150
|
docstring, signature, visibility,
|
|
94
151
|
is_exported, is_async, is_static, is_abstract,
|
|
95
|
-
decorators, type_parameters, updated_at
|
|
152
|
+
decorators, type_parameters, return_type, updated_at
|
|
96
153
|
) VALUES (
|
|
97
154
|
@id, @kind, @name, @qualifiedName, @filePath, @language,
|
|
98
155
|
@startLine, @endLine, @startColumn, @endColumn,
|
|
99
156
|
@docstring, @signature, @visibility,
|
|
100
157
|
@isExported, @isAsync, @isStatic, @isAbstract,
|
|
101
|
-
@decorators, @typeParameters, @updatedAt
|
|
158
|
+
@decorators, @typeParameters, @returnType, @updatedAt
|
|
102
159
|
)
|
|
103
160
|
`);
|
|
104
161
|
}
|
|
@@ -138,8 +195,42 @@ class QueryBuilder {
|
|
|
138
195
|
isAbstract: node.isAbstract ? 1 : 0,
|
|
139
196
|
decorators: node.decorators ? JSON.stringify(node.decorators) : null,
|
|
140
197
|
typeParameters: node.typeParameters ? JSON.stringify(node.typeParameters) : null,
|
|
198
|
+
returnType: node.returnType ?? null,
|
|
141
199
|
updatedAt: node.updatedAt ?? Date.now(),
|
|
142
200
|
});
|
|
201
|
+
// Segment vocabulary rides the same write path (and transaction) so it can
|
|
202
|
+
// never drift ahead of the nodes it describes. Deletes intentionally leave
|
|
203
|
+
// orphans behind — vocab rows are proposals re-verified against nodes
|
|
204
|
+
// before use, and a full index clears the table at its start. File nodes
|
|
205
|
+
// are excluded: a file's basename duplicates the symbols inside it
|
|
206
|
+
// (state-machine.ts / OrderStateMachine), which double-counts every
|
|
207
|
+
// concept and defeats the singleton-vs-cluster rarity statistics. Import
|
|
208
|
+
// nodes are excluded too (#1144): they're named after module specifiers
|
|
209
|
+
// ("external-unindexed-pkg", "./utils/helpers"), not symbols — an
|
|
210
|
+
// import-only name can never be surfaced (getSegmentMatches requires a
|
|
211
|
+
// real definition), so its rows would only inflate the rarity statistics.
|
|
212
|
+
if (this.isSegmentableKind(node.kind))
|
|
213
|
+
this.insertNameSegments(node.name);
|
|
214
|
+
}
|
|
215
|
+
/** Which node kinds contribute their name to the segment vocabulary — the
|
|
216
|
+
* single gate shared by insertNode, updateNode, and the rebuild page query
|
|
217
|
+
* (getDistinctNodeNames), so the write paths can't drift apart. */
|
|
218
|
+
isSegmentableKind(kind) {
|
|
219
|
+
return kind !== 'file' && kind !== 'import';
|
|
220
|
+
}
|
|
221
|
+
/** Write `name`'s segments into name_segment_vocab (idempotent). */
|
|
222
|
+
insertNameSegments(name) {
|
|
223
|
+
if (this.segmentedNames.has(name))
|
|
224
|
+
return;
|
|
225
|
+
if (this.segmentedNames.size >= QueryBuilder.MAX_SEGMENTED_NAMES)
|
|
226
|
+
this.segmentedNames.clear();
|
|
227
|
+
this.segmentedNames.add(name);
|
|
228
|
+
if (!this.stmts.insertNameSegment) {
|
|
229
|
+
this.stmts.insertNameSegment = this.db.prepare('INSERT OR IGNORE INTO name_segment_vocab (segment, name) VALUES (?, ?)');
|
|
230
|
+
}
|
|
231
|
+
for (const segment of (0, identifier_segments_1.splitIdentifierSegments)(name)) {
|
|
232
|
+
this.stmts.insertNameSegment.run(segment, name);
|
|
233
|
+
}
|
|
143
234
|
}
|
|
144
235
|
/**
|
|
145
236
|
* Insert multiple nodes in a transaction
|
|
@@ -176,6 +267,7 @@ class QueryBuilder {
|
|
|
176
267
|
is_abstract = @isAbstract,
|
|
177
268
|
decorators = @decorators,
|
|
178
269
|
type_parameters = @typeParameters,
|
|
270
|
+
return_type = @returnType,
|
|
179
271
|
updated_at = @updatedAt
|
|
180
272
|
WHERE id = @id
|
|
181
273
|
`);
|
|
@@ -207,8 +299,19 @@ class QueryBuilder {
|
|
|
207
299
|
isAbstract: node.isAbstract ? 1 : 0,
|
|
208
300
|
decorators: node.decorators ? JSON.stringify(node.decorators) : null,
|
|
209
301
|
typeParameters: node.typeParameters ? JSON.stringify(node.typeParameters) : null,
|
|
302
|
+
returnType: node.returnType ?? null,
|
|
210
303
|
updatedAt: node.updatedAt ?? Date.now(),
|
|
211
304
|
});
|
|
305
|
+
// updateNode is a second real write path to `nodes` — framework
|
|
306
|
+
// post-extract passes rewrite names through it (NestJS route prefixing),
|
|
307
|
+
// and a renamed node's new name must reach the segment vocabulary just
|
|
308
|
+
// like an inserted one's (#1141). Without this the rename left the new
|
|
309
|
+
// name permanently unsearchable: the old name's rows became honest-gate
|
|
310
|
+
// orphans and the only backfill is gated on the vocab being EMPTY.
|
|
311
|
+
// insertNameSegments is idempotent (in-memory set + INSERT OR IGNORE),
|
|
312
|
+
// so no name-changed check is needed.
|
|
313
|
+
if (this.isSegmentableKind(node.kind))
|
|
314
|
+
this.insertNameSegments(node.name);
|
|
212
315
|
}
|
|
213
316
|
/**
|
|
214
317
|
* Delete a node by ID
|
|
@@ -236,6 +339,86 @@ class QueryBuilder {
|
|
|
236
339
|
}
|
|
237
340
|
this.stmts.deleteNodesByFile.run(filePath);
|
|
238
341
|
}
|
|
342
|
+
// ===========================================================================
|
|
343
|
+
// Name-segment vocabulary (prompt-hook graph-derived gate)
|
|
344
|
+
// ===========================================================================
|
|
345
|
+
/** Wipe the segment vocabulary. A full index calls this at its start; the
|
|
346
|
+
* node write path repopulates it as files (re-)index, so the end state is
|
|
347
|
+
* exactly the current names with no orphan rows. */
|
|
348
|
+
clearNameSegmentVocab() {
|
|
349
|
+
this.db.exec('DELETE FROM name_segment_vocab');
|
|
350
|
+
this.segmentedNames.clear();
|
|
351
|
+
}
|
|
352
|
+
/** True when the vocab has no rows — an index built before the table existed.
|
|
353
|
+
* `sync` uses this to heal such databases (see rebuildNameSegmentVocabFrom). */
|
|
354
|
+
isNameSegmentVocabEmpty() {
|
|
355
|
+
const row = this.db.prepare('SELECT 1 FROM name_segment_vocab LIMIT 1').get();
|
|
356
|
+
return row === undefined;
|
|
357
|
+
}
|
|
358
|
+
/** One page of distinct segmentable node names, for batched vocab rebuilds
|
|
359
|
+
* (file basenames and import specifiers are excluded from the vocab — see
|
|
360
|
+
* insertNode). */
|
|
361
|
+
getDistinctNodeNames(limit, offset) {
|
|
362
|
+
const rows = this.db
|
|
363
|
+
.prepare("SELECT DISTINCT name FROM nodes WHERE kind NOT IN ('file', 'import') ORDER BY name LIMIT ? OFFSET ?")
|
|
364
|
+
.all(limit, offset);
|
|
365
|
+
return rows.map((r) => r.name);
|
|
366
|
+
}
|
|
367
|
+
/** Insert segments for a batch of names in one transaction (vocab heal path). */
|
|
368
|
+
insertNameSegmentsBatch(names) {
|
|
369
|
+
this.db.transaction(() => {
|
|
370
|
+
for (const name of names)
|
|
371
|
+
this.insertNameSegments(name);
|
|
372
|
+
})();
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Names whose segments cover at least `minWords` distinct PROMPT WORDS —
|
|
376
|
+
* the co-occurrence probe behind the prompt hook's medium tier: the words
|
|
377
|
+
* "state" and "machine" both being segments of `OrderStateMachine` is strong
|
|
378
|
+
* evidence the prompt names that symbol in prose. Ordered by coverage.
|
|
379
|
+
*
|
|
380
|
+
* Takes (segment variant → original word) pairs and folds variants back to
|
|
381
|
+
* their word INSIDE the SQL: a name matching both `service` and `services`
|
|
382
|
+
* counts ONE word, not two. Counting raw variants let plural-variant pairs
|
|
383
|
+
* of a single word tie with genuine two-word matches and — because ORDER
|
|
384
|
+
* BY/LIMIT run here, before any JS-side re-check — crowd a real match past
|
|
385
|
+
* the LIMIT on vocab-heavy repos (#1146).
|
|
386
|
+
*/
|
|
387
|
+
getSegmentCoOccurrence(variants, minWords, limit) {
|
|
388
|
+
if (variants.length === 0)
|
|
389
|
+
return [];
|
|
390
|
+
const placeholders = variants.map(() => '?').join(', ');
|
|
391
|
+
const whens = variants.map(() => 'WHEN ? THEN ?').join(' ');
|
|
392
|
+
const rows = this.db
|
|
393
|
+
.prepare(`SELECT name, COUNT(DISTINCT CASE segment ${whens} END) AS matches
|
|
394
|
+
FROM name_segment_vocab
|
|
395
|
+
WHERE segment IN (${placeholders})
|
|
396
|
+
GROUP BY name
|
|
397
|
+
HAVING matches >= ?
|
|
398
|
+
ORDER BY matches DESC, length(name) ASC
|
|
399
|
+
LIMIT ?`)
|
|
400
|
+
.all(...variants.flatMap((v) => [v.segment, v.word]), ...variants.map((v) => v.segment), minWords, limit);
|
|
401
|
+
return rows;
|
|
402
|
+
}
|
|
403
|
+
/** How many distinct names each segment appears in — the rarity signal that
|
|
404
|
+
* separates a discriminative word ("checkout") from a ubiquitous one ("state"). */
|
|
405
|
+
getSegmentNameCounts(segments) {
|
|
406
|
+
if (segments.length === 0)
|
|
407
|
+
return new Map();
|
|
408
|
+
const placeholders = segments.map(() => '?').join(', ');
|
|
409
|
+
const rows = this.db
|
|
410
|
+
.prepare(`SELECT segment, COUNT(*) AS n FROM name_segment_vocab
|
|
411
|
+
WHERE segment IN (${placeholders}) GROUP BY segment`)
|
|
412
|
+
.all(...segments);
|
|
413
|
+
return new Map(rows.map((r) => [r.segment, r.n]));
|
|
414
|
+
}
|
|
415
|
+
/** Names containing the given segment (rare-single-word tier). */
|
|
416
|
+
getNamesForSegment(segment, limit) {
|
|
417
|
+
const rows = this.db
|
|
418
|
+
.prepare('SELECT name FROM name_segment_vocab WHERE segment = ? ORDER BY length(name) ASC LIMIT ?')
|
|
419
|
+
.all(segment, limit);
|
|
420
|
+
return rows.map((r) => r.name);
|
|
421
|
+
}
|
|
239
422
|
/**
|
|
240
423
|
* Get a node by ID
|
|
241
424
|
*/
|
|
@@ -297,9 +480,8 @@ class QueryBuilder {
|
|
|
297
480
|
// Chunk under SQLite's parameter limit (default 999, raised to 32766
|
|
298
481
|
// in better-sqlite3 builds — chunk at 500 for safety across both
|
|
299
482
|
// backends and to keep the query plan simple).
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const chunk = misses.slice(i, i + CHUNK);
|
|
483
|
+
for (let i = 0; i < misses.length; i += SQLITE_PARAM_CHUNK_SIZE) {
|
|
484
|
+
const chunk = misses.slice(i, i + SQLITE_PARAM_CHUNK_SIZE);
|
|
303
485
|
const placeholders = chunk.map(() => '?').join(',');
|
|
304
486
|
const rows = this.db
|
|
305
487
|
.prepare(`SELECT * FROM nodes WHERE id IN (${placeholders})`)
|
|
@@ -312,6 +494,23 @@ class QueryBuilder {
|
|
|
312
494
|
}
|
|
313
495
|
return out;
|
|
314
496
|
}
|
|
497
|
+
getExistingNodeIds(ids) {
|
|
498
|
+
const out = new Set();
|
|
499
|
+
if (ids.length === 0)
|
|
500
|
+
return out;
|
|
501
|
+
const uniqueIds = [...new Set(ids)];
|
|
502
|
+
for (let i = 0; i < uniqueIds.length; i += SQLITE_PARAM_CHUNK_SIZE) {
|
|
503
|
+
const chunk = uniqueIds.slice(i, i + SQLITE_PARAM_CHUNK_SIZE);
|
|
504
|
+
const placeholders = chunk.map(() => '?').join(',');
|
|
505
|
+
const rows = this.db
|
|
506
|
+
.prepare(`SELECT id FROM nodes WHERE id IN (${placeholders})`)
|
|
507
|
+
.all(...chunk);
|
|
508
|
+
for (const row of rows) {
|
|
509
|
+
out.add(row.id);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return out;
|
|
513
|
+
}
|
|
315
514
|
/**
|
|
316
515
|
* Add a node to the cache, evicting oldest if needed
|
|
317
516
|
*/
|
|
@@ -341,6 +540,153 @@ class QueryBuilder {
|
|
|
341
540
|
const rows = this.stmts.getNodesByFile.all(filePath);
|
|
342
541
|
return rows.map(rowToNode);
|
|
343
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Find the file that holds the densest concentration of the project's
|
|
545
|
+
* internal call graph — the "core" file. Used by context-builder to
|
|
546
|
+
* boost ranking of symbols in that file's directory (so e.g. sinatra
|
|
547
|
+
* queries surface `lib/sinatra/base.rb`'s `route!` instead of
|
|
548
|
+
* `sinatra-contrib/lib/sinatra/multi_route.rb`'s `route` extension).
|
|
549
|
+
*
|
|
550
|
+
* Returns null if no file has a meaningful concentration (e.g. spread
|
|
551
|
+
* evenly across many files, or empty index).
|
|
552
|
+
*
|
|
553
|
+
* "Internal" = source and target are in the same file. Cross-file
|
|
554
|
+
* edges aren't useful here — they don't tell us which file is the
|
|
555
|
+
* functional center.
|
|
556
|
+
*
|
|
557
|
+
* Excludes test/spec files from candidacy via path-pattern. The agent's
|
|
558
|
+
* typical question is "how does X work", not "how is X tested", so
|
|
559
|
+
* boosting a test file's directory would be a misfire.
|
|
560
|
+
*/
|
|
561
|
+
getDominantFile() {
|
|
562
|
+
if (!this.stmts.getDominantFile) {
|
|
563
|
+
// Pull top 20 candidates; we then filter out test/generated files
|
|
564
|
+
// in code (regex-grade matching that SQL LIKE can't express). The
|
|
565
|
+
// generated-file filter is critical — without it, etcd's
|
|
566
|
+
// `api/etcdserverpb/rpc.pb.go` (1916 in-file edges, generated
|
|
567
|
+
// protobuf stub) outranks the real `server/etcdserver/server.go`
|
|
568
|
+
// (470 edges) by 4×, and the boost would push the agent toward
|
|
569
|
+
// generated code.
|
|
570
|
+
this.stmts.getDominantFile = this.db.prepare(`
|
|
571
|
+
SELECT n.file_path AS file_path, COUNT(*) AS edge_count
|
|
572
|
+
FROM edges e
|
|
573
|
+
JOIN nodes n ON e.source = n.id
|
|
574
|
+
JOIN nodes m ON e.target = m.id
|
|
575
|
+
WHERE n.file_path = m.file_path
|
|
576
|
+
GROUP BY n.file_path
|
|
577
|
+
ORDER BY edge_count DESC
|
|
578
|
+
LIMIT 20
|
|
579
|
+
`);
|
|
580
|
+
}
|
|
581
|
+
const rows = this.stmts.getDominantFile.all();
|
|
582
|
+
const filtered = rows.filter(r => !isLowValueFile(r.file_path));
|
|
583
|
+
if (filtered.length === 0 || filtered[0].edge_count < 20)
|
|
584
|
+
return null;
|
|
585
|
+
return {
|
|
586
|
+
filePath: filtered[0].file_path,
|
|
587
|
+
edgeCount: filtered[0].edge_count,
|
|
588
|
+
nextEdgeCount: filtered[1]?.edge_count ?? 0,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Find the file that holds the densest concentration of the project's
|
|
593
|
+
* `route` nodes (framework-emitted: Express/Gin/Flask/Rails/Drupal/etc.).
|
|
594
|
+
* Used by handleContext on small repos to inline the project's routing
|
|
595
|
+
* config when the agent's query is about request flow — eliminating the
|
|
596
|
+
* "Glob + Read routes.rb" pattern that beats codegraph on tiny realworld
|
|
597
|
+
* template repos.
|
|
598
|
+
*
|
|
599
|
+
* Excludes test/generated files from candidacy. Returns null if there
|
|
600
|
+
* are fewer than 3 non-test routes total, or if no file holds at least
|
|
601
|
+
* 30% of them (diffuse routing → no single answer file).
|
|
602
|
+
*/
|
|
603
|
+
getTopRouteFile() {
|
|
604
|
+
if (!this.stmts.getTopRouteFile) {
|
|
605
|
+
this.stmts.getTopRouteFile = this.db.prepare(`
|
|
606
|
+
SELECT file_path, COUNT(*) AS cnt
|
|
607
|
+
FROM nodes
|
|
608
|
+
WHERE kind = 'route'
|
|
609
|
+
GROUP BY file_path
|
|
610
|
+
ORDER BY cnt DESC
|
|
611
|
+
LIMIT 20
|
|
612
|
+
`);
|
|
613
|
+
}
|
|
614
|
+
const rows = this.stmts.getTopRouteFile.all();
|
|
615
|
+
const filtered = rows.filter(r => !isLowValueFile(r.file_path));
|
|
616
|
+
if (filtered.length === 0)
|
|
617
|
+
return null;
|
|
618
|
+
const totalRoutes = filtered.reduce((sum, r) => sum + r.cnt, 0);
|
|
619
|
+
const top = filtered[0];
|
|
620
|
+
if (totalRoutes < 3 || top.cnt < 3)
|
|
621
|
+
return null;
|
|
622
|
+
if (top.cnt / totalRoutes < 0.30)
|
|
623
|
+
return null;
|
|
624
|
+
return { filePath: top.file_path, routeCount: top.cnt, totalRoutes };
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Build a URL → handler manifest from the index. Each route node's
|
|
628
|
+
* `references` edge points at the function/method that handles the
|
|
629
|
+
* request. We join them in one pass; the agent gets the canonical
|
|
630
|
+
* routing answer ("POST /users/login → AuthController#login") without
|
|
631
|
+
* having to parse the framework's route DSL itself.
|
|
632
|
+
*
|
|
633
|
+
* Also returns the file with the most handler endpoints — used as the
|
|
634
|
+
* "top handler file" to inline source for, so the agent has both the
|
|
635
|
+
* mapping AND the handler implementations.
|
|
636
|
+
*/
|
|
637
|
+
getRoutingManifest(limit = 40) {
|
|
638
|
+
if (!this.stmts.getRoutingManifest) {
|
|
639
|
+
// Edge kind varies across framework resolvers: Spring/Rails/
|
|
640
|
+
// Laravel/Drupal emit `references`, Express emits `calls`. Accept
|
|
641
|
+
// both — the semantic is the same (route → its handler).
|
|
642
|
+
this.stmts.getRoutingManifest = this.db.prepare(`
|
|
643
|
+
SELECT
|
|
644
|
+
r.name AS url,
|
|
645
|
+
h.name AS handler,
|
|
646
|
+
h.file_path AS handler_file,
|
|
647
|
+
h.start_line AS handler_line,
|
|
648
|
+
h.kind AS handler_kind
|
|
649
|
+
FROM nodes r
|
|
650
|
+
JOIN edges e ON e.source = r.id
|
|
651
|
+
JOIN nodes h ON e.target = h.id
|
|
652
|
+
WHERE r.kind = 'route'
|
|
653
|
+
AND e.kind IN ('references', 'calls')
|
|
654
|
+
AND h.kind IN ('function', 'method', 'class')
|
|
655
|
+
ORDER BY r.file_path, r.start_line
|
|
656
|
+
LIMIT ?
|
|
657
|
+
`);
|
|
658
|
+
}
|
|
659
|
+
const rows = this.stmts.getRoutingManifest.all(limit);
|
|
660
|
+
// Drop test/generated handlers — same hygiene as elsewhere.
|
|
661
|
+
const filtered = rows.filter(r => !isLowValueFile(r.handler_file));
|
|
662
|
+
if (filtered.length < 3)
|
|
663
|
+
return null;
|
|
664
|
+
// Identify the file holding the most handlers (the "primary handler file").
|
|
665
|
+
const fileCounts = new Map();
|
|
666
|
+
for (const r of filtered) {
|
|
667
|
+
fileCounts.set(r.handler_file, (fileCounts.get(r.handler_file) ?? 0) + 1);
|
|
668
|
+
}
|
|
669
|
+
let topHandlerFile = null;
|
|
670
|
+
let topHandlerFileCount = 0;
|
|
671
|
+
for (const [file, count] of fileCounts) {
|
|
672
|
+
if (count > topHandlerFileCount) {
|
|
673
|
+
topHandlerFile = file;
|
|
674
|
+
topHandlerFileCount = count;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return {
|
|
678
|
+
entries: filtered.map(r => ({
|
|
679
|
+
url: r.url,
|
|
680
|
+
handler: r.handler,
|
|
681
|
+
handlerFile: r.handler_file,
|
|
682
|
+
handlerLine: r.handler_line,
|
|
683
|
+
handlerKind: r.handler_kind,
|
|
684
|
+
})),
|
|
685
|
+
topHandlerFile,
|
|
686
|
+
topHandlerFileCount,
|
|
687
|
+
totalRoutes: filtered.length,
|
|
688
|
+
};
|
|
689
|
+
}
|
|
344
690
|
/**
|
|
345
691
|
* Get all nodes of a specific kind
|
|
346
692
|
*/
|
|
@@ -351,6 +697,21 @@ class QueryBuilder {
|
|
|
351
697
|
const rows = this.stmts.getNodesByKind.all(kind);
|
|
352
698
|
return rows.map(rowToNode);
|
|
353
699
|
}
|
|
700
|
+
/**
|
|
701
|
+
* Stream every node of a kind one at a time (lazy) instead of materializing
|
|
702
|
+
* them all like {@link getNodesByKind}. For unbounded kinds (`function`,
|
|
703
|
+
* `method`) on a symbol-dense project the full array is gigabytes; the
|
|
704
|
+
* dynamic-edge synthesizers only scan-and-filter, so they iterate to keep
|
|
705
|
+
* memory O(1) in the node count rather than O(nodes) (#610).
|
|
706
|
+
*/
|
|
707
|
+
*iterateNodesByKind(kind) {
|
|
708
|
+
// Fresh statement per call (not a cached one): an iterator holds an open
|
|
709
|
+
// cursor, so a shared statement would conflict across overlapping scans.
|
|
710
|
+
const stmt = this.db.prepare('SELECT * FROM nodes WHERE kind = ?');
|
|
711
|
+
for (const row of stmt.iterate(kind)) {
|
|
712
|
+
yield rowToNode(row);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
354
715
|
/**
|
|
355
716
|
* Get all nodes in the database
|
|
356
717
|
*/
|
|
@@ -358,6 +719,33 @@ class QueryBuilder {
|
|
|
358
719
|
const rows = this.db.prepare('SELECT * FROM nodes').all();
|
|
359
720
|
return rows.map(rowToNode);
|
|
360
721
|
}
|
|
722
|
+
/**
|
|
723
|
+
* Stream nodes of one language whose `decorators` JSON array contains
|
|
724
|
+
* `decorator`. The LIKE on the JSON text is a cheap index-free pre-filter
|
|
725
|
+
* (a decorator name can appear as a substring of another), so callers must
|
|
726
|
+
* still exact-check `node.decorators.includes(decorator)`. Exists so the
|
|
727
|
+
* kotlin expect/actual synthesizer never materializes the whole node table
|
|
728
|
+
* the way `getAllNodes().filter(...)` did — that array alone OOM'd Node's
|
|
729
|
+
* default heap on a 2M-node graph (#1212).
|
|
730
|
+
*/
|
|
731
|
+
*iterateNodesByLanguageWithDecorator(language, decorator) {
|
|
732
|
+
// Fresh statement per call — an iterator holds an open cursor (see
|
|
733
|
+
// iterateNodesByKind).
|
|
734
|
+
const stmt = this.db.prepare("SELECT * FROM nodes WHERE language = ? AND decorators LIKE '%' || ? || '%'");
|
|
735
|
+
for (const row of stmt.iterate(language, `"${decorator}"`)) {
|
|
736
|
+
yield rowToNode(row);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Distinct languages present in the files table. One indexed aggregate —
|
|
741
|
+
* lets the dynamic-edge synthesizers skip passes for languages the project
|
|
742
|
+
* doesn't contain at all (a Kotlin pass has no work on a pure-C repo), so
|
|
743
|
+
* their cost is zero rather than a full-graph scan that finds nothing (#1212).
|
|
744
|
+
*/
|
|
745
|
+
getDistinctFileLanguages() {
|
|
746
|
+
const rows = this.db.prepare('SELECT DISTINCT language FROM files').all();
|
|
747
|
+
return new Set(rows.map((r) => r.language));
|
|
748
|
+
}
|
|
361
749
|
/**
|
|
362
750
|
* Get nodes by exact name match (uses idx_nodes_name index)
|
|
363
751
|
*/
|
|
@@ -368,6 +756,17 @@ class QueryBuilder {
|
|
|
368
756
|
const rows = this.stmts.getNodesByName.all(name);
|
|
369
757
|
return rows.map(rowToNode);
|
|
370
758
|
}
|
|
759
|
+
/**
|
|
760
|
+
* Nodes whose name starts with `prefix`, by index range scan (a LIKE would
|
|
761
|
+
* skip idx_nodes_name under SQLite's default case-insensitive LIKE).
|
|
762
|
+
*/
|
|
763
|
+
getNodesByNamePrefix(prefix, limit = 20) {
|
|
764
|
+
if (!this.stmts.getNodesByNamePrefix) {
|
|
765
|
+
this.stmts.getNodesByNamePrefix = this.db.prepare('SELECT * FROM nodes WHERE name >= ? AND name < ? ORDER BY name LIMIT ?');
|
|
766
|
+
}
|
|
767
|
+
const rows = this.stmts.getNodesByNamePrefix.all(prefix, prefix + '', limit);
|
|
768
|
+
return rows.map(rowToNode);
|
|
769
|
+
}
|
|
371
770
|
/**
|
|
372
771
|
* Get nodes by exact qualified name match (uses idx_nodes_qualified_name index)
|
|
373
772
|
*/
|
|
@@ -418,10 +817,7 @@ class QueryBuilder {
|
|
|
418
817
|
const text = parsed.text;
|
|
419
818
|
const kinds = mergedKinds;
|
|
420
819
|
const languages = mergedLanguages;
|
|
421
|
-
//
|
|
422
|
-
const cjkPattern = /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/;
|
|
423
|
-
const hasCJK = text ? cjkPattern.test(text) : false;
|
|
424
|
-
// First try FTS5 with prefix matching (best for English identifiers)
|
|
820
|
+
// First try FTS5 with prefix matching
|
|
425
821
|
let results = text
|
|
426
822
|
? this.searchNodesFTS(text, { kinds, languages, limit, offset })
|
|
427
823
|
// Over-fetch by 5× when running filter-only (no text). The
|
|
@@ -429,11 +825,6 @@ class QueryBuilder {
|
|
|
429
825
|
// a smaller multiplier risks returning fewer than `limit`
|
|
430
826
|
// results despite the DB having plenty of matches.
|
|
431
827
|
: this.searchAllByFilters({ kinds, languages, limit: limit * 5 });
|
|
432
|
-
// If query has CJK characters and FTS found nothing (FTS5 unicode61
|
|
433
|
-
// can't tokenize CJK), search name + docstring with LIKE
|
|
434
|
-
if (results.length === 0 && hasCJK) {
|
|
435
|
-
results = this.searchNodesCJK(text, { kinds, languages, limit, offset });
|
|
436
|
-
}
|
|
437
828
|
// If no FTS results, try LIKE-based substring search
|
|
438
829
|
if (results.length === 0 && text.length >= 2) {
|
|
439
830
|
results = this.searchNodesLike(text, { kinds, languages, limit, offset });
|
|
@@ -483,7 +874,7 @@ class QueryBuilder {
|
|
|
483
874
|
...r,
|
|
484
875
|
score: r.score
|
|
485
876
|
+ (0, query_utils_1.kindBonus)(r.node.kind)
|
|
486
|
-
+ (0, query_utils_1.scorePathRelevance)(r.node.filePath, scoringQuery)
|
|
877
|
+
+ (0, query_utils_1.scorePathRelevance)(r.node.filePath, scoringQuery, this.projectNameTokens)
|
|
487
878
|
+ (0, query_utils_1.nameMatchBonus)(r.node.name, scoringQuery),
|
|
488
879
|
}));
|
|
489
880
|
results.sort((a, b) => b.score - a.score);
|
|
@@ -706,80 +1097,6 @@ class QueryBuilder {
|
|
|
706
1097
|
score: row.score,
|
|
707
1098
|
}));
|
|
708
1099
|
}
|
|
709
|
-
/**
|
|
710
|
-
* CJK-aware search using LIKE on name + docstring.
|
|
711
|
-
*
|
|
712
|
-
* FTS5 unicode61 cannot tokenize CJK characters — it treats entire CJK
|
|
713
|
-
* sequences as opaque tokens that never match. This method bypasses FTS
|
|
714
|
-
* and searches the name, qualified_name, and docstring columns directly
|
|
715
|
-
* with LIKE, matching CJK bigrams/trigrams extracted by extractSearchTerms.
|
|
716
|
-
*
|
|
717
|
-
* Scoring priority:
|
|
718
|
-
* name match (1.0) > qualified_name (0.8) > docstring (0.6)
|
|
719
|
-
*/
|
|
720
|
-
searchNodesCJK(query, options) {
|
|
721
|
-
const { kinds, languages, limit = 100, offset = 0 } = options;
|
|
722
|
-
// Extract CJK terms (bigrams, trigrams, full sequences)
|
|
723
|
-
const cjkPattern = /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]+/g;
|
|
724
|
-
const cjkTerms = new Set();
|
|
725
|
-
let m;
|
|
726
|
-
while ((m = cjkPattern.exec(query)) !== null) {
|
|
727
|
-
const text = m[0];
|
|
728
|
-
if (text.length >= 2) cjkTerms.add(text);
|
|
729
|
-
for (let i = 0; i < text.length - 1; i++) cjkTerms.add(text.slice(i, i + 2));
|
|
730
|
-
for (let i = 0; i < text.length - 2; i++) cjkTerms.add(text.slice(i, i + 3));
|
|
731
|
-
}
|
|
732
|
-
// Also extract ASCII terms for combined matching
|
|
733
|
-
const asciiTerms = query
|
|
734
|
-
.replace(cjkPattern, ' ')
|
|
735
|
-
.split(/\s+/)
|
|
736
|
-
.filter(t => t.length >= 2);
|
|
737
|
-
const allTerms = [...cjkTerms, ...asciiTerms];
|
|
738
|
-
if (allTerms.length === 0) return [];
|
|
739
|
-
// Build OR conditions: each term searches name, qualified_name, docstring
|
|
740
|
-
const whereClauses = [];
|
|
741
|
-
const whereParams = [];
|
|
742
|
-
for (const term of allTerms.slice(0, 10)) { // cap at 10 terms to avoid huge queries
|
|
743
|
-
const pattern = `%${term}%`;
|
|
744
|
-
whereClauses.push('(name LIKE ? OR qualified_name LIKE ? OR docstring LIKE ?)');
|
|
745
|
-
whereParams.push(pattern, pattern, pattern);
|
|
746
|
-
}
|
|
747
|
-
let sql = `
|
|
748
|
-
SELECT *,
|
|
749
|
-
CASE
|
|
750
|
-
WHEN name LIKE ? THEN 1.0
|
|
751
|
-
WHEN qualified_name LIKE ? THEN 0.8
|
|
752
|
-
WHEN docstring LIKE ? THEN 0.6
|
|
753
|
-
ELSE 0.4
|
|
754
|
-
END as score
|
|
755
|
-
FROM nodes
|
|
756
|
-
WHERE (${whereClauses.join(' OR ')})
|
|
757
|
-
`;
|
|
758
|
-
// First term for the CASE scoring (use the longest CJK term)
|
|
759
|
-
const primaryTerm = allTerms.reduce((a, b) => a.length >= b.length ? a : b);
|
|
760
|
-
const primaryPattern = `%${primaryTerm}%`;
|
|
761
|
-
const params = [primaryPattern, primaryPattern, primaryPattern, ...whereParams];
|
|
762
|
-
if (kinds && kinds.length > 0) {
|
|
763
|
-
sql += ` AND kind IN (${kinds.map(() => '?').join(',')})`;
|
|
764
|
-
params.push(...kinds);
|
|
765
|
-
}
|
|
766
|
-
if (languages && languages.length > 0) {
|
|
767
|
-
sql += ` AND language IN (${languages.map(() => '?').join(',')})`;
|
|
768
|
-
params.push(...languages);
|
|
769
|
-
}
|
|
770
|
-
sql += ' ORDER BY score DESC, length(name) ASC LIMIT ? OFFSET ?';
|
|
771
|
-
params.push(limit, offset);
|
|
772
|
-
try {
|
|
773
|
-
const rows = this.db.prepare(sql).all(...params);
|
|
774
|
-
return rows.map((row) => ({
|
|
775
|
-
node: rowToNode(row),
|
|
776
|
-
score: row.score,
|
|
777
|
-
}));
|
|
778
|
-
}
|
|
779
|
-
catch {
|
|
780
|
-
return [];
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
1100
|
/**
|
|
784
1101
|
* Find nodes by exact name match
|
|
785
1102
|
*
|
|
@@ -924,8 +1241,19 @@ class QueryBuilder {
|
|
|
924
1241
|
* Insert multiple edges in a transaction
|
|
925
1242
|
*/
|
|
926
1243
|
insertEdges(edges) {
|
|
1244
|
+
if (edges.length === 0)
|
|
1245
|
+
return;
|
|
927
1246
|
this.db.transaction(() => {
|
|
1247
|
+
const endpointIds = new Set();
|
|
1248
|
+
for (const edge of edges) {
|
|
1249
|
+
endpointIds.add(edge.source);
|
|
1250
|
+
endpointIds.add(edge.target);
|
|
1251
|
+
}
|
|
1252
|
+
const existingNodeIds = this.getExistingNodeIds([...endpointIds]);
|
|
928
1253
|
for (const edge of edges) {
|
|
1254
|
+
if (!existingNodeIds.has(edge.source) || !existingNodeIds.has(edge.target)) {
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
929
1257
|
this.insertEdge(edge);
|
|
930
1258
|
}
|
|
931
1259
|
})();
|
|
@@ -995,6 +1323,78 @@ class QueryBuilder {
|
|
|
995
1323
|
const rows = this.db.prepare(sql).all(...params);
|
|
996
1324
|
return rows.map(rowToEdge);
|
|
997
1325
|
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Distinct file paths that DEPEND ON `filePath`: every file containing a
|
|
1328
|
+
* symbol with a cross-file edge (any kind except `contains`) into a symbol
|
|
1329
|
+
* of this file. This is the file-level projection of the symbol dependency
|
|
1330
|
+
* graph and the basis for blast-radius / `affected` test selection.
|
|
1331
|
+
*
|
|
1332
|
+
* It deliberately does NOT restrict to `imports` edges. In this graph an
|
|
1333
|
+
* `imports` edge connects a file to its own local import declarations
|
|
1334
|
+
* (it is always same-file), so an imports-only lookup returns zero
|
|
1335
|
+
* cross-file dependents for every file. The real cross-file dependency
|
|
1336
|
+
* signal is the resolved call/reference graph — calls, references,
|
|
1337
|
+
* instantiates, extends, implements, overrides, type_of, returns,
|
|
1338
|
+
* decorates — exactly what {@link GraphTraverser.getImpactRadius} traverses.
|
|
1339
|
+
* `contains` is excluded: a parent containing a symbol does not *depend* on
|
|
1340
|
+
* it. One indexed query (idx_nodes_file_path + idx_edges_target_kind).
|
|
1341
|
+
*/
|
|
1342
|
+
getDependentFilePaths(filePath) {
|
|
1343
|
+
const sql = `SELECT DISTINCT src.file_path AS fp
|
|
1344
|
+
FROM edges e
|
|
1345
|
+
JOIN nodes tgt ON tgt.id = e.target
|
|
1346
|
+
JOIN nodes src ON src.id = e.source
|
|
1347
|
+
WHERE tgt.file_path = ?
|
|
1348
|
+
AND e.kind != 'contains'
|
|
1349
|
+
AND src.file_path != ?`;
|
|
1350
|
+
const rows = this.db.prepare(sql).all(filePath, filePath);
|
|
1351
|
+
return rows.map((r) => r.fp);
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* Distinct file paths that `filePath` DEPENDS ON — the inverse of
|
|
1355
|
+
* {@link getDependentFilePaths}: every file containing a symbol that a
|
|
1356
|
+
* symbol of this file has a cross-file edge into. Same edge-kind rules
|
|
1357
|
+
* (all kinds except `contains`); same reason imports-only is insufficient.
|
|
1358
|
+
*/
|
|
1359
|
+
getDependencyFilePaths(filePath) {
|
|
1360
|
+
const sql = `SELECT DISTINCT tgt.file_path AS fp
|
|
1361
|
+
FROM edges e
|
|
1362
|
+
JOIN nodes src ON src.id = e.source
|
|
1363
|
+
JOIN nodes tgt ON tgt.id = e.target
|
|
1364
|
+
WHERE src.file_path = ?
|
|
1365
|
+
AND e.kind != 'contains'
|
|
1366
|
+
AND tgt.file_path != ?`;
|
|
1367
|
+
const rows = this.db.prepare(sql).all(filePath, filePath);
|
|
1368
|
+
return rows.map((r) => r.fp);
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Cross-file edges whose TARGET is a node in `filePath` and whose SOURCE is a
|
|
1372
|
+
* node in a *different* file, paired with the target node's (name, kind) so a
|
|
1373
|
+
* caller can re-resolve the edge to the re-indexed target's new ID (node IDs
|
|
1374
|
+
* are `sha256(filePath:kind:name:line)`, so any line shift in the callee file
|
|
1375
|
+
* changes target IDs and a naive re-insert by old ID silently drops them).
|
|
1376
|
+
* Used by `storeExtractionResult` to preserve incoming edges across a file
|
|
1377
|
+
* re-index (issue #899). Same edge-kind rules as
|
|
1378
|
+
* {@link getDependentFilePaths}: all kinds except `contains`.
|
|
1379
|
+
*/
|
|
1380
|
+
getCrossFileIncomingEdgesWithTarget(filePath) {
|
|
1381
|
+
const sql = `SELECT e.*, tgt.name AS target_name, tgt.kind AS target_kind,
|
|
1382
|
+
src.file_path AS source_file_path, src.language AS source_language
|
|
1383
|
+
FROM edges e
|
|
1384
|
+
JOIN nodes tgt ON tgt.id = e.target
|
|
1385
|
+
JOIN nodes src ON src.id = e.source
|
|
1386
|
+
WHERE tgt.file_path = ?
|
|
1387
|
+
AND e.kind != 'contains'
|
|
1388
|
+
AND src.file_path != ?`;
|
|
1389
|
+
const rows = this.db.prepare(sql).all(filePath, filePath);
|
|
1390
|
+
return rows.map(row => ({
|
|
1391
|
+
...rowToEdge(row),
|
|
1392
|
+
targetName: row.target_name,
|
|
1393
|
+
targetKind: row.target_kind,
|
|
1394
|
+
sourceFilePath: row.source_file_path,
|
|
1395
|
+
sourceLanguage: row.source_language,
|
|
1396
|
+
}));
|
|
1397
|
+
}
|
|
998
1398
|
// ===========================================================================
|
|
999
1399
|
// File Operations
|
|
1000
1400
|
// ===========================================================================
|
|
@@ -1059,6 +1459,16 @@ class QueryBuilder {
|
|
|
1059
1459
|
const rows = this.stmts.getAllFiles.all();
|
|
1060
1460
|
return rows.map(rowToFileRecord);
|
|
1061
1461
|
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Most recent index timestamp (ms since epoch) across all tracked files, or
|
|
1464
|
+
* null when nothing is indexed yet. One indexed aggregate, no per-row scan. (#329)
|
|
1465
|
+
*/
|
|
1466
|
+
getLastIndexedAt() {
|
|
1467
|
+
const row = this.db
|
|
1468
|
+
.prepare('SELECT MAX(indexed_at) AS last FROM files')
|
|
1469
|
+
.get();
|
|
1470
|
+
return row?.last ?? null;
|
|
1471
|
+
}
|
|
1062
1472
|
/**
|
|
1063
1473
|
* Get files that need re-indexing (hash changed)
|
|
1064
1474
|
*/
|
|
@@ -1151,22 +1561,28 @@ class QueryBuilder {
|
|
|
1151
1561
|
}));
|
|
1152
1562
|
}
|
|
1153
1563
|
/**
|
|
1154
|
-
* Get the count of
|
|
1564
|
+
* Get the count of PENDING (never-attempted) references without loading
|
|
1565
|
+
* them into memory. Rows marked status='failed' — attempted by a completed
|
|
1566
|
+
* pass, no match — are excluded: they are not outstanding work, only retry
|
|
1567
|
+
* candidates for the #1240 sweep, so they must not trip the #1187 orphan
|
|
1568
|
+
* sweep or the `status` pending-refs warning.
|
|
1155
1569
|
*/
|
|
1156
1570
|
getUnresolvedReferencesCount() {
|
|
1157
1571
|
if (!this.stmts.getUnresolvedCount) {
|
|
1158
|
-
this.stmts.getUnresolvedCount = this.db.prepare(
|
|
1572
|
+
this.stmts.getUnresolvedCount = this.db.prepare("SELECT COUNT(*) as count FROM unresolved_refs WHERE status = 'pending'");
|
|
1159
1573
|
}
|
|
1160
1574
|
const row = this.stmts.getUnresolvedCount.get();
|
|
1161
1575
|
return row.count;
|
|
1162
1576
|
}
|
|
1163
1577
|
/**
|
|
1164
|
-
* Get a batch of unresolved references using LIMIT/OFFSET
|
|
1165
|
-
* Used to process references in bounded memory chunks
|
|
1578
|
+
* Get a batch of PENDING unresolved references using LIMIT/OFFSET
|
|
1579
|
+
* pagination. Used to process references in bounded memory chunks; failed
|
|
1580
|
+
* rows are excluded so the batched drain loop terminates once every row
|
|
1581
|
+
* has been attempted.
|
|
1166
1582
|
*/
|
|
1167
1583
|
getUnresolvedReferencesBatch(offset, limit) {
|
|
1168
1584
|
if (!this.stmts.getUnresolvedBatch) {
|
|
1169
|
-
this.stmts.getUnresolvedBatch = this.db.prepare(
|
|
1585
|
+
this.stmts.getUnresolvedBatch = this.db.prepare("SELECT * FROM unresolved_refs WHERE status = 'pending' LIMIT ? OFFSET ?");
|
|
1170
1586
|
}
|
|
1171
1587
|
const rows = this.stmts.getUnresolvedBatch.all(limit, offset);
|
|
1172
1588
|
return rows.map((row) => ({
|
|
@@ -1200,6 +1616,18 @@ class QueryBuilder {
|
|
|
1200
1616
|
const rows = this.stmts.getAllNodeNames.all();
|
|
1201
1617
|
return rows.map((r) => r.name);
|
|
1202
1618
|
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Stream the distinct node names one row at a time — the incremental
|
|
1621
|
+
* counterpart to {@link getAllNodeNames} for callers that need to yield
|
|
1622
|
+
* to the event loop mid-scan (resolver cache warm-up on multi-million-node
|
|
1623
|
+
* indexes). Fresh statement per call: the iterator holds an open cursor.
|
|
1624
|
+
*/
|
|
1625
|
+
*iterateNodeNames() {
|
|
1626
|
+
const stmt = this.db.prepare('SELECT DISTINCT name FROM nodes');
|
|
1627
|
+
for (const row of stmt.iterate()) {
|
|
1628
|
+
yield row.name;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1203
1631
|
/**
|
|
1204
1632
|
* Get unresolved references scoped to specific file paths.
|
|
1205
1633
|
* Uses the idx_unresolved_file_path index for efficient lookup.
|
|
@@ -1207,10 +1635,19 @@ class QueryBuilder {
|
|
|
1207
1635
|
getUnresolvedReferencesByFiles(filePaths) {
|
|
1208
1636
|
if (filePaths.length === 0)
|
|
1209
1637
|
return [];
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1638
|
+
// Chunk under SQLite's parameter limit: the first sync of a very large repo
|
|
1639
|
+
// passes every changed file here, which an unbounded `IN (...)` would bind
|
|
1640
|
+
// as one parameter each — exceeding MAX_VARIABLE_NUMBER and aborting with
|
|
1641
|
+
// "too many SQL variables". (#540)
|
|
1642
|
+
const rows = [];
|
|
1643
|
+
for (let i = 0; i < filePaths.length; i += SQLITE_PARAM_CHUNK_SIZE) {
|
|
1644
|
+
const chunk = filePaths.slice(i, i + SQLITE_PARAM_CHUNK_SIZE);
|
|
1645
|
+
const placeholders = chunk.map(() => '?').join(',');
|
|
1646
|
+
const chunkRows = this.db
|
|
1647
|
+
.prepare(`SELECT * FROM unresolved_refs WHERE status = 'pending' AND file_path IN (${placeholders})`)
|
|
1648
|
+
.all(...chunk);
|
|
1649
|
+
rows.push(...chunkRows);
|
|
1650
|
+
}
|
|
1214
1651
|
return rows.map((row) => ({
|
|
1215
1652
|
fromNodeId: row.from_node_id,
|
|
1216
1653
|
referenceName: row.reference_name,
|
|
@@ -1234,15 +1671,16 @@ class QueryBuilder {
|
|
|
1234
1671
|
deleteResolvedReferences(fromNodeIds) {
|
|
1235
1672
|
if (fromNodeIds.length === 0)
|
|
1236
1673
|
return;
|
|
1237
|
-
|
|
1238
|
-
this.
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1674
|
+
// Chunk under SQLite's parameter limit, matching every other IN-list in
|
|
1675
|
+
// this file. The internal resolution path uses deleteSpecificResolvedReferences
|
|
1676
|
+
// instead, but QueryBuilder is part of the public API, so a library consumer
|
|
1677
|
+
// passing more ids than SQLITE_MAX_VARIABLE_NUMBER (32766 on the bundled
|
|
1678
|
+
// node:sqlite) would otherwise hit "too many SQL variables". (#540, #1001)
|
|
1679
|
+
for (let i = 0; i < fromNodeIds.length; i += SQLITE_PARAM_CHUNK_SIZE) {
|
|
1680
|
+
const chunk = fromNodeIds.slice(i, i + SQLITE_PARAM_CHUNK_SIZE);
|
|
1681
|
+
const placeholders = chunk.map(() => '?').join(',');
|
|
1682
|
+
this.db.prepare(`DELETE FROM unresolved_refs WHERE from_node_id IN (${placeholders})`).run(...chunk);
|
|
1683
|
+
}
|
|
1246
1684
|
}
|
|
1247
1685
|
/**
|
|
1248
1686
|
* Delete specific resolved references by (fromNodeId, referenceName, referenceKind) tuples.
|
|
@@ -1259,9 +1697,108 @@ class QueryBuilder {
|
|
|
1259
1697
|
});
|
|
1260
1698
|
deleteMany(refs);
|
|
1261
1699
|
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Mark refs a completed resolution pass could not resolve as status='failed'
|
|
1702
|
+
* instead of deleting them (#1240). Failed rows are invisible to the pending
|
|
1703
|
+
* count/batch readers (so drain loops and the #1187 orphan sweep still
|
|
1704
|
+
* terminate) but stay queryable by name_tail so a later sync can retry them
|
|
1705
|
+
* when a changed file introduces a symbol that could satisfy them. name_tail
|
|
1706
|
+
* is (re)written here so rows inserted before the v8 migration get their
|
|
1707
|
+
* tail the first time they're attempted.
|
|
1708
|
+
*/
|
|
1709
|
+
markReferencesFailed(refs) {
|
|
1710
|
+
if (refs.length === 0)
|
|
1711
|
+
return;
|
|
1712
|
+
const stmt = this.db.prepare("UPDATE unresolved_refs SET status = 'failed', name_tail = ? WHERE from_node_id = ? AND reference_name = ? AND reference_kind = ?");
|
|
1713
|
+
const markMany = this.db.transaction((items) => {
|
|
1714
|
+
for (const ref of items) {
|
|
1715
|
+
stmt.run(referenceNameTail(ref.referenceName), ref.fromNodeId, ref.referenceName, ref.referenceKind);
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
markMany(refs);
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Failed refs whose name tail matches one of the given symbol names — the
|
|
1722
|
+
* candidates a sync should retry after files carrying those names changed
|
|
1723
|
+
* (#1240). Names matching more than `perNameCeiling` failed refs are
|
|
1724
|
+
* skipped entirely: at that population a name is external/builtin noise
|
|
1725
|
+
* (`get`, `map`, …) that one new definition won't resolve — the same
|
|
1726
|
+
* rationale as resolution's AMBIGUOUS_NAME_CEILING (#999) — and retrying an
|
|
1727
|
+
* arbitrary subset would be both wasted work and incoherent coverage.
|
|
1728
|
+
*/
|
|
1729
|
+
getRetryableFailedReferences(names, perNameCeiling = 500) {
|
|
1730
|
+
if (names.length === 0)
|
|
1731
|
+
return [];
|
|
1732
|
+
// Pass 1: per-tail counts, chunked under the SQLite parameter limit.
|
|
1733
|
+
const retryNames = [];
|
|
1734
|
+
for (let i = 0; i < names.length; i += SQLITE_PARAM_CHUNK_SIZE) {
|
|
1735
|
+
const chunk = names.slice(i, i + SQLITE_PARAM_CHUNK_SIZE);
|
|
1736
|
+
const placeholders = chunk.map(() => '?').join(',');
|
|
1737
|
+
const counts = this.db
|
|
1738
|
+
.prepare(`SELECT name_tail, COUNT(*) as count FROM unresolved_refs WHERE status = 'failed' AND name_tail IN (${placeholders}) GROUP BY name_tail`)
|
|
1739
|
+
.all(...chunk);
|
|
1740
|
+
for (const row of counts) {
|
|
1741
|
+
if (row.count <= perNameCeiling)
|
|
1742
|
+
retryNames.push(row.name_tail);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
if (retryNames.length === 0)
|
|
1746
|
+
return [];
|
|
1747
|
+
// Pass 2: load the surviving rows.
|
|
1748
|
+
const rows = [];
|
|
1749
|
+
for (let i = 0; i < retryNames.length; i += SQLITE_PARAM_CHUNK_SIZE) {
|
|
1750
|
+
const chunk = retryNames.slice(i, i + SQLITE_PARAM_CHUNK_SIZE);
|
|
1751
|
+
const placeholders = chunk.map(() => '?').join(',');
|
|
1752
|
+
const chunkRows = this.db
|
|
1753
|
+
.prepare(`SELECT * FROM unresolved_refs WHERE status = 'failed' AND name_tail IN (${placeholders})`)
|
|
1754
|
+
.all(...chunk);
|
|
1755
|
+
rows.push(...chunkRows);
|
|
1756
|
+
}
|
|
1757
|
+
return rows.map((row) => ({
|
|
1758
|
+
fromNodeId: row.from_node_id,
|
|
1759
|
+
referenceName: row.reference_name,
|
|
1760
|
+
referenceKind: row.reference_kind,
|
|
1761
|
+
line: row.line,
|
|
1762
|
+
column: row.col,
|
|
1763
|
+
candidates: row.candidates ? (0, utils_1.safeJsonParse)(row.candidates, undefined) : undefined,
|
|
1764
|
+
filePath: row.file_path,
|
|
1765
|
+
language: row.language,
|
|
1766
|
+
}));
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Distinct node names present in the given files — the symbol names a sync
|
|
1770
|
+
* pass uses to look up retryable failed refs after those files changed.
|
|
1771
|
+
*/
|
|
1772
|
+
getNodeNamesByFiles(filePaths) {
|
|
1773
|
+
if (filePaths.length === 0)
|
|
1774
|
+
return [];
|
|
1775
|
+
const names = new Set();
|
|
1776
|
+
for (let i = 0; i < filePaths.length; i += SQLITE_PARAM_CHUNK_SIZE) {
|
|
1777
|
+
const chunk = filePaths.slice(i, i + SQLITE_PARAM_CHUNK_SIZE);
|
|
1778
|
+
const placeholders = chunk.map(() => '?').join(',');
|
|
1779
|
+
const rows = this.db
|
|
1780
|
+
.prepare(`SELECT DISTINCT name FROM nodes WHERE file_path IN (${placeholders})`)
|
|
1781
|
+
.all(...chunk);
|
|
1782
|
+
for (const row of rows)
|
|
1783
|
+
names.add(row.name);
|
|
1784
|
+
}
|
|
1785
|
+
return [...names];
|
|
1786
|
+
}
|
|
1262
1787
|
// ===========================================================================
|
|
1263
1788
|
// Statistics
|
|
1264
1789
|
// ===========================================================================
|
|
1790
|
+
/**
|
|
1791
|
+
* Lightweight (nodes, edges) count snapshot. Used around an index/sync
|
|
1792
|
+
* run to compute true additions across extraction + resolution +
|
|
1793
|
+
* synthesis — the per-phase counter in the orchestrator only sees
|
|
1794
|
+
* extraction's contribution, which is why the CLI summary under-reported
|
|
1795
|
+
* the edge count (resolution + synthesizer edges were invisible).
|
|
1796
|
+
*/
|
|
1797
|
+
getNodeAndEdgeCount() {
|
|
1798
|
+
return this.db
|
|
1799
|
+
.prepare('SELECT (SELECT COUNT(*) FROM nodes) AS nodes, (SELECT COUNT(*) FROM edges) AS edges')
|
|
1800
|
+
.get();
|
|
1801
|
+
}
|
|
1265
1802
|
/**
|
|
1266
1803
|
* Get graph statistics
|
|
1267
1804
|
*/
|