@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
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { debug, warn } from './logger.js';
|
|
4
|
+
|
|
5
|
+
export const CHANGE_EVENTS_FILENAME = 'change-events.ndjson';
|
|
6
|
+
export const DEFAULT_MAX_BYTES = 1024 * 1024; // 1 MB
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Returns the absolute path to the NDJSON change events file.
|
|
10
|
+
*/
|
|
11
|
+
export function changeEventsPath(rootDir) {
|
|
12
|
+
return path.join(rootDir, '.codegraph', CHANGE_EVENTS_FILENAME);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Compare old and new symbol arrays, returning added/removed/modified sets.
|
|
17
|
+
* Symbols are keyed on `name\0kind`. A symbol is "modified" if the same
|
|
18
|
+
* name+kind exists in both but the line changed.
|
|
19
|
+
*
|
|
20
|
+
* @param {Array<{name:string, kind:string, line:number}>} oldSymbols
|
|
21
|
+
* @param {Array<{name:string, kind:string, line:number}>} newSymbols
|
|
22
|
+
* @returns {{ added: Array, removed: Array, modified: Array }}
|
|
23
|
+
*/
|
|
24
|
+
export function diffSymbols(oldSymbols, newSymbols) {
|
|
25
|
+
const oldMap = new Map();
|
|
26
|
+
for (const s of oldSymbols) {
|
|
27
|
+
oldMap.set(`${s.name}\0${s.kind}`, s);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const newMap = new Map();
|
|
31
|
+
for (const s of newSymbols) {
|
|
32
|
+
newMap.set(`${s.name}\0${s.kind}`, s);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const added = [];
|
|
36
|
+
const removed = [];
|
|
37
|
+
const modified = [];
|
|
38
|
+
|
|
39
|
+
for (const [key, s] of newMap) {
|
|
40
|
+
const old = oldMap.get(key);
|
|
41
|
+
if (!old) {
|
|
42
|
+
added.push({ name: s.name, kind: s.kind, line: s.line });
|
|
43
|
+
} else if (old.line !== s.line) {
|
|
44
|
+
modified.push({ name: s.name, kind: s.kind, line: s.line });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const [key, s] of oldMap) {
|
|
49
|
+
if (!newMap.has(key)) {
|
|
50
|
+
removed.push({ name: s.name, kind: s.kind });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { added, removed, modified };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Assemble a single change event object.
|
|
59
|
+
*/
|
|
60
|
+
export function buildChangeEvent(file, event, symbolDiff, counts) {
|
|
61
|
+
return {
|
|
62
|
+
ts: new Date().toISOString(),
|
|
63
|
+
file,
|
|
64
|
+
event,
|
|
65
|
+
symbols: symbolDiff,
|
|
66
|
+
counts: {
|
|
67
|
+
nodes: { before: counts.nodesBefore ?? 0, after: counts.nodesAfter ?? 0 },
|
|
68
|
+
edges: { added: counts.edgesAdded ?? 0 },
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Append change events as NDJSON lines to the change events file.
|
|
75
|
+
* Creates the .codegraph directory if needed. Non-fatal on failure.
|
|
76
|
+
*/
|
|
77
|
+
export function appendChangeEvents(rootDir, events) {
|
|
78
|
+
const filePath = changeEventsPath(rootDir);
|
|
79
|
+
const dir = path.dirname(filePath);
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
if (!fs.existsSync(dir)) {
|
|
83
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
84
|
+
}
|
|
85
|
+
const lines = `${events.map((e) => JSON.stringify(e)).join('\n')}\n`;
|
|
86
|
+
fs.appendFileSync(filePath, lines);
|
|
87
|
+
debug(`Appended ${events.length} change event(s) to ${filePath}`);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
warn(`Failed to append change events: ${err.message}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
rotateIfNeeded(filePath, DEFAULT_MAX_BYTES);
|
|
95
|
+
} catch {
|
|
96
|
+
/* rotation failure is non-fatal */
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* If the file exceeds maxBytes, keep the last ~half by finding
|
|
102
|
+
* the first newline at or after the midpoint and rewriting from there.
|
|
103
|
+
*/
|
|
104
|
+
export function rotateIfNeeded(filePath, maxBytes = DEFAULT_MAX_BYTES) {
|
|
105
|
+
let stat;
|
|
106
|
+
try {
|
|
107
|
+
stat = fs.statSync(filePath);
|
|
108
|
+
} catch {
|
|
109
|
+
return; // file doesn't exist, nothing to rotate
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (stat.size <= maxBytes) return;
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
const buf = fs.readFileSync(filePath);
|
|
116
|
+
const mid = Math.floor(buf.length / 2);
|
|
117
|
+
const newlineIdx = buf.indexOf(0x0a, mid);
|
|
118
|
+
if (newlineIdx === -1) {
|
|
119
|
+
warn(
|
|
120
|
+
`Change events file exceeds ${maxBytes} bytes but contains no line breaks; skipping rotation`,
|
|
121
|
+
);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const kept = buf.slice(newlineIdx + 1);
|
|
125
|
+
fs.writeFileSync(filePath, kept);
|
|
126
|
+
debug(`Rotated change events: ${stat.size} → ${kept.length} bytes`);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
warn(`Failed to rotate change events: ${err.message}`);
|
|
129
|
+
}
|
|
130
|
+
}
|