@optave/codegraph 3.5.0 → 3.6.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 +35 -14
- 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 +164 -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/go.d.ts.map +1 -1
- package/dist/extractors/go.js +126 -130
- package/dist/extractors/go.js.map +1 -1
- 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 +6 -0
- package/dist/extractors/index.d.ts.map +1 -1
- package/dist/extractors/index.js +6 -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/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/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 +118 -62
- 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-kotlin.wasm +0 -0
- package/grammars/tree-sitter-scala.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/package.json +13 -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 +168 -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/go.ts +152 -134
- package/src/extractors/hcl.ts +6 -6
- package/src/extractors/helpers.ts +93 -1
- package/src/extractors/index.ts +6 -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/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/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 +158 -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 +189 -7
|
@@ -4,13 +4,12 @@ import {
|
|
|
4
4
|
findImplementors,
|
|
5
5
|
findImportDependents,
|
|
6
6
|
findNodeById,
|
|
7
|
-
openReadonlyOrFail,
|
|
8
7
|
} from '../../db/index.js';
|
|
9
|
-
import { loadConfig } from '../../infrastructure/config.js';
|
|
10
8
|
import { isTestFile } from '../../infrastructure/test-filter.js';
|
|
11
9
|
import { normalizeSymbol } from '../../shared/normalize.js';
|
|
12
10
|
import { paginateResult } from '../../shared/paginate.js';
|
|
13
11
|
import type { BetterSqlite3Database, NodeRow, RelatedNodeRow } from '../../types.js';
|
|
12
|
+
import { resolveAnalysisOpts, withReadonlyDb } from './query-helpers.js';
|
|
14
13
|
import { findMatchingNodes } from './symbol-lookup.js';
|
|
15
14
|
|
|
16
15
|
// --- Shared BFS: transitive callers ---
|
|
@@ -36,6 +35,62 @@ function hasImplementsEdges(db: BetterSqlite3Database): boolean {
|
|
|
36
35
|
* during traversal), its concrete implementors are also added to the frontier
|
|
37
36
|
* so that changes to an interface signature propagate to all implementors.
|
|
38
37
|
*/
|
|
38
|
+
type BfsLevel = Array<{
|
|
39
|
+
name: string;
|
|
40
|
+
kind: string;
|
|
41
|
+
file: string;
|
|
42
|
+
line: number;
|
|
43
|
+
viaImplements?: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
type BfsLevels = Record<number, BfsLevel>;
|
|
46
|
+
type BfsOnVisit = (
|
|
47
|
+
caller: RelatedNodeRow & { viaImplements?: boolean },
|
|
48
|
+
parentId: number,
|
|
49
|
+
depth: number,
|
|
50
|
+
) => void;
|
|
51
|
+
|
|
52
|
+
/** Record an implementor node at the given depth, adding to frontier and levels. */
|
|
53
|
+
function recordImplementor(
|
|
54
|
+
impl: RelatedNodeRow,
|
|
55
|
+
parentId: number,
|
|
56
|
+
depth: number,
|
|
57
|
+
visited: Set<number>,
|
|
58
|
+
frontier: number[],
|
|
59
|
+
levels: BfsLevels,
|
|
60
|
+
noTests: boolean,
|
|
61
|
+
onVisit?: BfsOnVisit,
|
|
62
|
+
): void {
|
|
63
|
+
if (visited.has(impl.id) || (noTests && isTestFile(impl.file))) return;
|
|
64
|
+
visited.add(impl.id);
|
|
65
|
+
frontier.push(impl.id);
|
|
66
|
+
if (!levels[depth]) levels[depth] = [];
|
|
67
|
+
levels[depth].push({
|
|
68
|
+
name: impl.name,
|
|
69
|
+
kind: impl.kind,
|
|
70
|
+
file: impl.file,
|
|
71
|
+
line: impl.line,
|
|
72
|
+
viaImplements: true,
|
|
73
|
+
});
|
|
74
|
+
if (onVisit) onVisit({ ...impl, viaImplements: true }, parentId, depth);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Expand implementors for an interface/trait node into the BFS frontier. */
|
|
78
|
+
function expandImplementors(
|
|
79
|
+
db: BetterSqlite3Database,
|
|
80
|
+
nodeId: number,
|
|
81
|
+
depth: number,
|
|
82
|
+
visited: Set<number>,
|
|
83
|
+
frontier: number[],
|
|
84
|
+
levels: BfsLevels,
|
|
85
|
+
noTests: boolean,
|
|
86
|
+
onVisit?: BfsOnVisit,
|
|
87
|
+
): void {
|
|
88
|
+
const impls = findImplementors(db, nodeId) as RelatedNodeRow[];
|
|
89
|
+
for (const impl of impls) {
|
|
90
|
+
recordImplementor(impl, nodeId, depth, visited, frontier, levels, noTests, onVisit);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
39
94
|
export function bfsTransitiveCallers(
|
|
40
95
|
db: BetterSqlite3Database,
|
|
41
96
|
startId: number,
|
|
@@ -48,50 +103,24 @@ export function bfsTransitiveCallers(
|
|
|
48
103
|
noTests?: boolean;
|
|
49
104
|
maxDepth?: number;
|
|
50
105
|
includeImplementors?: boolean;
|
|
51
|
-
onVisit?:
|
|
52
|
-
caller: RelatedNodeRow & { viaImplements?: boolean },
|
|
53
|
-
parentId: number,
|
|
54
|
-
depth: number,
|
|
55
|
-
) => void;
|
|
106
|
+
onVisit?: BfsOnVisit;
|
|
56
107
|
} = {},
|
|
57
108
|
) {
|
|
58
|
-
// Skip all implementor lookups when the graph has no implements edges
|
|
59
109
|
const resolveImplementors = includeImplementors && hasImplementsEdges(db);
|
|
60
|
-
|
|
61
110
|
const visited = new Set([startId]);
|
|
62
|
-
const levels:
|
|
63
|
-
number,
|
|
64
|
-
Array<{ name: string; kind: string; file: string; line: number; viaImplements?: boolean }>
|
|
65
|
-
> = {};
|
|
111
|
+
const levels: BfsLevels = {};
|
|
66
112
|
let frontier = [startId];
|
|
67
113
|
|
|
68
|
-
// Seed: if start node is an interface/trait, include its implementors at depth 1
|
|
69
|
-
// Implementors go into a separate list so their callers appear at depth 2, not depth 1.
|
|
114
|
+
// Seed: if start node is an interface/trait, include its implementors at depth 1
|
|
70
115
|
const implNextFrontier: number[] = [];
|
|
71
116
|
if (resolveImplementors) {
|
|
72
117
|
const startNode = findNodeById(db, startId) as NodeRow | undefined;
|
|
73
118
|
if (startNode && INTERFACE_LIKE_KINDS.has(startNode.kind)) {
|
|
74
|
-
|
|
75
|
-
for (const impl of impls) {
|
|
76
|
-
if (!visited.has(impl.id) && (!noTests || !isTestFile(impl.file))) {
|
|
77
|
-
visited.add(impl.id);
|
|
78
|
-
implNextFrontier.push(impl.id);
|
|
79
|
-
if (!levels[1]) levels[1] = [];
|
|
80
|
-
levels[1].push({
|
|
81
|
-
name: impl.name,
|
|
82
|
-
kind: impl.kind,
|
|
83
|
-
file: impl.file,
|
|
84
|
-
line: impl.line,
|
|
85
|
-
viaImplements: true,
|
|
86
|
-
});
|
|
87
|
-
if (onVisit) onVisit({ ...impl, viaImplements: true }, startId, 1);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
119
|
+
expandImplementors(db, startId, 1, visited, implNextFrontier, levels, noTests, onVisit);
|
|
90
120
|
}
|
|
91
121
|
}
|
|
92
122
|
|
|
93
123
|
for (let d = 1; d <= maxDepth; d++) {
|
|
94
|
-
// On the first wave, merge seeded implementors so their callers appear at d=2
|
|
95
124
|
if (d === 1 && implNextFrontier.length > 0) {
|
|
96
125
|
frontier = [...frontier, ...implNextFrontier];
|
|
97
126
|
}
|
|
@@ -106,27 +135,8 @@ export function bfsTransitiveCallers(
|
|
|
106
135
|
levels[d]!.push({ name: c.name, kind: c.kind, file: c.file, line: c.line });
|
|
107
136
|
if (onVisit) onVisit(c, fid, d);
|
|
108
137
|
}
|
|
109
|
-
|
|
110
|
-
// If a caller is an interface/trait, also pull in its implementors
|
|
111
|
-
// Implementors are one extra hop away, so record at d+1
|
|
112
138
|
if (resolveImplementors && INTERFACE_LIKE_KINDS.has(c.kind)) {
|
|
113
|
-
|
|
114
|
-
for (const impl of impls) {
|
|
115
|
-
if (!visited.has(impl.id) && (!noTests || !isTestFile(impl.file))) {
|
|
116
|
-
visited.add(impl.id);
|
|
117
|
-
nextFrontier.push(impl.id);
|
|
118
|
-
const implDepth = d + 1;
|
|
119
|
-
if (!levels[implDepth]) levels[implDepth] = [];
|
|
120
|
-
levels[implDepth].push({
|
|
121
|
-
name: impl.name,
|
|
122
|
-
kind: impl.kind,
|
|
123
|
-
file: impl.file,
|
|
124
|
-
line: impl.line,
|
|
125
|
-
viaImplements: true,
|
|
126
|
-
});
|
|
127
|
-
if (onVisit) onVisit({ ...impl, viaImplements: true }, c.id, implDepth);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
139
|
+
expandImplementors(db, c.id, d + 1, visited, nextFrontier, levels, noTests, onVisit);
|
|
130
140
|
}
|
|
131
141
|
}
|
|
132
142
|
}
|
|
@@ -142,8 +152,7 @@ export function impactAnalysisData(
|
|
|
142
152
|
customDbPath: string,
|
|
143
153
|
opts: { noTests?: boolean } = {},
|
|
144
154
|
) {
|
|
145
|
-
|
|
146
|
-
try {
|
|
155
|
+
return withReadonlyDb(customDbPath, (db) => {
|
|
147
156
|
const noTests = opts.noTests || false;
|
|
148
157
|
const fileNodes = findFileNodes(db, `%${file}%`) as NodeRow[];
|
|
149
158
|
if (fileNodes.length === 0) {
|
|
@@ -187,9 +196,7 @@ export function impactAnalysisData(
|
|
|
187
196
|
levels: byLevel,
|
|
188
197
|
totalDependents: visited.size - fileNodes.length,
|
|
189
198
|
};
|
|
190
|
-
}
|
|
191
|
-
db.close();
|
|
192
|
-
}
|
|
199
|
+
});
|
|
193
200
|
}
|
|
194
201
|
|
|
195
202
|
export function fnImpactData(
|
|
@@ -206,11 +213,9 @@ export function fnImpactData(
|
|
|
206
213
|
config?: any;
|
|
207
214
|
} = {},
|
|
208
215
|
) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const config = opts.config || loadConfig();
|
|
216
|
+
return withReadonlyDb(customDbPath, (db) => {
|
|
217
|
+
const { noTests, config } = resolveAnalysisOpts(opts);
|
|
212
218
|
const maxDepth = opts.depth || config.analysis?.fnImpactDepth || 5;
|
|
213
|
-
const noTests = opts.noTests || false;
|
|
214
219
|
const hc = new Map();
|
|
215
220
|
|
|
216
221
|
const nodes = findMatchingNodes(db, name, { noTests, file: opts.file, kind: opts.kind });
|
|
@@ -235,7 +240,5 @@ export function fnImpactData(
|
|
|
235
240
|
|
|
236
241
|
const base = { name, results };
|
|
237
242
|
return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
|
|
238
|
-
}
|
|
239
|
-
db.close();
|
|
240
|
-
}
|
|
243
|
+
});
|
|
241
244
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { openReadonlyOrFail, testFilterSQL } from '../../db/index.js';
|
|
2
|
+
import { openReadonlyOrFail, openReadonlyWithNative, testFilterSQL } from '../../db/index.js';
|
|
3
3
|
import { cachedStmt } from '../../db/repository/cached-stmt.js';
|
|
4
4
|
import { loadConfig } from '../../infrastructure/config.js';
|
|
5
5
|
import { debug } from '../../infrastructure/logger.js';
|
|
@@ -381,20 +381,115 @@ export function moduleMapData(customDbPath: string, limit = 20, opts: { noTests?
|
|
|
381
381
|
}
|
|
382
382
|
|
|
383
383
|
export function statsData(customDbPath: string, opts: { noTests?: boolean; config?: any } = {}) {
|
|
384
|
-
const db =
|
|
384
|
+
const { db, nativeDb, close } = openReadonlyWithNative(customDbPath);
|
|
385
385
|
try {
|
|
386
386
|
const noTests = opts.noTests || false;
|
|
387
387
|
const config = opts.config || loadConfig();
|
|
388
|
-
const testFilter = testFilterSQL('n.file', noTests);
|
|
389
388
|
|
|
389
|
+
// These always need JS (non-SQL logic)
|
|
390
|
+
const files = countFilesByLanguage(db, noTests);
|
|
391
|
+
const fileCycles = findCycles(db, { fileLevel: true, noTests });
|
|
392
|
+
const fnCycles = findCycles(db, { fileLevel: false, noTests });
|
|
393
|
+
|
|
394
|
+
// ── Native fast path: batch all SQL aggregations in one napi call ──
|
|
395
|
+
if (nativeDb?.getGraphStats) {
|
|
396
|
+
const s = nativeDb.getGraphStats(noTests);
|
|
397
|
+
const nodesByKind: Record<string, number> = {};
|
|
398
|
+
for (const k of s.nodesByKind) nodesByKind[k.kind] = k.count;
|
|
399
|
+
const edgesByKind: Record<string, number> = {};
|
|
400
|
+
for (const k of s.edgesByKind) edgesByKind[k.kind] = k.count;
|
|
401
|
+
const roles: Record<string, number> & { dead?: number } = {};
|
|
402
|
+
let deadTotal = 0;
|
|
403
|
+
for (const r of s.roleCounts) {
|
|
404
|
+
roles[r.role] = r.count;
|
|
405
|
+
if (r.role.startsWith(DEAD_ROLE_PREFIX)) deadTotal += r.count;
|
|
406
|
+
}
|
|
407
|
+
if (deadTotal > 0) roles.dead = deadTotal;
|
|
408
|
+
|
|
409
|
+
const callerCoverage =
|
|
410
|
+
s.quality.callableTotal > 0 ? s.quality.callableWithCallers / s.quality.callableTotal : 0;
|
|
411
|
+
const callConfidence =
|
|
412
|
+
s.quality.callEdges > 0 ? s.quality.highConfCallEdges / s.quality.callEdges : 0;
|
|
413
|
+
|
|
414
|
+
// False-positive analysis still uses JS (needs FALSE_POSITIVE_NAMES set)
|
|
415
|
+
const fpThreshold = config.analysis?.falsePositiveCallers ?? FALSE_POSITIVE_CALLER_THRESHOLD;
|
|
416
|
+
const fpRows = db
|
|
417
|
+
.prepare(`
|
|
418
|
+
SELECT n.name, n.file, n.line, COUNT(e.source_id) as caller_count
|
|
419
|
+
FROM nodes n
|
|
420
|
+
LEFT JOIN edges e ON n.id = e.target_id AND e.kind = 'calls'
|
|
421
|
+
WHERE n.kind IN ('function', 'method')
|
|
422
|
+
GROUP BY n.id
|
|
423
|
+
HAVING caller_count > ?
|
|
424
|
+
ORDER BY caller_count DESC
|
|
425
|
+
`)
|
|
426
|
+
.all(fpThreshold) as Array<{
|
|
427
|
+
name: string;
|
|
428
|
+
file: string;
|
|
429
|
+
line: number;
|
|
430
|
+
caller_count: number;
|
|
431
|
+
}>;
|
|
432
|
+
const falsePositiveWarnings = fpRows
|
|
433
|
+
.filter((r) =>
|
|
434
|
+
FALSE_POSITIVE_NAMES.has(r.name.includes('.') ? r.name.split('.').pop()! : r.name),
|
|
435
|
+
)
|
|
436
|
+
.map((r) => ({ name: r.name, file: r.file, line: r.line, callerCount: r.caller_count }));
|
|
437
|
+
let fpEdgeCount = 0;
|
|
438
|
+
for (const fp of falsePositiveWarnings) fpEdgeCount += fp.callerCount;
|
|
439
|
+
const falsePositiveRatio = s.quality.callEdges > 0 ? fpEdgeCount / s.quality.callEdges : 0;
|
|
440
|
+
const score = Math.round(
|
|
441
|
+
callerCoverage * 40 + callConfidence * 40 + (1 - falsePositiveRatio) * 20,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
return {
|
|
445
|
+
nodes: { total: s.totalNodes, byKind: nodesByKind },
|
|
446
|
+
edges: { total: s.totalEdges, byKind: edgesByKind },
|
|
447
|
+
files,
|
|
448
|
+
cycles: { fileLevel: fileCycles.length, functionLevel: fnCycles.length },
|
|
449
|
+
hotspots: s.hotspots.map((h) => ({ file: h.file, fanIn: h.fanIn, fanOut: h.fanOut })),
|
|
450
|
+
embeddings: s.embeddings
|
|
451
|
+
? {
|
|
452
|
+
count: s.embeddings.count,
|
|
453
|
+
model: s.embeddings.model,
|
|
454
|
+
dim: s.embeddings.dim,
|
|
455
|
+
builtAt: s.embeddings.builtAt,
|
|
456
|
+
}
|
|
457
|
+
: null,
|
|
458
|
+
quality: {
|
|
459
|
+
score,
|
|
460
|
+
callerCoverage: {
|
|
461
|
+
ratio: callerCoverage,
|
|
462
|
+
covered: s.quality.callableWithCallers,
|
|
463
|
+
total: s.quality.callableTotal,
|
|
464
|
+
},
|
|
465
|
+
callConfidence: {
|
|
466
|
+
ratio: callConfidence,
|
|
467
|
+
highConf: s.quality.highConfCallEdges,
|
|
468
|
+
total: s.quality.callEdges,
|
|
469
|
+
},
|
|
470
|
+
falsePositiveWarnings,
|
|
471
|
+
},
|
|
472
|
+
roles,
|
|
473
|
+
complexity: s.complexity
|
|
474
|
+
? {
|
|
475
|
+
analyzed: s.complexity.analyzed,
|
|
476
|
+
avgCognitive: s.complexity.avgCognitive,
|
|
477
|
+
avgCyclomatic: s.complexity.avgCyclomatic,
|
|
478
|
+
maxCognitive: s.complexity.maxCognitive,
|
|
479
|
+
maxCyclomatic: s.complexity.maxCyclomatic,
|
|
480
|
+
avgMI: s.complexity.avgMi,
|
|
481
|
+
minMI: s.complexity.minMi,
|
|
482
|
+
}
|
|
483
|
+
: null,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ── JS fallback ───────────────────────────────────────────────────
|
|
488
|
+
const testFilter = testFilterSQL('n.file', noTests);
|
|
390
489
|
const testFileIds = noTests ? buildTestFileIds(db) : null;
|
|
391
490
|
|
|
392
491
|
const { total: totalNodes, byKind: nodesByKind } = countNodesByKind(db, testFileIds);
|
|
393
492
|
const { total: totalEdges, byKind: edgesByKind } = countEdgesByKind(db, testFileIds);
|
|
394
|
-
const files = countFilesByLanguage(db, noTests);
|
|
395
|
-
|
|
396
|
-
const fileCycles = findCycles(db, { fileLevel: true, noTests });
|
|
397
|
-
const fnCycles = findCycles(db, { fileLevel: false, noTests });
|
|
398
493
|
|
|
399
494
|
const hotspots = findHotspots(db, noTests, 5);
|
|
400
495
|
const embeddings = getEmbeddingsInfo(db);
|
|
@@ -415,6 +510,6 @@ export function statsData(customDbPath: string, opts: { noTests?: boolean; confi
|
|
|
415
510
|
complexity,
|
|
416
511
|
};
|
|
417
512
|
} finally {
|
|
418
|
-
|
|
513
|
+
close();
|
|
419
514
|
}
|
|
420
515
|
}
|
|
@@ -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
|
|