@optave/codegraph 3.8.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -8
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +95 -87
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/metrics.d.ts +0 -3
- package/dist/ast-analysis/metrics.d.ts.map +1 -1
- package/dist/ast-analysis/metrics.js +30 -13
- package/dist/ast-analysis/metrics.js.map +1 -1
- package/dist/ast-analysis/shared.d.ts.map +1 -1
- package/dist/ast-analysis/shared.js +24 -19
- package/dist/ast-analysis/shared.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +55 -39
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitor.js +91 -70
- package/dist/ast-analysis/visitor.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 +54 -58
- 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 +32 -39
- package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.js +57 -38
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +16 -2
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +29 -26
- package/dist/db/connection.js.map +1 -1
- package/dist/db/query-builder.d.ts.map +1 -1
- package/dist/db/query-builder.js +16 -5
- package/dist/db/query-builder.js.map +1 -1
- package/dist/db/repository/base.d.ts +10 -0
- package/dist/db/repository/base.d.ts.map +1 -1
- package/dist/db/repository/base.js +17 -0
- package/dist/db/repository/base.js.map +1 -1
- package/dist/db/repository/native-repository.d.ts +6 -1
- package/dist/db/repository/native-repository.d.ts.map +1 -1
- package/dist/db/repository/native-repository.js +77 -1
- package/dist/db/repository/native-repository.js.map +1 -1
- package/dist/db/repository/nodes.d.ts.map +1 -1
- package/dist/db/repository/nodes.js +8 -4
- package/dist/db/repository/nodes.js.map +1 -1
- package/dist/db/repository/sqlite-repository.d.ts +3 -0
- package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
- package/dist/db/repository/sqlite-repository.js +26 -0
- package/dist/db/repository/sqlite-repository.js.map +1 -1
- package/dist/domain/analysis/brief.d.ts.map +1 -1
- package/dist/domain/analysis/brief.js +13 -17
- package/dist/domain/analysis/brief.js.map +1 -1
- package/dist/domain/analysis/context.d.ts.map +1 -1
- package/dist/domain/analysis/context.js +14 -11
- 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 +53 -52
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts +2 -7
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +33 -31
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/implementations.d.ts.map +1 -1
- package/dist/domain/analysis/implementations.js +11 -19
- package/dist/domain/analysis/implementations.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +55 -76
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/query-helpers.d.ts +7 -0
- package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
- package/dist/domain/analysis/query-helpers.js +15 -1
- package/dist/domain/analysis/query-helpers.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +255 -105
- 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 +22 -17
- package/dist/domain/graph/builder/stages/build-edges.js.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.js +2 -2
- 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 +32 -21
- 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 +95 -84
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/cycles.d.ts +6 -0
- package/dist/domain/graph/cycles.d.ts.map +1 -1
- package/dist/domain/graph/cycles.js +114 -22
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/resolve.js +1 -1
- package/dist/domain/graph/resolve.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts +2 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +170 -75
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +0 -5
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +13 -28
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.js +1 -1
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/search/models.d.ts +4 -3
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +18 -5
- package/dist/domain/search/models.js.map +1 -1
- package/dist/domain/search/search/hybrid.d.ts.map +1 -1
- package/dist/domain/search/search/hybrid.js +29 -18
- package/dist/domain/search/search/hybrid.js.map +1 -1
- package/dist/extractors/go.js +36 -33
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +40 -29
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.js +58 -46
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.js +46 -45
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.js +84 -78
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/python.js +29 -24
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/rust.js +41 -32
- package/dist/extractors/rust.js.map +1 -1
- package/dist/extractors/solidity.js +58 -67
- package/dist/extractors/solidity.js.map +1 -1
- package/dist/extractors/swift.js +83 -81
- package/dist/extractors/swift.js.map +1 -1
- package/dist/extractors/zig.js +58 -60
- package/dist/extractors/zig.js.map +1 -1
- package/dist/features/ast.d.ts +16 -14
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +83 -81
- package/dist/features/ast.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +8 -6
- package/dist/features/audit.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +69 -72
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/communities.d.ts.map +1 -1
- package/dist/features/communities.js +19 -7
- package/dist/features/communities.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +120 -125
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +136 -137
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +84 -79
- package/dist/features/flow.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +69 -65
- package/dist/features/structure-query.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +70 -55
- 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 +288 -266
- package/dist/graph/algorithms/leiden/partition.js.map +1 -1
- package/dist/graph/model.d.ts.map +1 -1
- package/dist/graph/model.js +5 -1
- package/dist/graph/model.js.map +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +6 -4
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/suppress.d.ts +25 -0
- package/dist/infrastructure/suppress.d.ts.map +1 -0
- package/dist/infrastructure/suppress.js +43 -0
- package/dist/infrastructure/suppress.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +29 -24
- package/dist/mcp/server.js.map +1 -1
- package/dist/presentation/dataflow.d.ts.map +1 -1
- package/dist/presentation/dataflow.js +47 -38
- package/dist/presentation/dataflow.js.map +1 -1
- package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
- package/dist/presentation/diff-impact-mermaid.js +60 -51
- package/dist/presentation/diff-impact-mermaid.js.map +1 -1
- package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
- package/dist/presentation/queries-cli/exports.js +20 -14
- 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 +15 -13
- package/dist/presentation/queries-cli/impact.js.map +1 -1
- package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
- package/dist/presentation/queries-cli/inspect.js +101 -79
- package/dist/presentation/queries-cli/inspect.js.map +1 -1
- package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
- package/dist/presentation/queries-cli/overview.js +25 -16
- package/dist/presentation/queries-cli/overview.js.map +1 -1
- package/dist/presentation/queries-cli/path.js +26 -20
- package/dist/presentation/queries-cli/path.js.map +1 -1
- package/dist/presentation/result-formatter.d.ts +10 -0
- package/dist/presentation/result-formatter.d.ts.map +1 -1
- package/dist/presentation/result-formatter.js +16 -1
- package/dist/presentation/result-formatter.js.map +1 -1
- package/dist/presentation/viewer.d.ts.map +1 -1
- package/dist/presentation/viewer.js +18 -12
- package/dist/presentation/viewer.js.map +1 -1
- package/dist/shared/errors.d.ts +5 -0
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +5 -0
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/hierarchy.d.ts +8 -2
- package/dist/shared/hierarchy.d.ts.map +1 -1
- package/dist/shared/hierarchy.js +42 -1
- package/dist/shared/hierarchy.js.map +1 -1
- package/dist/shared/normalize.d.ts +6 -1
- package/dist/shared/normalize.d.ts.map +1 -1
- package/dist/shared/normalize.js +20 -12
- package/dist/shared/normalize.js.map +1 -1
- package/dist/shared/paginate.d.ts +0 -9
- package/dist/shared/paginate.d.ts.map +1 -1
- package/dist/shared/paginate.js +0 -15
- package/dist/shared/paginate.js.map +1 -1
- package/dist/types.d.ts +10 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/ast-analysis/engine.ts +126 -105
- package/src/ast-analysis/metrics.ts +33 -11
- package/src/ast-analysis/shared.ts +33 -24
- package/src/ast-analysis/visitor-utils.ts +52 -32
- package/src/ast-analysis/visitor.ts +132 -71
- package/src/ast-analysis/visitors/ast-store-visitor.ts +53 -50
- package/src/ast-analysis/visitors/complexity-visitor.ts +35 -40
- package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
- package/src/cli/commands/watch.ts +16 -2
- package/src/db/connection.ts +29 -28
- package/src/db/query-builder.ts +15 -3
- package/src/db/repository/base.ts +20 -0
- package/src/db/repository/native-repository.ts +79 -1
- package/src/db/repository/nodes.ts +13 -8
- package/src/db/repository/sqlite-repository.ts +29 -0
- package/src/domain/analysis/brief.ts +15 -25
- package/src/domain/analysis/context.ts +17 -10
- package/src/domain/analysis/dependencies.ts +67 -76
- package/src/domain/analysis/fn-impact.ts +36 -43
- package/src/domain/analysis/implementations.ts +11 -17
- package/src/domain/analysis/module-map.ts +58 -92
- package/src/domain/analysis/query-helpers.ts +18 -1
- package/src/domain/graph/builder/pipeline.ts +286 -97
- package/src/domain/graph/builder/stages/build-edges.ts +22 -18
- package/src/domain/graph/builder/stages/detect-changes.ts +2 -2
- package/src/domain/graph/builder/stages/finalize.ts +2 -2
- package/src/domain/graph/builder/stages/insert-nodes.ts +59 -34
- package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
- package/src/domain/graph/cycles.ts +110 -23
- package/src/domain/graph/resolve.ts +1 -1
- package/src/domain/graph/watcher.ts +202 -96
- package/src/domain/parser.ts +14 -26
- package/src/domain/search/generator.ts +1 -1
- package/src/domain/search/models.ts +17 -4
- package/src/domain/search/search/hybrid.ts +69 -51
- package/src/extractors/go.ts +43 -33
- package/src/extractors/helpers.ts +37 -23
- package/src/extractors/java.ts +66 -47
- package/src/extractors/javascript.ts +45 -46
- package/src/extractors/kotlin.ts +84 -77
- package/src/extractors/python.ts +31 -25
- package/src/extractors/rust.ts +37 -29
- package/src/extractors/solidity.ts +57 -61
- package/src/extractors/swift.ts +81 -80
- package/src/extractors/zig.ts +58 -61
- package/src/features/ast.ts +130 -110
- package/src/features/audit.ts +8 -6
- package/src/features/branch-compare.ts +105 -79
- package/src/features/communities.ts +25 -10
- package/src/features/complexity.ts +171 -134
- package/src/features/dataflow.ts +165 -175
- package/src/features/flow.ts +129 -92
- package/src/features/structure-query.ts +79 -64
- package/src/graph/algorithms/leiden/optimiser.ts +99 -55
- package/src/graph/algorithms/leiden/partition.ts +359 -294
- package/src/graph/model.ts +6 -1
- package/src/infrastructure/config.ts +6 -4
- package/src/infrastructure/suppress.ts +47 -0
- package/src/mcp/server.ts +53 -37
- package/src/presentation/dataflow.ts +50 -44
- package/src/presentation/diff-impact-mermaid.ts +104 -62
- package/src/presentation/queries-cli/exports.ts +21 -13
- package/src/presentation/queries-cli/impact.ts +15 -13
- package/src/presentation/queries-cli/inspect.ts +100 -81
- package/src/presentation/queries-cli/overview.ts +26 -16
- package/src/presentation/queries-cli/path.ts +33 -25
- package/src/presentation/result-formatter.ts +19 -1
- package/src/presentation/viewer.ts +42 -14
- package/src/shared/errors.ts +6 -0
- package/src/shared/hierarchy.ts +50 -2
- package/src/shared/normalize.ts +31 -12
- package/src/shared/paginate.ts +0 -17
- package/src/types.ts +24 -4
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
findCallees,
|
|
3
|
-
findCallers,
|
|
4
|
-
findFileNodes,
|
|
5
|
-
findImportSources,
|
|
6
|
-
findImportTargets,
|
|
7
|
-
findNodesByFile,
|
|
8
|
-
} from '../../db/index.js';
|
|
1
|
+
import { findFileNodes, type Repository } from '../../db/index.js';
|
|
9
2
|
import { cachedStmt } from '../../db/repository/cached-stmt.js';
|
|
10
3
|
import { isTestFile } from '../../infrastructure/test-filter.js';
|
|
11
4
|
import { resolveMethodViaHierarchy } from '../../shared/hierarchy.js';
|
|
@@ -18,13 +11,11 @@ import type {
|
|
|
18
11
|
RelatedNodeRow,
|
|
19
12
|
StmtCache,
|
|
20
13
|
} from '../../types.js';
|
|
21
|
-
import { withReadonlyDb } from './query-helpers.js';
|
|
14
|
+
import { withReadonlyDb, withRepo } from './query-helpers.js';
|
|
22
15
|
import { findMatchingNodes } from './symbol-lookup.js';
|
|
23
16
|
|
|
24
|
-
type UpstreamRow = { id: number; name: string; kind: string; file: string; line: number };
|
|
25
17
|
type NodeByIdRow = { name: string; kind: string; file: string; line: number };
|
|
26
18
|
|
|
27
|
-
const _upstreamStmtCache: StmtCache<UpstreamRow> = new WeakMap();
|
|
28
19
|
const _nodeByIdStmtCache: StmtCache<NodeByIdRow> = new WeakMap();
|
|
29
20
|
|
|
30
21
|
export function fileDepsData(
|
|
@@ -32,21 +23,21 @@ export function fileDepsData(
|
|
|
32
23
|
customDbPath: string,
|
|
33
24
|
opts: { noTests?: boolean; limit?: number; offset?: number } = {},
|
|
34
25
|
) {
|
|
35
|
-
return
|
|
26
|
+
return withRepo(customDbPath, (repo) => {
|
|
36
27
|
const noTests = opts.noTests || false;
|
|
37
|
-
const fileNodes = findFileNodes(
|
|
28
|
+
const fileNodes = repo.findFileNodes(`%${file}%`) as NodeRow[];
|
|
38
29
|
if (fileNodes.length === 0) {
|
|
39
30
|
return { file, results: [] };
|
|
40
31
|
}
|
|
41
32
|
|
|
42
33
|
const results = fileNodes.map((fn) => {
|
|
43
|
-
let importsTo = findImportTargets(
|
|
34
|
+
let importsTo = repo.findImportTargets(fn.id) as ImportEdgeRow[];
|
|
44
35
|
if (noTests) importsTo = importsTo.filter((i) => !isTestFile(i.file));
|
|
45
36
|
|
|
46
|
-
let importedBy = findImportSources(
|
|
37
|
+
let importedBy = repo.findImportSources(fn.id) as ImportEdgeRow[];
|
|
47
38
|
if (noTests) importedBy = importedBy.filter((i) => !isTestFile(i.file));
|
|
48
39
|
|
|
49
|
-
const defs = findNodesByFile(
|
|
40
|
+
const defs = repo.findNodesByFile(fn.file) as NodeRow[];
|
|
50
41
|
|
|
51
42
|
return {
|
|
52
43
|
file: fn.file,
|
|
@@ -64,9 +55,11 @@ export function fileDepsData(
|
|
|
64
55
|
/**
|
|
65
56
|
* BFS transitive caller traversal starting from `callers` of `nodeId`.
|
|
66
57
|
* Returns an object keyed by depth (2..depth) -> array of caller descriptors.
|
|
58
|
+
*
|
|
59
|
+
* Uses Repository.findCallers() so it works with both native and WASM engines.
|
|
67
60
|
*/
|
|
68
61
|
function buildTransitiveCallers(
|
|
69
|
-
|
|
62
|
+
repo: InstanceType<typeof Repository>,
|
|
70
63
|
callers: Array<{ id: number; name: string; kind: string; file: string; line: number }>,
|
|
71
64
|
nodeId: number,
|
|
72
65
|
depth: number,
|
|
@@ -81,28 +74,12 @@ function buildTransitiveCallers(
|
|
|
81
74
|
const visited = new Set([nodeId]);
|
|
82
75
|
let frontier = callers;
|
|
83
76
|
|
|
84
|
-
const upstreamStmt = cachedStmt(
|
|
85
|
-
_upstreamStmtCache,
|
|
86
|
-
db,
|
|
87
|
-
`
|
|
88
|
-
SELECT n.id, n.name, n.kind, n.file, n.line
|
|
89
|
-
FROM edges e JOIN nodes n ON e.source_id = n.id
|
|
90
|
-
WHERE e.target_id = ? AND e.kind = 'calls'
|
|
91
|
-
`,
|
|
92
|
-
);
|
|
93
|
-
|
|
94
77
|
for (let d = 2; d <= depth; d++) {
|
|
95
78
|
const nextFrontier: typeof frontier = [];
|
|
96
79
|
for (const f of frontier) {
|
|
97
80
|
if (visited.has(f.id)) continue;
|
|
98
81
|
visited.add(f.id);
|
|
99
|
-
const upstream =
|
|
100
|
-
id: number;
|
|
101
|
-
name: string;
|
|
102
|
-
kind: string;
|
|
103
|
-
file: string;
|
|
104
|
-
line: number;
|
|
105
|
-
}>;
|
|
82
|
+
const upstream = repo.findCallers(f.id) as RelatedNodeRow[];
|
|
106
83
|
for (const u of upstream) {
|
|
107
84
|
if (noTests && isTestFile(u.file)) continue;
|
|
108
85
|
if (!visited.has(u.id)) {
|
|
@@ -125,6 +102,59 @@ function buildTransitiveCallers(
|
|
|
125
102
|
return transitiveCallers;
|
|
126
103
|
}
|
|
127
104
|
|
|
105
|
+
function collectCallersWithHierarchy(
|
|
106
|
+
repo: InstanceType<typeof Repository>,
|
|
107
|
+
node: NodeRow,
|
|
108
|
+
noTests: boolean,
|
|
109
|
+
): Array<RelatedNodeRow & { viaHierarchy?: string }> {
|
|
110
|
+
let callers: Array<RelatedNodeRow & { viaHierarchy?: string }> = repo.findCallers(
|
|
111
|
+
node.id,
|
|
112
|
+
) as RelatedNodeRow[];
|
|
113
|
+
|
|
114
|
+
if (node.kind === 'method' && node.name.includes('.')) {
|
|
115
|
+
const methodName = node.name.split('.').pop()!;
|
|
116
|
+
const relatedMethods = resolveMethodViaHierarchy(repo, methodName);
|
|
117
|
+
for (const rm of relatedMethods) {
|
|
118
|
+
if (rm.id === node.id) continue;
|
|
119
|
+
const extraCallers = repo.findCallers(rm.id) as RelatedNodeRow[];
|
|
120
|
+
callers.push(...extraCallers.map((c) => ({ ...c, viaHierarchy: rm.name })));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (noTests) callers = callers.filter((c) => !isTestFile(c.file));
|
|
124
|
+
return callers;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function buildNodeDepsResult(
|
|
128
|
+
repo: InstanceType<typeof Repository>,
|
|
129
|
+
node: NodeRow,
|
|
130
|
+
hc: Map<string, string | null>,
|
|
131
|
+
depth: number,
|
|
132
|
+
noTests: boolean,
|
|
133
|
+
) {
|
|
134
|
+
const callees = repo.findCallees(node.id) as RelatedNodeRow[];
|
|
135
|
+
const filteredCallees = noTests ? callees.filter((c) => !isTestFile(c.file)) : callees;
|
|
136
|
+
const callers = collectCallersWithHierarchy(repo, node, noTests);
|
|
137
|
+
const transitiveCallers = buildTransitiveCallers(repo, callers, node.id, depth, noTests);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
...normalizeSymbol(node, repo, hc),
|
|
141
|
+
callees: filteredCallees.map((c) => ({
|
|
142
|
+
name: c.name,
|
|
143
|
+
kind: c.kind,
|
|
144
|
+
file: c.file,
|
|
145
|
+
line: c.line,
|
|
146
|
+
})),
|
|
147
|
+
callers: callers.map((c) => ({
|
|
148
|
+
name: c.name,
|
|
149
|
+
kind: c.kind,
|
|
150
|
+
file: c.file,
|
|
151
|
+
line: c.line,
|
|
152
|
+
viaHierarchy: c.viaHierarchy || undefined,
|
|
153
|
+
})),
|
|
154
|
+
transitiveCallers,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
128
158
|
export function fnDepsData(
|
|
129
159
|
name: string,
|
|
130
160
|
customDbPath: string,
|
|
@@ -137,56 +167,17 @@ export function fnDepsData(
|
|
|
137
167
|
offset?: number;
|
|
138
168
|
} = {},
|
|
139
169
|
) {
|
|
140
|
-
return
|
|
170
|
+
return withRepo(customDbPath, (repo) => {
|
|
141
171
|
const depth = opts.depth || 3;
|
|
142
172
|
const noTests = opts.noTests || false;
|
|
143
173
|
const hc = new Map();
|
|
144
174
|
|
|
145
|
-
const nodes = findMatchingNodes(
|
|
175
|
+
const nodes = findMatchingNodes(repo, name, { noTests, file: opts.file, kind: opts.kind });
|
|
146
176
|
if (nodes.length === 0) {
|
|
147
177
|
return { name, results: [] };
|
|
148
178
|
}
|
|
149
179
|
|
|
150
|
-
const results = nodes.map((node) =>
|
|
151
|
-
const callees = findCallees(db, node.id) as RelatedNodeRow[];
|
|
152
|
-
const filteredCallees = noTests ? callees.filter((c) => !isTestFile(c.file)) : callees;
|
|
153
|
-
|
|
154
|
-
let callers: Array<RelatedNodeRow & { viaHierarchy?: string }> = findCallers(
|
|
155
|
-
db,
|
|
156
|
-
node.id,
|
|
157
|
-
) as RelatedNodeRow[];
|
|
158
|
-
|
|
159
|
-
if (node.kind === 'method' && node.name.includes('.')) {
|
|
160
|
-
const methodName = node.name.split('.').pop()!;
|
|
161
|
-
const relatedMethods = resolveMethodViaHierarchy(db, methodName);
|
|
162
|
-
for (const rm of relatedMethods) {
|
|
163
|
-
if (rm.id === node.id) continue;
|
|
164
|
-
const extraCallers = findCallers(db, rm.id) as RelatedNodeRow[];
|
|
165
|
-
callers.push(...extraCallers.map((c) => ({ ...c, viaHierarchy: rm.name })));
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (noTests) callers = callers.filter((c) => !isTestFile(c.file));
|
|
169
|
-
|
|
170
|
-
const transitiveCallers = buildTransitiveCallers(db, callers, node.id, depth, noTests);
|
|
171
|
-
|
|
172
|
-
return {
|
|
173
|
-
...normalizeSymbol(node, db, hc),
|
|
174
|
-
callees: filteredCallees.map((c) => ({
|
|
175
|
-
name: c.name,
|
|
176
|
-
kind: c.kind,
|
|
177
|
-
file: c.file,
|
|
178
|
-
line: c.line,
|
|
179
|
-
})),
|
|
180
|
-
callers: callers.map((c) => ({
|
|
181
|
-
name: c.name,
|
|
182
|
-
kind: c.kind,
|
|
183
|
-
file: c.file,
|
|
184
|
-
line: c.line,
|
|
185
|
-
viaHierarchy: c.viaHierarchy || undefined,
|
|
186
|
-
})),
|
|
187
|
-
transitiveCallers,
|
|
188
|
-
};
|
|
189
|
-
});
|
|
180
|
+
const results = nodes.map((node) => buildNodeDepsResult(repo, node, hc, depth, noTests));
|
|
190
181
|
|
|
191
182
|
const base = { name, results };
|
|
192
183
|
return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
|
|
@@ -1,40 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
findDistinctCallers,
|
|
3
|
-
findFileNodes,
|
|
4
|
-
findImplementors,
|
|
5
|
-
findImportDependents,
|
|
6
|
-
findNodeById,
|
|
7
|
-
} from '../../db/index.js';
|
|
1
|
+
import { Repository, SqliteRepository } from '../../db/index.js';
|
|
8
2
|
import { isTestFile } from '../../infrastructure/test-filter.js';
|
|
9
3
|
import { normalizeSymbol } from '../../shared/normalize.js';
|
|
10
4
|
import { paginateResult } from '../../shared/paginate.js';
|
|
11
5
|
import type { BetterSqlite3Database, NodeRow, RelatedNodeRow } from '../../types.js';
|
|
12
|
-
import { resolveAnalysisOpts,
|
|
6
|
+
import { resolveAnalysisOpts, withRepo } from './query-helpers.js';
|
|
13
7
|
import { findMatchingNodes } from './symbol-lookup.js';
|
|
14
8
|
|
|
9
|
+
/** Cache so repeated raw-db calls reuse the same SqliteRepository (preserves per-instance memoization). */
|
|
10
|
+
const repoCache = new WeakMap<BetterSqlite3Database, InstanceType<typeof SqliteRepository>>();
|
|
11
|
+
|
|
12
|
+
/** Coerce a raw db handle or Repository into a Repository instance. */
|
|
13
|
+
function toRepo(
|
|
14
|
+
dbOrRepo: BetterSqlite3Database | InstanceType<typeof Repository>,
|
|
15
|
+
): InstanceType<typeof Repository> {
|
|
16
|
+
if (dbOrRepo instanceof Repository) return dbOrRepo;
|
|
17
|
+
const db = dbOrRepo as BetterSqlite3Database;
|
|
18
|
+
let repo = repoCache.get(db);
|
|
19
|
+
if (!repo) {
|
|
20
|
+
repo = new SqliteRepository(db);
|
|
21
|
+
repoCache.set(db, repo);
|
|
22
|
+
}
|
|
23
|
+
return repo;
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
// --- Shared BFS: transitive callers ---
|
|
16
27
|
|
|
17
28
|
const INTERFACE_LIKE_KINDS = new Set(['interface', 'trait']);
|
|
18
29
|
|
|
19
|
-
/**
|
|
20
|
-
* Check whether the graph contains any 'implements' edges.
|
|
21
|
-
* Cached per db handle so the query runs at most once per connection.
|
|
22
|
-
*/
|
|
23
|
-
const _hasImplementsCache: WeakMap<BetterSqlite3Database, boolean> = new WeakMap();
|
|
24
|
-
function hasImplementsEdges(db: BetterSqlite3Database): boolean {
|
|
25
|
-
if (_hasImplementsCache.has(db)) return _hasImplementsCache.get(db)!;
|
|
26
|
-
const row = db.prepare("SELECT 1 FROM edges WHERE kind = 'implements' LIMIT 1").get();
|
|
27
|
-
const result = !!row;
|
|
28
|
-
_hasImplementsCache.set(db, result);
|
|
29
|
-
return result;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* BFS traversal to find transitive callers of a node.
|
|
34
|
-
* When an interface/trait node is encountered (either as the start node or
|
|
35
|
-
* during traversal), its concrete implementors are also added to the frontier
|
|
36
|
-
* so that changes to an interface signature propagate to all implementors.
|
|
37
|
-
*/
|
|
38
30
|
type BfsLevel = Array<{
|
|
39
31
|
name: string;
|
|
40
32
|
kind: string;
|
|
@@ -76,7 +68,7 @@ function recordImplementor(
|
|
|
76
68
|
|
|
77
69
|
/** Expand implementors for an interface/trait node into the BFS frontier. */
|
|
78
70
|
function expandImplementors(
|
|
79
|
-
|
|
71
|
+
repo: InstanceType<typeof Repository>,
|
|
80
72
|
nodeId: number,
|
|
81
73
|
depth: number,
|
|
82
74
|
visited: Set<number>,
|
|
@@ -85,14 +77,14 @@ function expandImplementors(
|
|
|
85
77
|
noTests: boolean,
|
|
86
78
|
onVisit?: BfsOnVisit,
|
|
87
79
|
): void {
|
|
88
|
-
const impls = findImplementors(
|
|
80
|
+
const impls = repo.findImplementors(nodeId) as RelatedNodeRow[];
|
|
89
81
|
for (const impl of impls) {
|
|
90
82
|
recordImplementor(impl, nodeId, depth, visited, frontier, levels, noTests, onVisit);
|
|
91
83
|
}
|
|
92
84
|
}
|
|
93
85
|
|
|
94
86
|
export function bfsTransitiveCallers(
|
|
95
|
-
|
|
87
|
+
dbOrRepo: BetterSqlite3Database | InstanceType<typeof Repository>,
|
|
96
88
|
startId: number,
|
|
97
89
|
{
|
|
98
90
|
noTests = false,
|
|
@@ -106,7 +98,8 @@ export function bfsTransitiveCallers(
|
|
|
106
98
|
onVisit?: BfsOnVisit;
|
|
107
99
|
} = {},
|
|
108
100
|
) {
|
|
109
|
-
const
|
|
101
|
+
const repo = toRepo(dbOrRepo);
|
|
102
|
+
const resolveImplementors = includeImplementors && repo.hasImplementsEdges();
|
|
110
103
|
const visited = new Set([startId]);
|
|
111
104
|
const levels: BfsLevels = {};
|
|
112
105
|
let frontier = [startId];
|
|
@@ -114,9 +107,9 @@ export function bfsTransitiveCallers(
|
|
|
114
107
|
// Seed: if start node is an interface/trait, include its implementors at depth 1
|
|
115
108
|
const implNextFrontier: number[] = [];
|
|
116
109
|
if (resolveImplementors) {
|
|
117
|
-
const startNode = findNodeById(
|
|
110
|
+
const startNode = repo.findNodeById(startId) as NodeRow | undefined;
|
|
118
111
|
if (startNode && INTERFACE_LIKE_KINDS.has(startNode.kind)) {
|
|
119
|
-
expandImplementors(
|
|
112
|
+
expandImplementors(repo, startId, 1, visited, implNextFrontier, levels, noTests, onVisit);
|
|
120
113
|
}
|
|
121
114
|
}
|
|
122
115
|
|
|
@@ -126,7 +119,7 @@ export function bfsTransitiveCallers(
|
|
|
126
119
|
}
|
|
127
120
|
const nextFrontier: number[] = [];
|
|
128
121
|
for (const fid of frontier) {
|
|
129
|
-
const callers = findDistinctCallers(
|
|
122
|
+
const callers = repo.findDistinctCallers(fid) as RelatedNodeRow[];
|
|
130
123
|
for (const c of callers) {
|
|
131
124
|
if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
|
|
132
125
|
visited.add(c.id);
|
|
@@ -136,7 +129,7 @@ export function bfsTransitiveCallers(
|
|
|
136
129
|
if (onVisit) onVisit(c, fid, d);
|
|
137
130
|
}
|
|
138
131
|
if (resolveImplementors && INTERFACE_LIKE_KINDS.has(c.kind)) {
|
|
139
|
-
expandImplementors(
|
|
132
|
+
expandImplementors(repo, c.id, d + 1, visited, nextFrontier, levels, noTests, onVisit);
|
|
140
133
|
}
|
|
141
134
|
}
|
|
142
135
|
}
|
|
@@ -152,9 +145,9 @@ export function impactAnalysisData(
|
|
|
152
145
|
customDbPath: string,
|
|
153
146
|
opts: { noTests?: boolean } = {},
|
|
154
147
|
) {
|
|
155
|
-
return
|
|
148
|
+
return withRepo(customDbPath, (repo) => {
|
|
156
149
|
const noTests = opts.noTests || false;
|
|
157
|
-
const fileNodes = findFileNodes(
|
|
150
|
+
const fileNodes = repo.findFileNodes(`%${file}%`) as NodeRow[];
|
|
158
151
|
if (fileNodes.length === 0) {
|
|
159
152
|
return { file, sources: [], levels: {}, totalDependents: 0 };
|
|
160
153
|
}
|
|
@@ -172,7 +165,7 @@ export function impactAnalysisData(
|
|
|
172
165
|
while (queue.length > 0) {
|
|
173
166
|
const current = queue.shift()!;
|
|
174
167
|
const level = levels.get(current)!;
|
|
175
|
-
const dependents = findImportDependents(
|
|
168
|
+
const dependents = repo.findImportDependents(current) as RelatedNodeRow[];
|
|
176
169
|
for (const dep of dependents) {
|
|
177
170
|
if (!visited.has(dep.id) && (!noTests || !isTestFile(dep.file))) {
|
|
178
171
|
visited.add(dep.id);
|
|
@@ -186,7 +179,7 @@ export function impactAnalysisData(
|
|
|
186
179
|
for (const [id, level] of levels) {
|
|
187
180
|
if (level === 0) continue;
|
|
188
181
|
if (!byLevel[level]) byLevel[level] = [];
|
|
189
|
-
const node = findNodeById(
|
|
182
|
+
const node = repo.findNodeById(id) as NodeRow | undefined;
|
|
190
183
|
if (node) byLevel[level].push({ file: node.file });
|
|
191
184
|
}
|
|
192
185
|
|
|
@@ -213,12 +206,12 @@ export function fnImpactData(
|
|
|
213
206
|
config?: any;
|
|
214
207
|
} = {},
|
|
215
208
|
) {
|
|
216
|
-
return
|
|
209
|
+
return withRepo(customDbPath, (repo) => {
|
|
217
210
|
const { noTests, config } = resolveAnalysisOpts(opts);
|
|
218
211
|
const maxDepth = opts.depth || config.analysis?.fnImpactDepth || 5;
|
|
219
212
|
const hc = new Map();
|
|
220
213
|
|
|
221
|
-
const nodes = findMatchingNodes(
|
|
214
|
+
const nodes = findMatchingNodes(repo, name, { noTests, file: opts.file, kind: opts.kind });
|
|
222
215
|
if (nodes.length === 0) {
|
|
223
216
|
return { name, results: [] };
|
|
224
217
|
}
|
|
@@ -226,13 +219,13 @@ export function fnImpactData(
|
|
|
226
219
|
const includeImplementors = opts.includeImplementors !== false;
|
|
227
220
|
|
|
228
221
|
const results = nodes.map((node) => {
|
|
229
|
-
const { levels, totalDependents } = bfsTransitiveCallers(
|
|
222
|
+
const { levels, totalDependents } = bfsTransitiveCallers(repo, node.id, {
|
|
230
223
|
noTests,
|
|
231
224
|
maxDepth,
|
|
232
225
|
includeImplementors,
|
|
233
226
|
});
|
|
234
227
|
return {
|
|
235
|
-
...normalizeSymbol(node,
|
|
228
|
+
...normalizeSymbol(node, repo, hc),
|
|
236
229
|
levels,
|
|
237
230
|
totalDependents,
|
|
238
231
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { findImplementors, findInterfaces, openReadonlyOrFail } from '../../db/index.js';
|
|
2
1
|
import { isTestFile } from '../../infrastructure/test-filter.js';
|
|
3
2
|
import { CORE_SYMBOL_KINDS } from '../../shared/kinds.js';
|
|
4
3
|
import { normalizeSymbol } from '../../shared/normalize.js';
|
|
5
4
|
import { paginateResult } from '../../shared/paginate.js';
|
|
6
5
|
import type { RelatedNodeRow } from '../../types.js';
|
|
6
|
+
import { withRepo } from './query-helpers.js';
|
|
7
7
|
import { findMatchingNodes } from './symbol-lookup.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -14,12 +14,11 @@ export function implementationsData(
|
|
|
14
14
|
customDbPath: string,
|
|
15
15
|
opts: { noTests?: boolean; file?: string; kind?: string; limit?: number; offset?: number } = {},
|
|
16
16
|
) {
|
|
17
|
-
|
|
18
|
-
try {
|
|
17
|
+
return withRepo(customDbPath, (repo) => {
|
|
19
18
|
const noTests = opts.noTests || false;
|
|
20
19
|
const hc = new Map();
|
|
21
20
|
|
|
22
|
-
const nodes = findMatchingNodes(
|
|
21
|
+
const nodes = findMatchingNodes(repo, name, {
|
|
23
22
|
noTests,
|
|
24
23
|
file: opts.file,
|
|
25
24
|
kind: opts.kind,
|
|
@@ -30,11 +29,11 @@ export function implementationsData(
|
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
const results = nodes.map((node) => {
|
|
33
|
-
let implementors = findImplementors(
|
|
32
|
+
let implementors = repo.findImplementors(node.id) as RelatedNodeRow[];
|
|
34
33
|
if (noTests) implementors = implementors.filter((n) => !isTestFile(n.file));
|
|
35
34
|
|
|
36
35
|
return {
|
|
37
|
-
...normalizeSymbol(node,
|
|
36
|
+
...normalizeSymbol(node, repo, hc),
|
|
38
37
|
implementors: implementors.map((impl) => ({
|
|
39
38
|
name: impl.name,
|
|
40
39
|
kind: impl.kind,
|
|
@@ -46,9 +45,7 @@ export function implementationsData(
|
|
|
46
45
|
|
|
47
46
|
const base = { name, results };
|
|
48
47
|
return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
|
|
49
|
-
}
|
|
50
|
-
db.close();
|
|
51
|
-
}
|
|
48
|
+
});
|
|
52
49
|
}
|
|
53
50
|
|
|
54
51
|
/**
|
|
@@ -59,12 +56,11 @@ export function interfacesData(
|
|
|
59
56
|
customDbPath: string,
|
|
60
57
|
opts: { noTests?: boolean; file?: string; kind?: string; limit?: number; offset?: number } = {},
|
|
61
58
|
) {
|
|
62
|
-
|
|
63
|
-
try {
|
|
59
|
+
return withRepo(customDbPath, (repo) => {
|
|
64
60
|
const noTests = opts.noTests || false;
|
|
65
61
|
const hc = new Map();
|
|
66
62
|
|
|
67
|
-
const nodes = findMatchingNodes(
|
|
63
|
+
const nodes = findMatchingNodes(repo, name, {
|
|
68
64
|
noTests,
|
|
69
65
|
file: opts.file,
|
|
70
66
|
kind: opts.kind,
|
|
@@ -75,11 +71,11 @@ export function interfacesData(
|
|
|
75
71
|
}
|
|
76
72
|
|
|
77
73
|
const results = nodes.map((node) => {
|
|
78
|
-
let interfaces = findInterfaces(
|
|
74
|
+
let interfaces = repo.findInterfaces(node.id) as RelatedNodeRow[];
|
|
79
75
|
if (noTests) interfaces = interfaces.filter((n) => !isTestFile(n.file));
|
|
80
76
|
|
|
81
77
|
return {
|
|
82
|
-
...normalizeSymbol(node,
|
|
78
|
+
...normalizeSymbol(node, repo, hc),
|
|
83
79
|
interfaces: interfaces.map((iface) => ({
|
|
84
80
|
name: iface.name,
|
|
85
81
|
kind: iface.kind,
|
|
@@ -91,7 +87,5 @@ export function interfacesData(
|
|
|
91
87
|
|
|
92
88
|
const base = { name, results };
|
|
93
89
|
return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
|
|
94
|
-
}
|
|
95
|
-
db.close();
|
|
96
|
-
}
|
|
90
|
+
});
|
|
97
91
|
}
|