@optave/codegraph 3.5.0 → 3.7.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 +47 -21
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +119 -127
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.js +14 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.js +11 -13
- package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
- package/dist/db/connection.d.ts +12 -2
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +81 -53
- package/dist/db/connection.js.map +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +38 -32
- package/dist/db/migrations.js.map +1 -1
- package/dist/domain/analysis/context.d.ts.map +1 -1
- package/dist/domain/analysis/context.js +51 -66
- package/dist/domain/analysis/context.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +62 -70
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/diff-impact.d.ts +9 -7
- package/dist/domain/analysis/diff-impact.d.ts.map +1 -1
- package/dist/domain/analysis/exports.d.ts.map +1 -1
- package/dist/domain/analysis/exports.js +29 -33
- package/dist/domain/analysis/exports.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts +15 -17
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +35 -65
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +91 -6
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/query-helpers.d.ts +20 -0
- package/dist/domain/analysis/query-helpers.d.ts.map +1 -0
- package/dist/domain/analysis/query-helpers.js +27 -0
- package/dist/domain/analysis/query-helpers.js.map +1 -0
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +15 -9
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +3 -2
- package/dist/domain/graph/builder/incremental.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +69 -3
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +7 -51
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.js +7 -5
- package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.js +2 -2
- package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +2 -2
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +124 -105
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.js +28 -15
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +3 -2
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/resolve.d.ts +0 -4
- package/dist/domain/graph/resolve.d.ts.map +1 -1
- package/dist/domain/graph/resolve.js +32 -48
- package/dist/domain/graph/resolve.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +12 -12
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +1 -1
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +206 -101
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/search/cli-formatter.d.ts.map +1 -1
- package/dist/domain/search/search/cli-formatter.js +88 -83
- package/dist/domain/search/search/cli-formatter.js.map +1 -1
- package/dist/extractors/bash.d.ts +6 -0
- package/dist/extractors/bash.d.ts.map +1 -0
- package/dist/extractors/bash.js +91 -0
- package/dist/extractors/bash.js.map +1 -0
- package/dist/extractors/c.d.ts +6 -0
- package/dist/extractors/c.d.ts.map +1 -0
- package/dist/extractors/c.js +204 -0
- package/dist/extractors/c.js.map +1 -0
- package/dist/extractors/cpp.d.ts +6 -0
- package/dist/extractors/cpp.d.ts.map +1 -0
- package/dist/extractors/cpp.js +283 -0
- package/dist/extractors/cpp.js.map +1 -0
- package/dist/extractors/csharp.d.ts.map +1 -1
- package/dist/extractors/csharp.js +42 -54
- package/dist/extractors/csharp.js.map +1 -1
- package/dist/extractors/dart.d.ts +6 -0
- package/dist/extractors/dart.d.ts.map +1 -0
- package/dist/extractors/dart.js +277 -0
- package/dist/extractors/dart.js.map +1 -0
- package/dist/extractors/elixir.d.ts +9 -0
- package/dist/extractors/elixir.d.ts.map +1 -0
- package/dist/extractors/elixir.js +223 -0
- package/dist/extractors/elixir.js.map +1 -0
- package/dist/extractors/go.d.ts.map +1 -1
- package/dist/extractors/go.js +126 -130
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/haskell.d.ts +8 -0
- package/dist/extractors/haskell.d.ts.map +1 -0
- package/dist/extractors/haskell.js +217 -0
- package/dist/extractors/haskell.js.map +1 -0
- package/dist/extractors/hcl.js +6 -6
- package/dist/extractors/hcl.js.map +1 -1
- package/dist/extractors/helpers.d.ts +32 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +74 -0
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/index.d.ts +12 -0
- package/dist/extractors/index.d.ts.map +1 -1
- package/dist/extractors/index.js +12 -0
- package/dist/extractors/index.js.map +1 -1
- package/dist/extractors/java.d.ts.map +1 -1
- package/dist/extractors/java.js +32 -47
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +306 -292
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.d.ts +6 -0
- package/dist/extractors/kotlin.d.ts.map +1 -0
- package/dist/extractors/kotlin.js +275 -0
- package/dist/extractors/kotlin.js.map +1 -0
- package/dist/extractors/lua.d.ts +6 -0
- package/dist/extractors/lua.d.ts.map +1 -0
- package/dist/extractors/lua.js +162 -0
- package/dist/extractors/lua.js.map +1 -0
- package/dist/extractors/ocaml.d.ts +6 -0
- package/dist/extractors/ocaml.d.ts.map +1 -0
- package/dist/extractors/ocaml.js +236 -0
- package/dist/extractors/ocaml.js.map +1 -0
- package/dist/extractors/php.d.ts.map +1 -1
- package/dist/extractors/php.js +39 -44
- package/dist/extractors/php.js.map +1 -1
- package/dist/extractors/python.d.ts.map +1 -1
- package/dist/extractors/python.js +75 -93
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/ruby.js +6 -13
- package/dist/extractors/ruby.js.map +1 -1
- package/dist/extractors/rust.d.ts.map +1 -1
- package/dist/extractors/rust.js +58 -83
- package/dist/extractors/rust.js.map +1 -1
- package/dist/extractors/scala.d.ts +6 -0
- package/dist/extractors/scala.d.ts.map +1 -0
- package/dist/extractors/scala.js +269 -0
- package/dist/extractors/scala.js.map +1 -0
- package/dist/extractors/swift.d.ts +6 -0
- package/dist/extractors/swift.d.ts.map +1 -0
- package/dist/extractors/swift.js +275 -0
- package/dist/extractors/swift.js.map +1 -0
- package/dist/extractors/zig.d.ts +9 -0
- package/dist/extractors/zig.d.ts.map +1 -0
- package/dist/extractors/zig.js +276 -0
- package/dist/extractors/zig.js.map +1 -0
- package/dist/features/ast.d.ts +2 -0
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +9 -24
- package/dist/features/ast.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +17 -21
- package/dist/features/audit.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +47 -3
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/cfg.d.ts +7 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +72 -61
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/check.d.ts.map +1 -1
- package/dist/features/check.js +79 -62
- package/dist/features/check.js.map +1 -1
- package/dist/features/complexity-query.d.ts.map +1 -1
- package/dist/features/complexity-query.js +142 -137
- package/dist/features/complexity-query.js.map +1 -1
- package/dist/features/complexity.d.ts +7 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +62 -1
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts +7 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +356 -188
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/graph-enrichment.d.ts.map +1 -1
- package/dist/features/graph-enrichment.js +117 -104
- package/dist/features/graph-enrichment.js.map +1 -1
- package/dist/features/sequence.d.ts.map +1 -1
- package/dist/features/sequence.js +25 -4
- package/dist/features/sequence.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +29 -4
- package/dist/features/structure-query.js.map +1 -1
- package/dist/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +35 -15
- package/dist/features/structure.js.map +1 -1
- package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/adapter.js +88 -73
- package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
- package/dist/graph/algorithms/leiden/index.js +43 -28
- package/dist/graph/algorithms/leiden/index.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +90 -104
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
- package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/partition.js +89 -106
- package/dist/graph/algorithms/leiden/partition.js.map +1 -1
- package/dist/graph/model.d.ts +2 -0
- package/dist/graph/model.d.ts.map +1 -1
- package/dist/graph/model.js +20 -8
- package/dist/graph/model.js.map +1 -1
- package/dist/infrastructure/config.d.ts +0 -8
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +73 -62
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/registry.d.ts +0 -8
- package/dist/infrastructure/registry.d.ts.map +1 -1
- package/dist/infrastructure/registry.js +12 -14
- package/dist/infrastructure/registry.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +45 -36
- package/dist/mcp/server.js.map +1 -1
- package/dist/presentation/audit.d.ts.map +1 -1
- package/dist/presentation/audit.js +61 -57
- package/dist/presentation/audit.js.map +1 -1
- package/dist/presentation/branch-compare.d.ts.map +1 -1
- package/dist/presentation/branch-compare.js +56 -38
- package/dist/presentation/branch-compare.js.map +1 -1
- package/dist/presentation/check.d.ts.map +1 -1
- package/dist/presentation/check.js +30 -32
- package/dist/presentation/check.js.map +1 -1
- package/dist/presentation/colors.d.ts.map +1 -1
- package/dist/presentation/colors.js +2 -0
- package/dist/presentation/colors.js.map +1 -1
- package/dist/presentation/complexity.d.ts.map +1 -1
- package/dist/presentation/complexity.js +25 -19
- package/dist/presentation/complexity.js.map +1 -1
- package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
- package/dist/presentation/queries-cli/exports.js +15 -15
- package/dist/presentation/queries-cli/exports.js.map +1 -1
- package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
- package/dist/presentation/queries-cli/impact.js +29 -19
- package/dist/presentation/queries-cli/impact.js.map +1 -1
- package/dist/types.d.ts +182 -7
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-dart.wasm +0 -0
- package/grammars/tree-sitter-elixir.wasm +0 -0
- package/grammars/tree-sitter-haskell.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-lua.wasm +0 -0
- package/grammars/tree-sitter-ocaml.wasm +0 -0
- package/grammars/tree-sitter-scala.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-zig.wasm +0 -0
- package/package.json +19 -7
- package/src/ast-analysis/engine.ts +147 -138
- package/src/ast-analysis/visitors/ast-store-visitor.ts +15 -2
- package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
- package/src/db/connection.ts +90 -59
- package/src/db/index.ts +1 -0
- package/src/db/migrations.ts +36 -32
- package/src/domain/analysis/context.ts +73 -75
- package/src/domain/analysis/dependencies.ts +78 -68
- package/src/domain/analysis/exports.ts +45 -34
- package/src/domain/analysis/fn-impact.ts +67 -64
- package/src/domain/analysis/module-map.ts +103 -8
- package/src/domain/analysis/query-helpers.ts +35 -0
- package/src/domain/graph/builder/helpers.ts +12 -6
- package/src/domain/graph/builder/incremental.ts +3 -2
- package/src/domain/graph/builder/pipeline.ts +71 -3
- package/src/domain/graph/builder/stages/build-edges.ts +10 -75
- package/src/domain/graph/builder/stages/build-structure.ts +9 -7
- package/src/domain/graph/builder/stages/collect-files.ts +2 -2
- package/src/domain/graph/builder/stages/detect-changes.ts +7 -2
- package/src/domain/graph/builder/stages/finalize.ts +159 -125
- package/src/domain/graph/builder/stages/insert-nodes.ts +32 -21
- package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
- package/src/domain/graph/resolve.ts +34 -46
- package/src/domain/graph/watcher.ts +12 -14
- package/src/domain/parser.ts +222 -97
- package/src/domain/search/search/cli-formatter.ts +121 -94
- package/src/extractors/bash.ts +97 -0
- package/src/extractors/c.ts +212 -0
- package/src/extractors/cpp.ts +298 -0
- package/src/extractors/csharp.ts +53 -56
- package/src/extractors/dart.ts +304 -0
- package/src/extractors/elixir.ts +251 -0
- package/src/extractors/go.ts +152 -134
- package/src/extractors/haskell.ts +235 -0
- package/src/extractors/hcl.ts +6 -6
- package/src/extractors/helpers.ts +93 -1
- package/src/extractors/index.ts +12 -0
- package/src/extractors/java.ts +43 -48
- package/src/extractors/javascript.ts +328 -281
- package/src/extractors/kotlin.ts +293 -0
- package/src/extractors/lua.ts +169 -0
- package/src/extractors/ocaml.ts +259 -0
- package/src/extractors/php.ts +46 -40
- package/src/extractors/python.ts +81 -104
- package/src/extractors/ruby.ts +6 -13
- package/src/extractors/rust.ts +65 -85
- package/src/extractors/scala.ts +285 -0
- package/src/extractors/swift.ts +293 -0
- package/src/extractors/zig.ts +294 -0
- package/src/features/ast.ts +10 -25
- package/src/features/audit.ts +24 -20
- package/src/features/branch-compare.ts +51 -4
- package/src/features/cfg.ts +113 -65
- package/src/features/check.ts +90 -74
- package/src/features/complexity-query.ts +181 -163
- package/src/features/complexity.ts +64 -1
- package/src/features/dataflow.ts +462 -217
- package/src/features/graph-enrichment.ts +161 -117
- package/src/features/sequence.ts +27 -4
- package/src/features/structure-query.ts +43 -4
- package/src/features/structure.ts +50 -22
- package/src/graph/algorithms/leiden/adapter.ts +126 -71
- package/src/graph/algorithms/leiden/index.ts +67 -28
- package/src/graph/algorithms/leiden/optimiser.ts +114 -105
- package/src/graph/algorithms/leiden/partition.ts +131 -98
- package/src/graph/model.ts +19 -7
- package/src/infrastructure/config.ts +60 -58
- package/src/infrastructure/registry.ts +17 -14
- package/src/mcp/server.ts +46 -37
- package/src/presentation/audit.ts +72 -67
- package/src/presentation/branch-compare.ts +54 -50
- package/src/presentation/check.ts +34 -34
- package/src/presentation/colors.ts +2 -0
- package/src/presentation/complexity.ts +39 -33
- package/src/presentation/queries-cli/exports.ts +17 -17
- package/src/presentation/queries-cli/impact.ts +30 -22
- package/src/types.ts +195 -7
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { openReadonlyOrFail } from '../../db/index.js';
|
|
2
|
+
import { loadConfig } from '../../infrastructure/config.js';
|
|
3
|
+
import type { BetterSqlite3Database, CodegraphConfig } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Open a readonly DB connection, run `fn`, and close the DB on completion.
|
|
7
|
+
* Eliminates the duplicated `openReadonlyOrFail` + `try/finally/db.close()` pattern
|
|
8
|
+
* that appears in every analysis query function.
|
|
9
|
+
*/
|
|
10
|
+
export function withReadonlyDb<T>(
|
|
11
|
+
customDbPath: string | undefined,
|
|
12
|
+
fn: (db: BetterSqlite3Database) => T,
|
|
13
|
+
): T {
|
|
14
|
+
const db = openReadonlyOrFail(customDbPath);
|
|
15
|
+
try {
|
|
16
|
+
return fn(db);
|
|
17
|
+
} finally {
|
|
18
|
+
db.close();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve common analysis options into a normalized form.
|
|
24
|
+
* Shared across fn-impact, context, dependencies, and exports modules.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveAnalysisOpts(opts: { noTests?: boolean; config?: CodegraphConfig }): {
|
|
27
|
+
noTests: boolean;
|
|
28
|
+
config: CodegraphConfig;
|
|
29
|
+
displayOpts: Record<string, unknown>;
|
|
30
|
+
} {
|
|
31
|
+
const noTests = opts.noTests || false;
|
|
32
|
+
const config = opts.config || loadConfig();
|
|
33
|
+
const displayOpts = config.display || {};
|
|
34
|
+
return { noTests, config, displayOpts };
|
|
35
|
+
}
|
|
@@ -47,6 +47,17 @@ export const BUILTIN_RECEIVERS: Set<string> = new Set([
|
|
|
47
47
|
'require',
|
|
48
48
|
]);
|
|
49
49
|
|
|
50
|
+
/** Check if a directory entry should be skipped (ignored dirs, dotfiles). */
|
|
51
|
+
function shouldSkipEntry(entry: fs.Dirent, extraIgnore: Set<string> | null): boolean {
|
|
52
|
+
if (entry.name.startsWith('.') && entry.name !== '.') {
|
|
53
|
+
if (IGNORE_DIRS.has(entry.name)) return true;
|
|
54
|
+
if (entry.isDirectory()) return true;
|
|
55
|
+
}
|
|
56
|
+
if (IGNORE_DIRS.has(entry.name)) return true;
|
|
57
|
+
if (extraIgnore?.has(entry.name)) return true;
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
50
61
|
/**
|
|
51
62
|
* Recursively collect all source files under `dir`.
|
|
52
63
|
* When `directories` is a Set, also tracks which directories contain files.
|
|
@@ -100,12 +111,7 @@ export function collectFiles(
|
|
|
100
111
|
}
|
|
101
112
|
|
|
102
113
|
for (const entry of entries) {
|
|
103
|
-
if (
|
|
104
|
-
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
105
|
-
if (entry.isDirectory()) continue;
|
|
106
|
-
}
|
|
107
|
-
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
108
|
-
if (extraIgnore?.has(entry.name)) continue;
|
|
114
|
+
if (shouldSkipEntry(entry, extraIgnore)) continue;
|
|
109
115
|
|
|
110
116
|
const full = path.join(dir, entry.name);
|
|
111
117
|
if (entry.isDirectory()) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import fs from 'node:fs';
|
|
11
11
|
import path from 'node:path';
|
|
12
12
|
import { bulkNodeIdsByFile } from '../../../db/index.js';
|
|
13
|
-
import { warn } from '../../../infrastructure/logger.js';
|
|
13
|
+
import { debug, warn } from '../../../infrastructure/logger.js';
|
|
14
14
|
import { normalizePath } from '../../../shared/constants.js';
|
|
15
15
|
import type {
|
|
16
16
|
BetterSqlite3Database,
|
|
@@ -154,7 +154,8 @@ async function parseReverseDep(
|
|
|
154
154
|
let code: string;
|
|
155
155
|
try {
|
|
156
156
|
code = readFileSafe(absPath);
|
|
157
|
-
} catch {
|
|
157
|
+
} catch (e: unknown) {
|
|
158
|
+
debug(`parseReverseDep: cannot read ${absPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
158
159
|
return null;
|
|
159
160
|
}
|
|
160
161
|
|
|
@@ -35,6 +35,18 @@ function initializeEngine(ctx: PipelineContext): void {
|
|
|
35
35
|
dataflow: ctx.opts.dataflow !== false,
|
|
36
36
|
ast: ctx.opts.ast !== false,
|
|
37
37
|
nativeDb: ctx.nativeDb,
|
|
38
|
+
// WAL checkpoint callbacks for dual-connection WAL guard (#696).
|
|
39
|
+
// Feature modules (ast, cfg, complexity, dataflow) receive `db` as a
|
|
40
|
+
// parameter and cannot tolerate close/reopen (stale reference). Instead,
|
|
41
|
+
// checkpoint the WAL so native writes start with a clean slate. Features
|
|
42
|
+
// return early on native success and never read native-written WAL data
|
|
43
|
+
// through the JS connection, so a post-write checkpoint is unnecessary.
|
|
44
|
+
suspendJsDb: ctx.nativeDb
|
|
45
|
+
? () => {
|
|
46
|
+
ctx.db.pragma('wal_checkpoint(TRUNCATE)');
|
|
47
|
+
}
|
|
48
|
+
: undefined,
|
|
49
|
+
resumeJsDb: ctx.nativeDb ? () => {} : undefined,
|
|
38
50
|
};
|
|
39
51
|
const { name: engineName, version: engineVersion } = getActiveEngine(ctx.engineOpts);
|
|
40
52
|
ctx.engineName = engineName as 'native' | 'wasm';
|
|
@@ -48,9 +60,11 @@ function checkEngineSchemaMismatch(ctx: PipelineContext): void {
|
|
|
48
60
|
ctx.forceFullRebuild = false;
|
|
49
61
|
if (!ctx.incremental) return;
|
|
50
62
|
|
|
51
|
-
// Route metadata reads through NativeDatabase when
|
|
63
|
+
// Route metadata reads through NativeDatabase only when using the native engine,
|
|
64
|
+
// to avoid dual-SQLite WAL conflicts (rusqlite + better-sqlite3 on same file).
|
|
65
|
+
const useNativeDb = ctx.engineName === 'native' && !!ctx.nativeDb;
|
|
52
66
|
const meta = (key: string): string | null =>
|
|
53
|
-
|
|
67
|
+
useNativeDb ? ctx.nativeDb!.getBuildMeta(key) : getBuildMeta(ctx.db, key);
|
|
54
68
|
|
|
55
69
|
const prevEngine = meta('engine');
|
|
56
70
|
if (prevEngine && prevEngine !== ctx.engineName) {
|
|
@@ -109,8 +123,10 @@ function setupPipeline(ctx: PipelineContext): void {
|
|
|
109
123
|
} catch (err) {
|
|
110
124
|
warn(`NativeDatabase init failed, falling back to JS: ${(err as Error).message}`);
|
|
111
125
|
ctx.nativeDb = undefined;
|
|
112
|
-
initSchema(ctx.db);
|
|
113
126
|
}
|
|
127
|
+
// Always run JS initSchema so better-sqlite3 sees the schema —
|
|
128
|
+
// nativeDb is closed during pipeline stages and reopened for analyses.
|
|
129
|
+
initSchema(ctx.db);
|
|
114
130
|
} else {
|
|
115
131
|
initSchema(ctx.db);
|
|
116
132
|
}
|
|
@@ -156,6 +172,26 @@ function formatTimingResult(ctx: PipelineContext): BuildResult {
|
|
|
156
172
|
// ── Pipeline stages execution ───────────────────────────────────────────
|
|
157
173
|
|
|
158
174
|
async function runPipelineStages(ctx: PipelineContext): Promise<void> {
|
|
175
|
+
// Prevent dual-connection WAL corruption during pipeline stages: when both
|
|
176
|
+
// better-sqlite3 (ctx.db) and rusqlite (ctx.nativeDb) are open to the same
|
|
177
|
+
// WAL-mode file, native writes corrupt the DB. Close nativeDb so stages
|
|
178
|
+
// use JS fallback paths. Reopened before runAnalyses for feature modules
|
|
179
|
+
// that use suspendJsDb/resumeJsDb WAL checkpoint pattern (#696).
|
|
180
|
+
const hadNativeDb = !!ctx.nativeDb;
|
|
181
|
+
if (ctx.db && ctx.nativeDb) {
|
|
182
|
+
try {
|
|
183
|
+
ctx.nativeDb.close();
|
|
184
|
+
} catch {
|
|
185
|
+
/* ignore close errors */
|
|
186
|
+
}
|
|
187
|
+
ctx.nativeDb = undefined;
|
|
188
|
+
// Also clear stale reference in engineOpts to prevent stages from
|
|
189
|
+
// calling methods on the closed NativeDatabase.
|
|
190
|
+
if (ctx.engineOpts?.nativeDb) {
|
|
191
|
+
ctx.engineOpts.nativeDb = undefined;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
159
195
|
await collectFiles(ctx);
|
|
160
196
|
await detectChanges(ctx);
|
|
161
197
|
|
|
@@ -166,7 +202,39 @@ async function runPipelineStages(ctx: PipelineContext): Promise<void> {
|
|
|
166
202
|
await resolveImports(ctx);
|
|
167
203
|
await buildEdges(ctx);
|
|
168
204
|
await buildStructure(ctx);
|
|
205
|
+
|
|
206
|
+
// Reopen nativeDb for feature modules (ast, cfg, complexity, dataflow)
|
|
207
|
+
// which use suspendJsDb/resumeJsDb WAL checkpoint before native writes.
|
|
208
|
+
if (hadNativeDb) {
|
|
209
|
+
const native = loadNative();
|
|
210
|
+
if (native?.NativeDatabase) {
|
|
211
|
+
try {
|
|
212
|
+
ctx.nativeDb = native.NativeDatabase.openReadWrite(ctx.dbPath);
|
|
213
|
+
if (ctx.engineOpts) {
|
|
214
|
+
ctx.engineOpts.nativeDb = ctx.nativeDb;
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
ctx.nativeDb = undefined;
|
|
218
|
+
if (ctx.engineOpts) {
|
|
219
|
+
ctx.engineOpts.nativeDb = undefined;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
169
225
|
await runAnalyses(ctx);
|
|
226
|
+
|
|
227
|
+
// Close nativeDb after analyses — finalize uses JS paths for setBuildMeta
|
|
228
|
+
// and closeDbPair handles cleanup. Avoids dual-connection during finalize.
|
|
229
|
+
if (ctx.nativeDb) {
|
|
230
|
+
try {
|
|
231
|
+
ctx.nativeDb.close();
|
|
232
|
+
} catch {
|
|
233
|
+
/* ignore close errors */
|
|
234
|
+
}
|
|
235
|
+
ctx.nativeDb = undefined;
|
|
236
|
+
}
|
|
237
|
+
|
|
170
238
|
await finalize(ctx);
|
|
171
239
|
}
|
|
172
240
|
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { performance } from 'node:perf_hooks';
|
|
9
|
-
import { getNodeId } from '
|
|
10
|
-
import { debug } from '
|
|
11
|
-
import { loadNative } from '
|
|
9
|
+
import { getNodeId } from '../../../../db/index.js';
|
|
10
|
+
import { debug } from '../../../../infrastructure/logger.js';
|
|
11
|
+
import { loadNative } from '../../../../infrastructure/native.js';
|
|
12
12
|
import type {
|
|
13
13
|
BetterSqlite3Database,
|
|
14
14
|
Call,
|
|
@@ -18,10 +18,11 @@ import type {
|
|
|
18
18
|
NativeAddon,
|
|
19
19
|
NodeRow,
|
|
20
20
|
TypeMapEntry,
|
|
21
|
-
} from '
|
|
21
|
+
} from '../../../../types.js';
|
|
22
22
|
import { computeConfidence } from '../../resolve.js';
|
|
23
23
|
import type { PipelineContext } from '../context.js';
|
|
24
24
|
import { BUILTIN_RECEIVERS, batchInsertEdges } from '../helpers.js';
|
|
25
|
+
|
|
25
26
|
import { getResolved, isBarrelFile, resolveBarrelExport } from './resolve-imports.js';
|
|
26
27
|
|
|
27
28
|
// ── Local types ──────────────────────────────────────────────────────────
|
|
@@ -61,12 +62,6 @@ interface NativeEdge {
|
|
|
61
62
|
dynamic: number;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
/** TypeMap entry used in receiver supplement (normalized from native format). */
|
|
65
|
-
interface NormalizedTypeEntry {
|
|
66
|
-
type: string;
|
|
67
|
-
confidence: number;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
65
|
// ── Node lookup setup ───────────────────────────────────────────────────
|
|
71
66
|
|
|
72
67
|
function makeGetNodeIdStmt(db: BetterSqlite3Database): NodeIdStmt {
|
|
@@ -210,14 +205,6 @@ function buildCallEdgesNative(
|
|
|
210
205
|
for (const e of nativeEdges) {
|
|
211
206
|
allEdgeRows.push([e.sourceId, e.targetId, e.kind, e.confidence, e.dynamic]);
|
|
212
207
|
}
|
|
213
|
-
|
|
214
|
-
// Older native binaries (< 3.2.0) don't emit receiver or type-resolved method-call
|
|
215
|
-
// edges. Supplement them on the JS side if the native binary missed them.
|
|
216
|
-
// TODO: Remove once all published native binaries handle receivers (>= 3.2.0)
|
|
217
|
-
const hasReceiver = nativeEdges.some((e) => e.kind === 'receiver');
|
|
218
|
-
if (!hasReceiver) {
|
|
219
|
-
supplementReceiverEdges(ctx, nativeFiles, getNodeIdStmt, allEdgeRows);
|
|
220
|
-
}
|
|
221
208
|
}
|
|
222
209
|
|
|
223
210
|
function buildImportedNamesForNative(
|
|
@@ -242,58 +229,6 @@ function buildImportedNamesForNative(
|
|
|
242
229
|
return importedNames;
|
|
243
230
|
}
|
|
244
231
|
|
|
245
|
-
// ── Receiver edge supplement for older native binaries ──────────────────
|
|
246
|
-
|
|
247
|
-
function supplementReceiverEdges(
|
|
248
|
-
ctx: PipelineContext,
|
|
249
|
-
nativeFiles: NativeFileEntry[],
|
|
250
|
-
getNodeIdStmt: NodeIdStmt,
|
|
251
|
-
allEdgeRows: EdgeRowTuple[],
|
|
252
|
-
): void {
|
|
253
|
-
const seenCallEdges = new Set<string>();
|
|
254
|
-
// Collect existing edges to avoid duplicates
|
|
255
|
-
for (const row of allEdgeRows) {
|
|
256
|
-
seenCallEdges.add(`${row[0]}|${row[1]}|${row[2]}`);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
for (const nf of nativeFiles) {
|
|
260
|
-
const relPath = nf.file;
|
|
261
|
-
const typeMap = new Map<string, NormalizedTypeEntry>(
|
|
262
|
-
nf.typeMap.map((t) => [t.name, { type: t.typeName, confidence: t.confidence ?? 0.9 }]),
|
|
263
|
-
);
|
|
264
|
-
const fileNodeRow = { id: nf.fileNodeId };
|
|
265
|
-
|
|
266
|
-
for (const call of nf.calls) {
|
|
267
|
-
if (!call.receiver || BUILTIN_RECEIVERS.has(call.receiver)) continue;
|
|
268
|
-
if (call.receiver === 'this' || call.receiver === 'self' || call.receiver === 'super')
|
|
269
|
-
continue;
|
|
270
|
-
|
|
271
|
-
const caller = findCaller(call, nf.definitions, relPath, getNodeIdStmt, fileNodeRow);
|
|
272
|
-
|
|
273
|
-
// Receiver edge: caller → receiver type node
|
|
274
|
-
buildReceiverEdge(ctx, call, caller, relPath, seenCallEdges, allEdgeRows, typeMap);
|
|
275
|
-
|
|
276
|
-
// Type-resolved method call: caller → Type.method
|
|
277
|
-
const typeEntry = typeMap.get(call.receiver);
|
|
278
|
-
const typeName = typeEntry ? typeEntry.type : null;
|
|
279
|
-
if (typeName) {
|
|
280
|
-
const qualifiedName = `${typeName}.${call.name}`;
|
|
281
|
-
const targets = (ctx.nodesByName.get(qualifiedName) || []).filter(
|
|
282
|
-
(n) => n.kind === 'method',
|
|
283
|
-
);
|
|
284
|
-
for (const t of targets) {
|
|
285
|
-
const key = `${caller.id}|${t.id}|calls`;
|
|
286
|
-
if (t.id !== caller.id && !seenCallEdges.has(key)) {
|
|
287
|
-
seenCallEdges.add(key);
|
|
288
|
-
const confidence = computeConfidence(relPath, t.file, null);
|
|
289
|
-
allEdgeRows.push([caller.id, t.id, 'calls', confidence, call.dynamic ? 1 : 0]);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
232
|
// ── Call edges (JS fallback) ────────────────────────────────────────────
|
|
298
233
|
|
|
299
234
|
function buildCallEdgesJS(
|
|
@@ -495,7 +430,7 @@ function buildReceiverEdge(
|
|
|
495
430
|
relPath: string,
|
|
496
431
|
seenCallEdges: Set<string>,
|
|
497
432
|
allEdgeRows: EdgeRowTuple[],
|
|
498
|
-
typeMap: Map<string, TypeMapEntry |
|
|
433
|
+
typeMap: Map<string, TypeMapEntry | string>,
|
|
499
434
|
): void {
|
|
500
435
|
const receiverKinds = new Set(['class', 'struct', 'interface', 'type', 'module']);
|
|
501
436
|
const typeEntry = typeMap?.get(call.receiver!);
|
|
@@ -608,7 +543,7 @@ function loadNodes(ctx: PipelineContext): { rows: QueryNodeRow[]; scoped: boolea
|
|
|
608
543
|
|
|
609
544
|
/**
|
|
610
545
|
* For scoped node loading, patch nodesByName.get with a lazy SQL fallback
|
|
611
|
-
* so global name-only lookups (resolveByMethodOrGlobal
|
|
546
|
+
* so global name-only lookups (resolveByMethodOrGlobal)
|
|
612
547
|
* can still find nodes outside the scoped set.
|
|
613
548
|
*/
|
|
614
549
|
function addLazyFallback(ctx: PipelineContext, scopedLoad: boolean): void {
|
|
@@ -673,7 +608,7 @@ export async function buildEdges(ctx: PipelineContext): Promise<void> {
|
|
|
673
608
|
|
|
674
609
|
// When using native edge insert, skip JS insert here — do it after tx commits.
|
|
675
610
|
// Otherwise insert edges within this transaction for atomicity.
|
|
676
|
-
const useNativeEdgeInsert = !!ctx.nativeDb?.bulkInsertEdges;
|
|
611
|
+
const useNativeEdgeInsert = ctx.engineName === 'native' && !!ctx.nativeDb?.bulkInsertEdges;
|
|
677
612
|
if (!useNativeEdgeInsert) {
|
|
678
613
|
batchInsertEdges(db, allEdgeRows);
|
|
679
614
|
}
|
|
@@ -683,7 +618,7 @@ export async function buildEdges(ctx: PipelineContext): Promise<void> {
|
|
|
683
618
|
// Phase 2: Native rusqlite bulk insert (outside better-sqlite3 transaction
|
|
684
619
|
// to avoid SQLITE_BUSY contention). Uses NativeDatabase persistent connection.
|
|
685
620
|
// Standalone napi functions were removed in 6.17.
|
|
686
|
-
if (ctx.nativeDb?.bulkInsertEdges && allEdgeRows.length > 0) {
|
|
621
|
+
if (ctx.engineName === 'native' && ctx.nativeDb?.bulkInsertEdges && allEdgeRows.length > 0) {
|
|
687
622
|
const nativeEdges = allEdgeRows.map((r) => ({
|
|
688
623
|
sourceId: r[0],
|
|
689
624
|
targetId: r[1],
|
|
@@ -694,7 +629,7 @@ export async function buildEdges(ctx: PipelineContext): Promise<void> {
|
|
|
694
629
|
const ok = ctx.nativeDb.bulkInsertEdges(nativeEdges);
|
|
695
630
|
if (!ok) {
|
|
696
631
|
debug('Native bulkInsertEdges failed — falling back to JS batchInsertEdges');
|
|
697
|
-
batchInsertEdges(db, allEdgeRows);
|
|
632
|
+
batchInsertEdges(ctx.db, allEdgeRows);
|
|
698
633
|
}
|
|
699
634
|
}
|
|
700
635
|
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { performance } from 'node:perf_hooks';
|
|
8
|
-
import { debug } from '
|
|
9
|
-
import { normalizePath } from '
|
|
10
|
-
import type { ExtractorOutput } from '
|
|
8
|
+
import { debug } from '../../../../infrastructure/logger.js';
|
|
9
|
+
import { normalizePath } from '../../../../shared/constants.js';
|
|
10
|
+
import type { ExtractorOutput } from '../../../../types.js';
|
|
11
11
|
import type { PipelineContext } from '../context.js';
|
|
12
12
|
import { readFileSafe } from '../helpers.js';
|
|
13
13
|
|
|
@@ -39,10 +39,11 @@ export async function buildStructure(ctx: PipelineContext): Promise<void> {
|
|
|
39
39
|
// loading ALL definitions from DB (~8ms) and recomputing ALL metrics (~15ms).
|
|
40
40
|
// Gate: ≤5 changed files AND significantly more existing files (>20) to
|
|
41
41
|
// avoid triggering on small test fixtures where directory metrics matter.
|
|
42
|
+
const useNativeReads = ctx.engineName === 'native' && !!ctx.nativeDb;
|
|
42
43
|
const existingFileCount = !isFullBuild
|
|
43
44
|
? (
|
|
44
|
-
(
|
|
45
|
-
? ctx.nativeDb
|
|
45
|
+
(useNativeReads
|
|
46
|
+
? ctx.nativeDb!.queryGet("SELECT COUNT(*) as c FROM nodes WHERE kind = 'file'", [])
|
|
46
47
|
: db.prepare("SELECT COUNT(*) as c FROM nodes WHERE kind = 'file'").get()) as {
|
|
47
48
|
c: number;
|
|
48
49
|
}
|
|
@@ -96,7 +97,8 @@ export async function buildStructure(ctx: PipelineContext): Promise<void> {
|
|
|
96
97
|
|
|
97
98
|
// Use NativeDatabase persistent connection (Phase 6.15+).
|
|
98
99
|
// Standalone napi functions were removed in 6.17 — falls through to JS if nativeDb unavailable.
|
|
99
|
-
|
|
100
|
+
// Note: classifyRoles* both read (fan-in/fan-out) and write (UPDATE nodes SET role).
|
|
101
|
+
if (useNativeReads && ctx.nativeDb?.classifyRolesFull) {
|
|
100
102
|
const nativeResult =
|
|
101
103
|
changedFileList && changedFileList.length > 0
|
|
102
104
|
? ctx.nativeDb.classifyRolesIncremental(changedFileList)
|
|
@@ -126,7 +128,7 @@ export async function buildStructure(ctx: PipelineContext): Promise<void> {
|
|
|
126
128
|
changedFiles?: string[] | null,
|
|
127
129
|
) => Record<string, number>;
|
|
128
130
|
};
|
|
129
|
-
roleSummary = classifyNodeRoles(db, changedFileList);
|
|
131
|
+
roleSummary = classifyNodeRoles(ctx.db, changedFileList);
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
debug(
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import { debug, info } from '
|
|
11
|
-
import { normalizePath } from '
|
|
10
|
+
import { debug, info } from '../../../../infrastructure/logger.js';
|
|
11
|
+
import { normalizePath } from '../../../../shared/constants.js';
|
|
12
12
|
import { readJournal } from '../../journal.js';
|
|
13
13
|
import type { PipelineContext } from '../context.js';
|
|
14
14
|
import { collectFiles as collectFilesUtil } from '../helpers.js';
|
|
@@ -338,7 +338,7 @@ function purgeAndAddReverseDeps(
|
|
|
338
338
|
if (hasPurge || hasReverseDeps) {
|
|
339
339
|
const filesToPurge = hasPurge ? [...ctx.removed, ...changePaths] : [];
|
|
340
340
|
// Prefer NativeDatabase: purge + reverse-dep edge deletion in one transaction (#670)
|
|
341
|
-
if (ctx.nativeDb?.purgeFilesData) {
|
|
341
|
+
if (ctx.engineName === 'native' && ctx.nativeDb?.purgeFilesData) {
|
|
342
342
|
ctx.nativeDb.purgeFilesData(filesToPurge, false, hasReverseDeps ? reverseDepList : undefined);
|
|
343
343
|
} else {
|
|
344
344
|
if (hasPurge) {
|
|
@@ -433,7 +433,12 @@ export async function detectChanges(ctx: PipelineContext): Promise<void> {
|
|
|
433
433
|
}
|
|
434
434
|
const increResult =
|
|
435
435
|
incremental && !forceFullRebuild
|
|
436
|
-
? getChangedFiles(
|
|
436
|
+
? getChangedFiles(
|
|
437
|
+
db,
|
|
438
|
+
allFiles,
|
|
439
|
+
rootDir,
|
|
440
|
+
ctx.engineName === 'native' ? ctx.nativeDb : undefined,
|
|
441
|
+
)
|
|
437
442
|
: {
|
|
438
443
|
changed: allFiles.map((f): ChangedFile => ({ file: f })),
|
|
439
444
|
removed: [] as string[],
|