@optave/codegraph 2.6.0 → 3.0.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 +111 -54
- package/package.json +5 -5
- package/src/ast.js +418 -0
- package/src/batch.js +93 -3
- package/src/builder.js +371 -103
- package/src/cfg.js +1452 -0
- package/src/change-journal.js +130 -0
- package/src/cli.js +415 -139
- package/src/complexity.js +8 -8
- package/src/dataflow.js +1190 -0
- package/src/db.js +96 -0
- package/src/embedder.js +16 -16
- package/src/export.js +305 -0
- package/src/extractors/csharp.js +64 -1
- package/src/extractors/go.js +66 -1
- package/src/extractors/hcl.js +22 -0
- package/src/extractors/java.js +61 -1
- package/src/extractors/javascript.js +193 -0
- package/src/extractors/php.js +79 -0
- package/src/extractors/python.js +134 -0
- package/src/extractors/ruby.js +89 -0
- package/src/extractors/rust.js +71 -1
- package/src/flow.js +5 -2
- package/src/index.js +52 -4
- package/src/mcp.js +403 -222
- package/src/paginate.js +3 -3
- package/src/parser.js +24 -0
- package/src/queries.js +362 -36
- package/src/structure.js +64 -8
- package/src/viewer.js +948 -0
- package/src/watcher.js +36 -1
package/src/structure.js
CHANGED
|
@@ -17,7 +17,7 @@ import { isTestFile } from './queries.js';
|
|
|
17
17
|
* @param {Map<string, number>} lineCountMap - Map of relPath → line count
|
|
18
18
|
* @param {Set<string>} directories - Set of relative directory paths
|
|
19
19
|
*/
|
|
20
|
-
export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, directories) {
|
|
20
|
+
export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, directories, changedFiles) {
|
|
21
21
|
const insertNode = db.prepare(
|
|
22
22
|
'INSERT OR IGNORE INTO nodes (name, kind, file, line, end_line) VALUES (?, ?, ?, ?, ?)',
|
|
23
23
|
);
|
|
@@ -33,12 +33,49 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
33
33
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
34
34
|
`);
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const isIncremental = changedFiles != null && changedFiles.length > 0;
|
|
37
|
+
|
|
38
|
+
if (isIncremental) {
|
|
39
|
+
// Incremental: only clean up data for changed files and their ancestor directories
|
|
40
|
+
const affectedDirs = new Set();
|
|
41
|
+
for (const f of changedFiles) {
|
|
42
|
+
let d = normalizePath(path.dirname(f));
|
|
43
|
+
while (d && d !== '.') {
|
|
44
|
+
affectedDirs.add(d);
|
|
45
|
+
d = normalizePath(path.dirname(d));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const deleteContainsForDir = db.prepare(
|
|
49
|
+
"DELETE FROM edges WHERE kind = 'contains' AND source_id IN (SELECT id FROM nodes WHERE name = ? AND kind = 'directory')",
|
|
50
|
+
);
|
|
51
|
+
const deleteMetricForNode = db.prepare('DELETE FROM node_metrics WHERE node_id = ?');
|
|
52
|
+
db.transaction(() => {
|
|
53
|
+
// Delete contains edges only from affected directories
|
|
54
|
+
for (const dir of affectedDirs) {
|
|
55
|
+
deleteContainsForDir.run(dir);
|
|
56
|
+
}
|
|
57
|
+
// Delete metrics for changed files
|
|
58
|
+
for (const f of changedFiles) {
|
|
59
|
+
const fileRow = getNodeId.get(f, 'file', f, 0);
|
|
60
|
+
if (fileRow) deleteMetricForNode.run(fileRow.id);
|
|
61
|
+
}
|
|
62
|
+
// Delete metrics for affected directories
|
|
63
|
+
for (const dir of affectedDirs) {
|
|
64
|
+
const dirRow = getNodeId.get(dir, 'directory', dir, 0);
|
|
65
|
+
if (dirRow) deleteMetricForNode.run(dirRow.id);
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
} else {
|
|
69
|
+
// Full rebuild: clean previous directory nodes/edges (idempotent)
|
|
70
|
+
// Scope contains-edge delete to directory-sourced edges only,
|
|
71
|
+
// preserving symbol-level contains edges (file→def, class→method, etc.)
|
|
72
|
+
db.exec(`
|
|
73
|
+
DELETE FROM edges WHERE kind = 'contains'
|
|
74
|
+
AND source_id IN (SELECT id FROM nodes WHERE kind = 'directory');
|
|
75
|
+
DELETE FROM node_metrics;
|
|
76
|
+
DELETE FROM nodes WHERE kind = 'directory';
|
|
77
|
+
`);
|
|
78
|
+
}
|
|
42
79
|
|
|
43
80
|
// Step 1: Ensure all directories are represented (including intermediate parents)
|
|
44
81
|
const allDirs = new Set();
|
|
@@ -58,7 +95,7 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
58
95
|
}
|
|
59
96
|
}
|
|
60
97
|
|
|
61
|
-
// Step 2: Insert directory nodes
|
|
98
|
+
// Step 2: Insert directory nodes (INSERT OR IGNORE — safe for incremental)
|
|
62
99
|
const insertDirs = db.transaction(() => {
|
|
63
100
|
for (const dir of allDirs) {
|
|
64
101
|
insertNode.run(dir, 'directory', dir, 0, null);
|
|
@@ -67,11 +104,28 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
67
104
|
insertDirs();
|
|
68
105
|
|
|
69
106
|
// Step 3: Insert 'contains' edges (dir → file, dir → subdirectory)
|
|
107
|
+
// On incremental, only re-insert for affected directories (others are intact)
|
|
108
|
+
const affectedDirs = isIncremental
|
|
109
|
+
? (() => {
|
|
110
|
+
const dirs = new Set();
|
|
111
|
+
for (const f of changedFiles) {
|
|
112
|
+
let d = normalizePath(path.dirname(f));
|
|
113
|
+
while (d && d !== '.') {
|
|
114
|
+
dirs.add(d);
|
|
115
|
+
d = normalizePath(path.dirname(d));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return dirs;
|
|
119
|
+
})()
|
|
120
|
+
: null;
|
|
121
|
+
|
|
70
122
|
const insertContains = db.transaction(() => {
|
|
71
123
|
// dir → file
|
|
72
124
|
for (const relPath of fileSymbols.keys()) {
|
|
73
125
|
const dir = normalizePath(path.dirname(relPath));
|
|
74
126
|
if (!dir || dir === '.') continue;
|
|
127
|
+
// On incremental, skip dirs whose contains edges are intact
|
|
128
|
+
if (affectedDirs && !affectedDirs.has(dir)) continue;
|
|
75
129
|
const dirRow = getNodeId.get(dir, 'directory', dir, 0);
|
|
76
130
|
const fileRow = getNodeId.get(relPath, 'file', relPath, 0);
|
|
77
131
|
if (dirRow && fileRow) {
|
|
@@ -82,6 +136,8 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
82
136
|
for (const dir of allDirs) {
|
|
83
137
|
const parent = normalizePath(path.dirname(dir));
|
|
84
138
|
if (!parent || parent === '.' || parent === dir) continue;
|
|
139
|
+
// On incremental, skip parent dirs whose contains edges are intact
|
|
140
|
+
if (affectedDirs && !affectedDirs.has(parent)) continue;
|
|
85
141
|
const parentRow = getNodeId.get(parent, 'directory', parent, 0);
|
|
86
142
|
const childRow = getNodeId.get(dir, 'directory', dir, 0);
|
|
87
143
|
if (parentRow && childRow) {
|