@nithin554/code-crawler 0.0.0 → 0.1.2
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/LICENSE +21 -0
- package/README.md +221 -45
- package/dist/config.d.ts +13 -0
- package/dist/config.js +32 -0
- package/dist/config.js.map +1 -0
- package/dist/crawler.d.ts +46 -0
- package/dist/crawler.js +303 -0
- package/dist/crawler.js.map +1 -0
- package/dist/graph/queries.d.ts +111 -0
- package/dist/graph/queries.js +265 -0
- package/dist/graph/queries.js.map +1 -0
- package/dist/graph/schema.d.ts +63 -0
- package/dist/graph/schema.js +54 -0
- package/dist/graph/schema.js.map +1 -0
- package/dist/graph/store.d.ts +66 -0
- package/dist/graph/store.js +251 -0
- package/dist/graph/store.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +133 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/language.d.ts +111 -0
- package/dist/parser/language.js +13 -0
- package/dist/parser/language.js.map +1 -0
- package/dist/parser/languages/index.d.ts +5 -0
- package/dist/parser/languages/index.js +31 -0
- package/dist/parser/languages/index.js.map +1 -0
- package/dist/parser/languages/java.d.ts +10 -0
- package/dist/parser/languages/java.js +334 -0
- package/dist/parser/languages/java.js.map +1 -0
- package/dist/parser/treeSitter.d.ts +28 -0
- package/dist/parser/treeSitter.js +114 -0
- package/dist/parser/treeSitter.js.map +1 -0
- package/dist/resolver/javaResolver.d.ts +20 -0
- package/dist/resolver/javaResolver.js +253 -0
- package/dist/resolver/javaResolver.js.map +1 -0
- package/dist/resolver/types.d.ts +45 -0
- package/dist/resolver/types.js +80 -0
- package/dist/resolver/types.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +282 -0
- package/dist/server.js.map +1 -0
- package/package.json +44 -7
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
function nodeToRef(node) {
|
|
2
|
+
return {
|
|
3
|
+
id: node.id,
|
|
4
|
+
qualifiedName: node.qualifiedName,
|
|
5
|
+
name: node.properties.name,
|
|
6
|
+
kind: node.kind,
|
|
7
|
+
filePath: node.filePath,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/** Resolve a node by exact id or by qualified name, favoring id. */
|
|
11
|
+
function resolveNode(store, symbol) {
|
|
12
|
+
return store.getNode(symbol) ?? store.getNodeByQualifiedName(symbol);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Find every symbol that calls the given symbol, directly or transitively.
|
|
16
|
+
* Each row includes the edge chain depth so clients can render a call tree.
|
|
17
|
+
*/
|
|
18
|
+
export function getCallers(store, symbol, opts = {}) {
|
|
19
|
+
const target = resolveNode(store, symbol);
|
|
20
|
+
if (!target)
|
|
21
|
+
return null;
|
|
22
|
+
const transitive = opts.transitive ?? true;
|
|
23
|
+
const maxDepth = Math.max(1, opts.maxDepth ?? store.maxDepth ?? 10);
|
|
24
|
+
const limit = opts.limit ?? 100;
|
|
25
|
+
if (!transitive) {
|
|
26
|
+
const rows = store.raw(`SELECT n.id, n.qualified_name, n.kind, n.file_path, n.properties, e.properties AS edge_props
|
|
27
|
+
FROM edges e
|
|
28
|
+
JOIN nodes n ON n.id = e.source
|
|
29
|
+
WHERE e.target = ? AND e.kind = 'CALLS'
|
|
30
|
+
ORDER BY e.source
|
|
31
|
+
LIMIT ?`, symbol, limit);
|
|
32
|
+
return {
|
|
33
|
+
target: nodeToRef(target),
|
|
34
|
+
callers: rows.map((r) => ({
|
|
35
|
+
caller: rowToRef(r),
|
|
36
|
+
depth: 1,
|
|
37
|
+
callSiteLine: edgeLine(r.edge_props),
|
|
38
|
+
})),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const rows = store.raw(`WITH RECURSIVE callers(id, depth, path) AS (
|
|
42
|
+
SELECT e.source, 1, e.source || '/' || e.target
|
|
43
|
+
FROM edges e
|
|
44
|
+
WHERE e.target = ? AND e.kind = 'CALLS'
|
|
45
|
+
UNION ALL
|
|
46
|
+
SELECT e.source, c.depth + 1, c.path || '/' || e.source
|
|
47
|
+
FROM edges e
|
|
48
|
+
JOIN callers c ON e.target = c.id
|
|
49
|
+
WHERE e.kind = 'CALLS'
|
|
50
|
+
AND c.depth < ?
|
|
51
|
+
AND e.source || '/' || e.target NOT LIKE c.path || '/%'
|
|
52
|
+
)
|
|
53
|
+
SELECT n.id, n.qualified_name, n.kind, n.file_path, n.properties, c.depth
|
|
54
|
+
FROM callers c
|
|
55
|
+
JOIN nodes n ON n.id = c.id
|
|
56
|
+
ORDER BY c.depth, n.id
|
|
57
|
+
LIMIT ?`, symbol, maxDepth, limit);
|
|
58
|
+
return {
|
|
59
|
+
target: nodeToRef(target),
|
|
60
|
+
callers: rows.map((r) => ({ caller: rowToRef(r), depth: r.depth })),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Find every symbol the given symbol calls, directly or transitively.
|
|
65
|
+
*/
|
|
66
|
+
export function getCallees(store, symbol, opts = {}) {
|
|
67
|
+
const source = resolveNode(store, symbol);
|
|
68
|
+
if (!source)
|
|
69
|
+
return null;
|
|
70
|
+
const transitive = opts.transitive ?? true;
|
|
71
|
+
const maxDepth = Math.max(1, opts.maxDepth ?? store.maxDepth ?? 10);
|
|
72
|
+
const limit = opts.limit ?? 100;
|
|
73
|
+
if (!transitive) {
|
|
74
|
+
const rows = store.raw(`SELECT n.id, n.qualified_name, n.kind, n.file_path, n.properties, e.properties AS edge_props
|
|
75
|
+
FROM edges e
|
|
76
|
+
JOIN nodes n ON n.id = e.target
|
|
77
|
+
WHERE e.source = ? AND e.kind = 'CALLS'
|
|
78
|
+
ORDER BY e.target
|
|
79
|
+
LIMIT ?`, symbol, limit);
|
|
80
|
+
return {
|
|
81
|
+
source: nodeToRef(source),
|
|
82
|
+
callees: rows.map((r) => ({
|
|
83
|
+
callee: rowToRef(r),
|
|
84
|
+
depth: 1,
|
|
85
|
+
callSiteLine: edgeLine(r.edge_props),
|
|
86
|
+
})),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const rows = store.raw(`WITH RECURSIVE callees(id, depth, path) AS (
|
|
90
|
+
SELECT e.target, 1, e.source || '/' || e.target
|
|
91
|
+
FROM edges e
|
|
92
|
+
WHERE e.source = ? AND e.kind = 'CALLS'
|
|
93
|
+
UNION ALL
|
|
94
|
+
SELECT e.target, c.depth + 1, c.path || '/' || e.target
|
|
95
|
+
FROM edges e
|
|
96
|
+
JOIN callees c ON e.source = c.id
|
|
97
|
+
WHERE e.kind = 'CALLS'
|
|
98
|
+
AND c.depth < ?
|
|
99
|
+
AND e.source || '/' || e.target NOT LIKE c.path || '/%'
|
|
100
|
+
)
|
|
101
|
+
SELECT n.id, n.qualified_name, n.kind, n.file_path, n.properties, c.depth
|
|
102
|
+
FROM callees c
|
|
103
|
+
JOIN nodes n ON n.id = c.id
|
|
104
|
+
ORDER BY c.depth, n.id
|
|
105
|
+
LIMIT ?`, symbol, maxDepth, limit);
|
|
106
|
+
return {
|
|
107
|
+
source: nodeToRef(source),
|
|
108
|
+
callees: rows.map((r) => ({ callee: rowToRef(r), depth: r.depth })),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Find up to `limit` static call paths between two symbols.
|
|
113
|
+
* BFS over CALLS edges so the shortest paths come first.
|
|
114
|
+
*/
|
|
115
|
+
export function tracePath(store, from, to, opts = {}) {
|
|
116
|
+
const start = resolveNode(store, from);
|
|
117
|
+
const goal = resolveNode(store, to);
|
|
118
|
+
if (!start || !goal)
|
|
119
|
+
return null;
|
|
120
|
+
const maxDepth = Math.max(1, opts.maxDepth ?? store.maxDepth ?? 10);
|
|
121
|
+
const limit = opts.limit ?? 10;
|
|
122
|
+
// BFS in SQL via recursive CTE that accumulates the path taken.
|
|
123
|
+
const rows = store.raw(`WITH RECURSIVE paths(id, depth, path, edge_path) AS (
|
|
124
|
+
SELECT e.target, 1, e.source || '/' || e.target, e.source || '/' || e.target
|
|
125
|
+
FROM edges e
|
|
126
|
+
WHERE e.source = ? AND e.kind = 'CALLS'
|
|
127
|
+
UNION ALL
|
|
128
|
+
SELECT e.target, p.depth + 1, p.path || '/' || e.target, p.edge_path || '|' || e.source || '/' || e.target
|
|
129
|
+
FROM edges e
|
|
130
|
+
JOIN paths p ON e.source = p.id
|
|
131
|
+
WHERE e.kind = 'CALLS'
|
|
132
|
+
AND p.depth < ?
|
|
133
|
+
AND instr('|' || p.path || '|', '|' || e.target || '|') = 0
|
|
134
|
+
)
|
|
135
|
+
SELECT path, edge_path, depth
|
|
136
|
+
FROM paths
|
|
137
|
+
WHERE id = ?
|
|
138
|
+
ORDER BY depth
|
|
139
|
+
LIMIT ?`, from, maxDepth, to, limit);
|
|
140
|
+
return rows.map((r) => {
|
|
141
|
+
const ids = r.path.split("/");
|
|
142
|
+
const edges = r.edge_path.split("|").map((pair) => {
|
|
143
|
+
const [src, tgt] = pair.split("/");
|
|
144
|
+
return { source: src ?? "", target: tgt ?? "" };
|
|
145
|
+
});
|
|
146
|
+
return { path: ids, length: r.depth, edges };
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Extract the neighborhood around a symbol (any edge kinds), useful for
|
|
151
|
+
* visualization and for letting an LLM see the local shape of the graph.
|
|
152
|
+
*/
|
|
153
|
+
export function getSubgraph(store, symbol, opts = {}) {
|
|
154
|
+
const center = resolveNode(store, symbol);
|
|
155
|
+
if (!center)
|
|
156
|
+
return { center: null, nodes: [], edges: [] };
|
|
157
|
+
const radius = Math.max(1, opts.radius ?? 2);
|
|
158
|
+
const edgeKinds = opts.edgeKinds ?? ["CALLS", "DECLARED_IN", "CONTAINS", "EXTENDS", "IMPLEMENTS", "REFERENCES", "IMPORTS"];
|
|
159
|
+
const limit = opts.limit ?? 500;
|
|
160
|
+
const kinds = edgeKinds.map((k) => `'${k}'`).join(",");
|
|
161
|
+
const rows = store.raw(`WITH RECURSIVE reach(id, depth) AS (
|
|
162
|
+
SELECT ? , 0
|
|
163
|
+
UNION
|
|
164
|
+
SELECT e.target, r.depth + 1
|
|
165
|
+
FROM edges e
|
|
166
|
+
JOIN reach r ON e.source = r.id
|
|
167
|
+
WHERE e.kind IN (${kinds}) AND r.depth < ?
|
|
168
|
+
UNION
|
|
169
|
+
SELECT e.source, r.depth + 1
|
|
170
|
+
FROM edges e
|
|
171
|
+
JOIN reach r ON e.target = r.id
|
|
172
|
+
WHERE e.kind IN (${kinds}) AND r.depth < ?
|
|
173
|
+
)
|
|
174
|
+
SELECT n.id, n.qualified_name, n.kind, n.file_path, n.properties, r.depth
|
|
175
|
+
FROM reach r
|
|
176
|
+
JOIN nodes n ON n.id = r.id
|
|
177
|
+
ORDER BY r.depth, n.id
|
|
178
|
+
LIMIT ?`, symbol, radius, radius, limit);
|
|
179
|
+
const nodes = rows.map(rowToRef);
|
|
180
|
+
const ids = nodes.map((n) => n.id);
|
|
181
|
+
if (ids.length === 0)
|
|
182
|
+
return { center: nodeToRef(center), nodes, edges: [] };
|
|
183
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
184
|
+
const edgeRows = store.raw(`SELECT source, target, kind, properties
|
|
185
|
+
FROM edges
|
|
186
|
+
WHERE kind IN (${kinds})
|
|
187
|
+
AND source IN (${placeholders}) AND target IN (${placeholders})
|
|
188
|
+
LIMIT ?`, ...ids, ...ids, limit);
|
|
189
|
+
return {
|
|
190
|
+
center: nodeToRef(center),
|
|
191
|
+
nodes,
|
|
192
|
+
edges: edgeRows.map((r) => ({
|
|
193
|
+
source: r.source,
|
|
194
|
+
target: r.target,
|
|
195
|
+
kind: r.kind,
|
|
196
|
+
callSiteLine: edgeLine(r.properties),
|
|
197
|
+
})),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Fuzzy symbol lookup by short name or FQN. Matches substring of either the
|
|
202
|
+
* id, the qualified name, or the display name.
|
|
203
|
+
*/
|
|
204
|
+
export function searchSymbols(store, query, opts = {}) {
|
|
205
|
+
const q = query.trim().toLowerCase();
|
|
206
|
+
if (!q)
|
|
207
|
+
return [];
|
|
208
|
+
const limit = opts.limit ?? store.defaultLimit ?? 50;
|
|
209
|
+
const like = `%${q}%`;
|
|
210
|
+
const kindClause = opts.kind ? "AND n.kind = ?" : "";
|
|
211
|
+
const params = [like, like, like];
|
|
212
|
+
if (opts.kind)
|
|
213
|
+
params.push(opts.kind);
|
|
214
|
+
// ORDER BY CASE params: exact-qualified-name, then prefix/name match
|
|
215
|
+
params.push(q, like, limit);
|
|
216
|
+
const rows = store.raw(`SELECT n.id, n.qualified_name, n.kind, n.file_path, n.properties
|
|
217
|
+
FROM nodes n
|
|
218
|
+
WHERE LOWER(n.id) LIKE ? OR LOWER(n.qualified_name) LIKE ? OR LOWER(n.properties) LIKE ?
|
|
219
|
+
${kindClause}
|
|
220
|
+
ORDER BY
|
|
221
|
+
CASE WHEN LOWER(n.qualified_name) = ? THEN 0
|
|
222
|
+
WHEN LOWER(n.id) LIKE ? THEN 1
|
|
223
|
+
ELSE 2 END,
|
|
224
|
+
n.qualified_name
|
|
225
|
+
LIMIT ?`, ...params);
|
|
226
|
+
// Re-rank: exact-qualified-name matches first.
|
|
227
|
+
return rows
|
|
228
|
+
.map((r) => rowToRef(r))
|
|
229
|
+
.sort((a, b) => rank(a, q) - rank(b, q));
|
|
230
|
+
}
|
|
231
|
+
function rank(ref, q) {
|
|
232
|
+
if (ref.qualifiedName.toLowerCase() === q)
|
|
233
|
+
return 0;
|
|
234
|
+
const name = ref.name?.toLowerCase() ?? "";
|
|
235
|
+
if (name === q)
|
|
236
|
+
return 1;
|
|
237
|
+
if (ref.qualifiedName.toLowerCase().startsWith(q))
|
|
238
|
+
return 2;
|
|
239
|
+
return 3;
|
|
240
|
+
}
|
|
241
|
+
/** Suggest similar symbols when an exact lookup fails. */
|
|
242
|
+
export function suggestSymbols(store, symbol, limit = 8) {
|
|
243
|
+
const tokens = symbol
|
|
244
|
+
.split(/[.#$<>]/)
|
|
245
|
+
.filter((t) => t.length > 1)
|
|
246
|
+
.slice(-1);
|
|
247
|
+
const best = tokens[0] ?? symbol.slice(-20);
|
|
248
|
+
return searchSymbols(store, best, { limit });
|
|
249
|
+
}
|
|
250
|
+
function rowToRef(r) {
|
|
251
|
+
return {
|
|
252
|
+
id: r.id,
|
|
253
|
+
qualifiedName: r.qualified_name,
|
|
254
|
+
kind: r.kind,
|
|
255
|
+
filePath: r.file_path,
|
|
256
|
+
name: JSON.parse(r.properties).name,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function edgeLine(props) {
|
|
260
|
+
if (!props)
|
|
261
|
+
return undefined;
|
|
262
|
+
const line = JSON.parse(props).line;
|
|
263
|
+
return typeof line === "number" ? line : undefined;
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/graph/queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph schema for the code-crawler property graph.
|
|
3
|
+
*
|
|
4
|
+
* The graph is a labelled property graph stored in SQLite:
|
|
5
|
+
* - Nodes represent source-level symbols (files, classes, methods, fields, ...)
|
|
6
|
+
* - Directed edges represent relationships between symbols (calls, contains, ...)
|
|
7
|
+
*
|
|
8
|
+
* Both nodes and edges carry an arbitrary `properties` JSON blob so languages
|
|
9
|
+
* can attach extra metadata (signatures, modifiers, confidence scores) without
|
|
10
|
+
* schema migrations.
|
|
11
|
+
*/
|
|
12
|
+
export declare const NODE_KINDS: readonly ["file", "package", "class", "interface", "enum", "method", "constructor", "field", "import", "unknown_symbol"];
|
|
13
|
+
export type NodeKind = (typeof NODE_KINDS)[number];
|
|
14
|
+
export declare const EDGE_KINDS: readonly ["CALLS", "DECLARED_IN", "CONTAINS", "IMPLEMENTS", "EXTENDS", "IMPORTS", "REFERENCES"];
|
|
15
|
+
export type EdgeKind = (typeof EDGE_KINDS)[number];
|
|
16
|
+
/** Kinds that may appear as call *targets* (nodes at the arrow end of CALLS). */
|
|
17
|
+
export declare const CALLABLE_KINDS: readonly NodeKind[];
|
|
18
|
+
export interface NodeProperties {
|
|
19
|
+
/** Zero-based line of the declaration start. */
|
|
20
|
+
startLine?: number;
|
|
21
|
+
/** Zero-based line of the declaration end. */
|
|
22
|
+
endLine?: number;
|
|
23
|
+
/** Short display name (method/field/class simple name). */
|
|
24
|
+
name?: string;
|
|
25
|
+
/** Fully-qualified name when distinct from the node id. */
|
|
26
|
+
qualifiedName?: string;
|
|
27
|
+
/** Free-form language-specific metadata. */
|
|
28
|
+
[key: string]: string | number | boolean | (string | number)[] | undefined;
|
|
29
|
+
}
|
|
30
|
+
export interface GraphNode {
|
|
31
|
+
id: string;
|
|
32
|
+
kind: NodeKind;
|
|
33
|
+
/** Globally unique fully-qualified name, e.g. `com.acme.Foo#run`. */
|
|
34
|
+
qualifiedName: string;
|
|
35
|
+
/** Absolute path of the source file that declares this symbol. */
|
|
36
|
+
filePath: string | null;
|
|
37
|
+
properties: NodeProperties;
|
|
38
|
+
}
|
|
39
|
+
export interface EdgeProperties {
|
|
40
|
+
/** Zero-based line number of the call site / reference. */
|
|
41
|
+
line?: number;
|
|
42
|
+
/** For CALLS edges: whether the target was statically resolved. */
|
|
43
|
+
resolved?: boolean;
|
|
44
|
+
/** For CALLS edges: heuristic confidence 0..1 when resolved. */
|
|
45
|
+
confidence?: number;
|
|
46
|
+
/** Free-form language-specific metadata. */
|
|
47
|
+
[key: string]: string | number | boolean | undefined;
|
|
48
|
+
}
|
|
49
|
+
export interface GraphEdge {
|
|
50
|
+
source: string;
|
|
51
|
+
target: string;
|
|
52
|
+
kind: EdgeKind;
|
|
53
|
+
properties: EdgeProperties;
|
|
54
|
+
}
|
|
55
|
+
/** Node id scheme. `#` separates type from member so FQNs stay readable. */
|
|
56
|
+
export declare function methodNodeId(qualifiedType: string, methodName: string): string;
|
|
57
|
+
export declare function constructorNodeId(qualifiedType: string): string;
|
|
58
|
+
/** Field ids use `$` so they never collide with nested-type FQNs (`.`). */
|
|
59
|
+
export declare function fieldNodeId(qualifiedType: string, fieldName: string): string;
|
|
60
|
+
/** Import nodes represent external (or unindexed) dependencies. */
|
|
61
|
+
export declare function importNodeId(fqn: string): string;
|
|
62
|
+
/** Fallback id for calls that could not be statically resolved. */
|
|
63
|
+
export declare function unknownNodeId(qualifiedName: string): string;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph schema for the code-crawler property graph.
|
|
3
|
+
*
|
|
4
|
+
* The graph is a labelled property graph stored in SQLite:
|
|
5
|
+
* - Nodes represent source-level symbols (files, classes, methods, fields, ...)
|
|
6
|
+
* - Directed edges represent relationships between symbols (calls, contains, ...)
|
|
7
|
+
*
|
|
8
|
+
* Both nodes and edges carry an arbitrary `properties` JSON blob so languages
|
|
9
|
+
* can attach extra metadata (signatures, modifiers, confidence scores) without
|
|
10
|
+
* schema migrations.
|
|
11
|
+
*/
|
|
12
|
+
export const NODE_KINDS = [
|
|
13
|
+
"file",
|
|
14
|
+
"package",
|
|
15
|
+
"class",
|
|
16
|
+
"interface",
|
|
17
|
+
"enum",
|
|
18
|
+
"method",
|
|
19
|
+
"constructor",
|
|
20
|
+
"field",
|
|
21
|
+
"import",
|
|
22
|
+
"unknown_symbol",
|
|
23
|
+
];
|
|
24
|
+
export const EDGE_KINDS = [
|
|
25
|
+
"CALLS",
|
|
26
|
+
"DECLARED_IN",
|
|
27
|
+
"CONTAINS",
|
|
28
|
+
"IMPLEMENTS",
|
|
29
|
+
"EXTENDS",
|
|
30
|
+
"IMPORTS",
|
|
31
|
+
"REFERENCES",
|
|
32
|
+
];
|
|
33
|
+
/** Kinds that may appear as call *targets* (nodes at the arrow end of CALLS). */
|
|
34
|
+
export const CALLABLE_KINDS = ["method", "constructor"];
|
|
35
|
+
/** Node id scheme. `#` separates type from member so FQNs stay readable. */
|
|
36
|
+
export function methodNodeId(qualifiedType, methodName) {
|
|
37
|
+
return `${qualifiedType}#${methodName}`;
|
|
38
|
+
}
|
|
39
|
+
export function constructorNodeId(qualifiedType) {
|
|
40
|
+
return `${qualifiedType}#<init>`;
|
|
41
|
+
}
|
|
42
|
+
/** Field ids use `$` so they never collide with nested-type FQNs (`.`). */
|
|
43
|
+
export function fieldNodeId(qualifiedType, fieldName) {
|
|
44
|
+
return `${qualifiedType}$${fieldName}`;
|
|
45
|
+
}
|
|
46
|
+
/** Import nodes represent external (or unindexed) dependencies. */
|
|
47
|
+
export function importNodeId(fqn) {
|
|
48
|
+
return `__import__:${fqn}`;
|
|
49
|
+
}
|
|
50
|
+
/** Fallback id for calls that could not be statically resolved. */
|
|
51
|
+
export function unknownNodeId(qualifiedName) {
|
|
52
|
+
return `__unknown__:${qualifiedName}`;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/graph/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,MAAM;IACN,SAAS;IACT,OAAO;IACP,WAAW;IACX,MAAM;IACN,QAAQ;IACR,aAAa;IACb,OAAO;IACP,QAAQ;IACR,gBAAgB;CACR,CAAC;AAGX,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,OAAO;IACP,aAAa;IACb,UAAU;IACV,YAAY;IACZ,SAAS;IACT,SAAS;IACT,YAAY;CACJ,CAAC;AAGX,iFAAiF;AACjF,MAAM,CAAC,MAAM,cAAc,GAAwB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;AA2C7E,4EAA4E;AAC5E,MAAM,UAAU,YAAY,CAAC,aAAqB,EAAE,UAAkB;IACpE,OAAO,GAAG,aAAa,IAAI,UAAU,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,aAAqB;IACrD,OAAO,GAAG,aAAa,SAAS,CAAC;AACnC,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,aAAqB,EAAE,SAAiB;IAClE,OAAO,GAAG,aAAa,IAAI,SAAS,EAAE,CAAC;AACzC,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,cAAc,GAAG,EAAE,CAAC;AAC7B,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,aAAa,CAAC,aAAqB;IACjD,OAAO,eAAe,aAAa,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import type { EdgeKind, GraphEdge, GraphNode, EdgeProperties } from "./schema.js";
|
|
3
|
+
export interface StoreStats {
|
|
4
|
+
nodes: number;
|
|
5
|
+
edges: number;
|
|
6
|
+
files: number;
|
|
7
|
+
lastCrawl: string | null;
|
|
8
|
+
}
|
|
9
|
+
export declare class GraphStore {
|
|
10
|
+
readonly db: Database.Database;
|
|
11
|
+
/** Default cap on transitive traversal depth (config override). */
|
|
12
|
+
readonly maxDepth: number;
|
|
13
|
+
/** Default result cap for list queries (config override). */
|
|
14
|
+
readonly defaultLimit: number;
|
|
15
|
+
private insertNodeStmt;
|
|
16
|
+
private insertEdgeStmt;
|
|
17
|
+
private deleteFileNodesStmt;
|
|
18
|
+
private updateMetaStmt;
|
|
19
|
+
constructor(dbPath: string, options?: {
|
|
20
|
+
maxDepth?: number;
|
|
21
|
+
defaultLimit?: number;
|
|
22
|
+
});
|
|
23
|
+
close(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Persist a node. Idempotent: re-indexing an existing symbol just updates it.
|
|
26
|
+
*/
|
|
27
|
+
upsertNode(node: GraphNode): void;
|
|
28
|
+
/**
|
|
29
|
+
* Persist multiple nodes in a single transaction for batch ingestion.
|
|
30
|
+
*/
|
|
31
|
+
upsertNodes(nodes: GraphNode[]): void;
|
|
32
|
+
/**
|
|
33
|
+
* Persist an edge. Both endpoints must exist (FK enforced).
|
|
34
|
+
*/
|
|
35
|
+
upsertEdge(edge: GraphEdge): void;
|
|
36
|
+
upsertEdges(edges: GraphEdge[]): void;
|
|
37
|
+
getNode(id: string): GraphNode | null;
|
|
38
|
+
getNodeByQualifiedName(qn: string): GraphNode | null;
|
|
39
|
+
/** All nodes originating from one source file (for reindex + stats). */
|
|
40
|
+
getFileNodes(filePath: string): GraphNode[];
|
|
41
|
+
/**
|
|
42
|
+
* Remove every node declared in a file, plus their incident edges
|
|
43
|
+
* (cascade). Nodes referenced by the file but declared elsewhere survive.
|
|
44
|
+
*/
|
|
45
|
+
removeFile(filePath: string): void;
|
|
46
|
+
/**
|
|
47
|
+
* Rebuild the graph for a single file: drop its symbols and edges, then
|
|
48
|
+
* re-ingest. Call inside a caller-supplied transaction when batching.
|
|
49
|
+
*/
|
|
50
|
+
replaceFile(filePath: string, nodes: GraphNode[], edges: GraphEdge[]): void;
|
|
51
|
+
/** Ensure every CALLS edge endpoint exists (e.g. phantom unknown symbols). */
|
|
52
|
+
ensureNode(node: GraphNode): void;
|
|
53
|
+
setMeta(key: string, value: string): void;
|
|
54
|
+
getMeta(key: string): string | null;
|
|
55
|
+
stats(): StoreStats;
|
|
56
|
+
clearAll(): void;
|
|
57
|
+
/** Run a function inside a transaction. */
|
|
58
|
+
withTransaction<T>(fn: () => T): T;
|
|
59
|
+
/**
|
|
60
|
+
* SQLite uses 0-based row/col; tree-sitter uses 0-based row but 1-based col.
|
|
61
|
+
* Store and expose tree-sitter-compatible positions (0-based line) and let
|
|
62
|
+
* the presentation layer add +1 where humans expect 1-based lines.
|
|
63
|
+
*/
|
|
64
|
+
raw(sql: string, ...params: unknown[]): unknown;
|
|
65
|
+
}
|
|
66
|
+
export type { EdgeKind, EdgeProperties };
|