@ngommans/codefocus 0.1.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.
Potentially problematic release.
This version of @ngommans/codefocus might be problematic. Click here for more details.
- package/README.md +124 -0
- package/dist/benchmark-43DOYNYR.js +465 -0
- package/dist/benchmark-43DOYNYR.js.map +1 -0
- package/dist/chunk-6XH2ZLP6.js +127 -0
- package/dist/chunk-6XH2ZLP6.js.map +1 -0
- package/dist/chunk-7RYHZOYF.js +27 -0
- package/dist/chunk-7RYHZOYF.js.map +1 -0
- package/dist/chunk-ITVAEU6K.js +250 -0
- package/dist/chunk-ITVAEU6K.js.map +1 -0
- package/dist/chunk-Q6DOBQ4F.js +231 -0
- package/dist/chunk-Q6DOBQ4F.js.map +1 -0
- package/dist/chunk-X7DRJUEX.js +543 -0
- package/dist/chunk-X7DRJUEX.js.map +1 -0
- package/dist/cli.js +111 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands-ICBN54MT.js +64 -0
- package/dist/commands-ICBN54MT.js.map +1 -0
- package/dist/config-OCBWYENF.js +12 -0
- package/dist/config-OCBWYENF.js.map +1 -0
- package/dist/extended-benchmark-5RUXDG3D.js +323 -0
- package/dist/extended-benchmark-5RUXDG3D.js.map +1 -0
- package/dist/find-W5UDE4US.js +63 -0
- package/dist/find-W5UDE4US.js.map +1 -0
- package/dist/graph-DZNBEATA.js +189 -0
- package/dist/graph-DZNBEATA.js.map +1 -0
- package/dist/map-6WOMDLCP.js +131 -0
- package/dist/map-6WOMDLCP.js.map +1 -0
- package/dist/mcp-7WYTXIQS.js +354 -0
- package/dist/mcp-7WYTXIQS.js.map +1 -0
- package/dist/mcp-server.js +369 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/query-DJNWYYJD.js +427 -0
- package/dist/query-DJNWYYJD.js.map +1 -0
- package/dist/query-PS6QVPXP.js +538 -0
- package/dist/query-PS6QVPXP.js.map +1 -0
- package/dist/root-ODTOXM2J.js +10 -0
- package/dist/root-ODTOXM2J.js.map +1 -0
- package/dist/watcher-LFBZAM5E.js +73 -0
- package/dist/watcher-LFBZAM5E.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/db.ts
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
import { mkdirSync, existsSync } from "fs";
|
|
6
|
+
import { dirname } from "path";
|
|
7
|
+
var require2 = createRequire(import.meta.url);
|
|
8
|
+
var Database = require2("better-sqlite3");
|
|
9
|
+
var IndexDatabase = class {
|
|
10
|
+
db;
|
|
11
|
+
constructor(dbPath) {
|
|
12
|
+
const dir = dirname(dbPath);
|
|
13
|
+
if (!existsSync(dir)) {
|
|
14
|
+
mkdirSync(dir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
this.db = new Database(dbPath);
|
|
17
|
+
this.db.pragma("journal_mode = WAL");
|
|
18
|
+
this.db.pragma("foreign_keys = ON");
|
|
19
|
+
this.init();
|
|
20
|
+
}
|
|
21
|
+
init() {
|
|
22
|
+
this.db.exec(`
|
|
23
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
24
|
+
path TEXT PRIMARY KEY,
|
|
25
|
+
content_hash TEXT NOT NULL,
|
|
26
|
+
language TEXT,
|
|
27
|
+
last_indexed INTEGER
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
31
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
32
|
+
file_path TEXT NOT NULL,
|
|
33
|
+
name TEXT NOT NULL,
|
|
34
|
+
kind TEXT NOT NULL,
|
|
35
|
+
start_byte INTEGER,
|
|
36
|
+
end_byte INTEGER,
|
|
37
|
+
start_line INTEGER,
|
|
38
|
+
end_line INTEGER,
|
|
39
|
+
start_column INTEGER,
|
|
40
|
+
end_column INTEGER,
|
|
41
|
+
signature TEXT,
|
|
42
|
+
FOREIGN KEY (file_path) REFERENCES files(path) ON DELETE CASCADE
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
CREATE TABLE IF NOT EXISTS imports (
|
|
46
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
47
|
+
file_path TEXT NOT NULL,
|
|
48
|
+
specifier TEXT NOT NULL,
|
|
49
|
+
source_path TEXT,
|
|
50
|
+
raw_module TEXT NOT NULL,
|
|
51
|
+
is_type_only INTEGER DEFAULT 0,
|
|
52
|
+
FOREIGN KEY (file_path) REFERENCES files(path) ON DELETE CASCADE
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
CREATE TABLE IF NOT EXISTS "references" (
|
|
56
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
57
|
+
source_symbol_id INTEGER NOT NULL,
|
|
58
|
+
target_symbol_id INTEGER NOT NULL,
|
|
59
|
+
ref_type TEXT NOT NULL DEFAULT 'import',
|
|
60
|
+
FOREIGN KEY (source_symbol_id) REFERENCES symbols(id) ON DELETE CASCADE,
|
|
61
|
+
FOREIGN KEY (target_symbol_id) REFERENCES symbols(id) ON DELETE CASCADE
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file_path);
|
|
65
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
|
|
66
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_kind ON symbols(kind);
|
|
67
|
+
CREATE INDEX IF NOT EXISTS idx_imports_file ON imports(file_path);
|
|
68
|
+
CREATE INDEX IF NOT EXISTS idx_imports_source ON imports(source_path);
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_refs_source ON "references"(source_symbol_id);
|
|
70
|
+
CREATE INDEX IF NOT EXISTS idx_refs_target ON "references"(target_symbol_id);
|
|
71
|
+
|
|
72
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS content_fts USING fts5(
|
|
73
|
+
file_path,
|
|
74
|
+
content,
|
|
75
|
+
tokenize='unicode61'
|
|
76
|
+
);
|
|
77
|
+
`);
|
|
78
|
+
}
|
|
79
|
+
clearFile(filePath) {
|
|
80
|
+
this.db.prepare("DELETE FROM symbols WHERE file_path = ?").run(filePath);
|
|
81
|
+
this.db.prepare("DELETE FROM imports WHERE file_path = ?").run(filePath);
|
|
82
|
+
this.db.prepare("DELETE FROM files WHERE path = ?").run(filePath);
|
|
83
|
+
}
|
|
84
|
+
clearAllImports() {
|
|
85
|
+
this.db.prepare("DELETE FROM imports").run();
|
|
86
|
+
}
|
|
87
|
+
clearAllReferences() {
|
|
88
|
+
this.db.prepare('DELETE FROM "references"').run();
|
|
89
|
+
}
|
|
90
|
+
upsertFile(file) {
|
|
91
|
+
this.db.prepare(
|
|
92
|
+
`INSERT OR REPLACE INTO files (path, content_hash, language, last_indexed)
|
|
93
|
+
VALUES (@path, @content_hash, @language, @last_indexed)`
|
|
94
|
+
).run(file);
|
|
95
|
+
}
|
|
96
|
+
insertSymbol(symbol) {
|
|
97
|
+
const result = this.db.prepare(
|
|
98
|
+
`INSERT INTO symbols (file_path, name, kind, start_byte, end_byte, start_line, end_line, start_column, end_column, signature)
|
|
99
|
+
VALUES (@file_path, @name, @kind, @start_byte, @end_byte, @start_line, @end_line, @start_column, @end_column, @signature)`
|
|
100
|
+
).run(symbol);
|
|
101
|
+
return Number(result.lastInsertRowid);
|
|
102
|
+
}
|
|
103
|
+
insertImport(imp) {
|
|
104
|
+
const result = this.db.prepare(
|
|
105
|
+
`INSERT INTO imports (file_path, specifier, source_path, raw_module, is_type_only)
|
|
106
|
+
VALUES (@file_path, @specifier, @source_path, @raw_module, @is_type_only)`
|
|
107
|
+
).run(imp);
|
|
108
|
+
return Number(result.lastInsertRowid);
|
|
109
|
+
}
|
|
110
|
+
insertReference(ref) {
|
|
111
|
+
this.db.prepare(
|
|
112
|
+
`INSERT INTO "references" (source_symbol_id, target_symbol_id, ref_type)
|
|
113
|
+
VALUES (@source_symbol_id, @target_symbol_id, @ref_type)`
|
|
114
|
+
).run(ref);
|
|
115
|
+
}
|
|
116
|
+
upsertFileContent(filePath, content) {
|
|
117
|
+
this.db.prepare("DELETE FROM content_fts WHERE file_path = ?").run(filePath);
|
|
118
|
+
this.db.prepare("INSERT INTO content_fts (file_path, content) VALUES (?, ?)").run(filePath, content);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Search file content using FTS5 full-text search.
|
|
122
|
+
* Returns files with BM25 relevance scores and matching line ranges.
|
|
123
|
+
*/
|
|
124
|
+
searchContent(term) {
|
|
125
|
+
return this.db.prepare(
|
|
126
|
+
`SELECT file_path, -rank AS rank
|
|
127
|
+
FROM content_fts
|
|
128
|
+
WHERE content_fts MATCH ?
|
|
129
|
+
ORDER BY rank DESC`
|
|
130
|
+
).all(term);
|
|
131
|
+
}
|
|
132
|
+
getFileHash(filePath) {
|
|
133
|
+
const row = this.db.prepare("SELECT content_hash FROM files WHERE path = ?").get(filePath);
|
|
134
|
+
return row?.content_hash;
|
|
135
|
+
}
|
|
136
|
+
getSymbolsByFile(filePath) {
|
|
137
|
+
return this.db.prepare("SELECT * FROM symbols WHERE file_path = ?").all(filePath);
|
|
138
|
+
}
|
|
139
|
+
getSymbolById(id) {
|
|
140
|
+
return this.db.prepare("SELECT * FROM symbols WHERE id = ?").get(id);
|
|
141
|
+
}
|
|
142
|
+
getSymbolByName(name) {
|
|
143
|
+
return this.db.prepare("SELECT * FROM symbols WHERE name = ?").get(name);
|
|
144
|
+
}
|
|
145
|
+
findSymbolsByName(name) {
|
|
146
|
+
return this.db.prepare("SELECT * FROM symbols WHERE name LIKE ?").all(`%${name}%`);
|
|
147
|
+
}
|
|
148
|
+
findSymbols(name, kind) {
|
|
149
|
+
if (kind && kind !== "all") {
|
|
150
|
+
return this.db.prepare(
|
|
151
|
+
"SELECT * FROM symbols WHERE name LIKE ? AND kind = ? ORDER BY file_path, start_line"
|
|
152
|
+
).all(`%${name}%`, kind);
|
|
153
|
+
}
|
|
154
|
+
return this.db.prepare(
|
|
155
|
+
"SELECT * FROM symbols WHERE name LIKE ? ORDER BY file_path, start_line"
|
|
156
|
+
).all(`%${name}%`);
|
|
157
|
+
}
|
|
158
|
+
/** Get all file-to-file import edges (for file-level graph). */
|
|
159
|
+
getFileImportEdges() {
|
|
160
|
+
return this.db.prepare(
|
|
161
|
+
`SELECT file_path AS source_file,
|
|
162
|
+
source_path AS target_file,
|
|
163
|
+
GROUP_CONCAT(specifier, ', ') AS specifiers,
|
|
164
|
+
MAX(is_type_only) AS has_type_only
|
|
165
|
+
FROM imports
|
|
166
|
+
WHERE source_path IS NOT NULL
|
|
167
|
+
GROUP BY file_path, source_path
|
|
168
|
+
ORDER BY file_path, source_path`
|
|
169
|
+
).all();
|
|
170
|
+
}
|
|
171
|
+
/** Get outgoing symbol references (symbols this symbol depends on). */
|
|
172
|
+
getOutgoingReferences(symbolId) {
|
|
173
|
+
return this.db.prepare(
|
|
174
|
+
`SELECT s.id AS target_id, s.name AS target_name, s.kind AS target_kind,
|
|
175
|
+
s.file_path AS target_file, s.start_line AS target_line, r.ref_type
|
|
176
|
+
FROM "references" r
|
|
177
|
+
JOIN symbols s ON r.target_symbol_id = s.id
|
|
178
|
+
WHERE r.source_symbol_id = ?
|
|
179
|
+
ORDER BY s.file_path, s.start_line`
|
|
180
|
+
).all(symbolId);
|
|
181
|
+
}
|
|
182
|
+
/** Get incoming symbol references (symbols that depend on this symbol). */
|
|
183
|
+
getIncomingReferences(symbolId) {
|
|
184
|
+
return this.db.prepare(
|
|
185
|
+
`SELECT s.id AS source_id, s.name AS source_name, s.kind AS source_kind,
|
|
186
|
+
s.file_path AS source_file, s.start_line AS source_line, r.ref_type
|
|
187
|
+
FROM "references" r
|
|
188
|
+
JOIN symbols s ON r.source_symbol_id = s.id
|
|
189
|
+
WHERE r.target_symbol_id = ?
|
|
190
|
+
ORDER BY s.file_path, s.start_line`
|
|
191
|
+
).all(symbolId);
|
|
192
|
+
}
|
|
193
|
+
/** Get in-degree (number of incoming references) for a symbol. */
|
|
194
|
+
getSymbolInDegree(symbolId) {
|
|
195
|
+
const row = this.db.prepare(
|
|
196
|
+
'SELECT COUNT(*) as count FROM "references" WHERE target_symbol_id = ?'
|
|
197
|
+
).get(symbolId);
|
|
198
|
+
return row.count;
|
|
199
|
+
}
|
|
200
|
+
/** Get all files in the index. */
|
|
201
|
+
getAllFiles() {
|
|
202
|
+
return this.db.prepare("SELECT * FROM files ORDER BY path").all();
|
|
203
|
+
}
|
|
204
|
+
countFiles() {
|
|
205
|
+
const row = this.db.prepare("SELECT COUNT(*) as count FROM files").get();
|
|
206
|
+
return row.count;
|
|
207
|
+
}
|
|
208
|
+
countSymbols() {
|
|
209
|
+
const row = this.db.prepare("SELECT COUNT(*) as count FROM symbols").get();
|
|
210
|
+
return row.count;
|
|
211
|
+
}
|
|
212
|
+
countImports() {
|
|
213
|
+
const row = this.db.prepare("SELECT COUNT(*) as count FROM imports").get();
|
|
214
|
+
return row.count;
|
|
215
|
+
}
|
|
216
|
+
countReferences() {
|
|
217
|
+
const row = this.db.prepare('SELECT COUNT(*) as count FROM "references"').get();
|
|
218
|
+
return row.count;
|
|
219
|
+
}
|
|
220
|
+
transaction(fn) {
|
|
221
|
+
return this.db.transaction(fn)();
|
|
222
|
+
}
|
|
223
|
+
close() {
|
|
224
|
+
this.db.close();
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export {
|
|
229
|
+
IndexDatabase
|
|
230
|
+
};
|
|
231
|
+
//# sourceMappingURL=chunk-Q6DOBQ4F.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/db.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { mkdirSync, existsSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nconst require = createRequire(import.meta.url);\nconst Database = require(\"better-sqlite3\");\n\nexport interface FileRow {\n path: string;\n content_hash: string;\n language: string;\n last_indexed: number;\n}\n\nexport interface SymbolRow {\n id?: number;\n file_path: string;\n name: string;\n kind: string;\n start_byte: number;\n end_byte: number;\n start_line: number;\n end_line: number;\n start_column: number;\n end_column: number;\n signature: string | null;\n}\n\nexport interface ImportRow {\n id?: number;\n file_path: string;\n specifier: string;\n source_path: string | null;\n raw_module: string;\n is_type_only: number;\n}\n\nexport interface ReferenceRow {\n id?: number;\n source_symbol_id: number;\n target_symbol_id: number;\n ref_type: string;\n}\n\nexport class IndexDatabase {\n private db: InstanceType<typeof Database>;\n\n constructor(dbPath: string) {\n const dir = dirname(dbPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n this.db = new Database(dbPath);\n this.db.pragma(\"journal_mode = WAL\");\n this.db.pragma(\"foreign_keys = ON\");\n this.init();\n }\n\n private init(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS files (\n path TEXT PRIMARY KEY,\n content_hash TEXT NOT NULL,\n language TEXT,\n last_indexed INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS symbols (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n file_path TEXT NOT NULL,\n name TEXT NOT NULL,\n kind TEXT NOT NULL,\n start_byte INTEGER,\n end_byte INTEGER,\n start_line INTEGER,\n end_line INTEGER,\n start_column INTEGER,\n end_column INTEGER,\n signature TEXT,\n FOREIGN KEY (file_path) REFERENCES files(path) ON DELETE CASCADE\n );\n\n CREATE TABLE IF NOT EXISTS imports (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n file_path TEXT NOT NULL,\n specifier TEXT NOT NULL,\n source_path TEXT,\n raw_module TEXT NOT NULL,\n is_type_only INTEGER DEFAULT 0,\n FOREIGN KEY (file_path) REFERENCES files(path) ON DELETE CASCADE\n );\n\n CREATE TABLE IF NOT EXISTS \"references\" (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n source_symbol_id INTEGER NOT NULL,\n target_symbol_id INTEGER NOT NULL,\n ref_type TEXT NOT NULL DEFAULT 'import',\n FOREIGN KEY (source_symbol_id) REFERENCES symbols(id) ON DELETE CASCADE,\n FOREIGN KEY (target_symbol_id) REFERENCES symbols(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file_path);\n CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);\n CREATE INDEX IF NOT EXISTS idx_symbols_kind ON symbols(kind);\n CREATE INDEX IF NOT EXISTS idx_imports_file ON imports(file_path);\n CREATE INDEX IF NOT EXISTS idx_imports_source ON imports(source_path);\n CREATE INDEX IF NOT EXISTS idx_refs_source ON \"references\"(source_symbol_id);\n CREATE INDEX IF NOT EXISTS idx_refs_target ON \"references\"(target_symbol_id);\n\n CREATE VIRTUAL TABLE IF NOT EXISTS content_fts USING fts5(\n file_path,\n content,\n tokenize='unicode61'\n );\n `);\n }\n\n clearFile(filePath: string): void {\n this.db.prepare(\"DELETE FROM symbols WHERE file_path = ?\").run(filePath);\n this.db.prepare(\"DELETE FROM imports WHERE file_path = ?\").run(filePath);\n this.db.prepare(\"DELETE FROM files WHERE path = ?\").run(filePath);\n }\n\n clearAllImports(): void {\n this.db.prepare(\"DELETE FROM imports\").run();\n }\n\n clearAllReferences(): void {\n this.db.prepare('DELETE FROM \"references\"').run();\n }\n\n upsertFile(file: FileRow): void {\n this.db\n .prepare(\n `INSERT OR REPLACE INTO files (path, content_hash, language, last_indexed)\n VALUES (@path, @content_hash, @language, @last_indexed)`,\n )\n .run(file);\n }\n\n insertSymbol(symbol: SymbolRow): number {\n const result = this.db\n .prepare(\n `INSERT INTO symbols (file_path, name, kind, start_byte, end_byte, start_line, end_line, start_column, end_column, signature)\n VALUES (@file_path, @name, @kind, @start_byte, @end_byte, @start_line, @end_line, @start_column, @end_column, @signature)`,\n )\n .run(symbol);\n return Number(result.lastInsertRowid);\n }\n\n insertImport(imp: ImportRow): number {\n const result = this.db\n .prepare(\n `INSERT INTO imports (file_path, specifier, source_path, raw_module, is_type_only)\n VALUES (@file_path, @specifier, @source_path, @raw_module, @is_type_only)`,\n )\n .run(imp);\n return Number(result.lastInsertRowid);\n }\n\n insertReference(ref: ReferenceRow): void {\n this.db\n .prepare(\n `INSERT INTO \"references\" (source_symbol_id, target_symbol_id, ref_type)\n VALUES (@source_symbol_id, @target_symbol_id, @ref_type)`,\n )\n .run(ref);\n }\n\n upsertFileContent(filePath: string, content: string): void {\n // Delete old entry first (FTS5 doesn't support REPLACE)\n this.db.prepare(\"DELETE FROM content_fts WHERE file_path = ?\").run(filePath);\n this.db\n .prepare(\"INSERT INTO content_fts (file_path, content) VALUES (?, ?)\")\n .run(filePath, content);\n }\n\n /**\n * Search file content using FTS5 full-text search.\n * Returns files with BM25 relevance scores and matching line ranges.\n */\n searchContent(\n term: string,\n ): Array<{ file_path: string; rank: number }> {\n // FTS5 rank is negative (more negative = more relevant), so we negate it\n return this.db\n .prepare(\n `SELECT file_path, -rank AS rank\n FROM content_fts\n WHERE content_fts MATCH ?\n ORDER BY rank DESC`,\n )\n .all(term) as Array<{ file_path: string; rank: number }>;\n }\n\n getFileHash(filePath: string): string | undefined {\n const row = this.db\n .prepare(\"SELECT content_hash FROM files WHERE path = ?\")\n .get(filePath) as { content_hash: string } | undefined;\n return row?.content_hash;\n }\n\n getSymbolsByFile(filePath: string): SymbolRow[] {\n return this.db\n .prepare(\"SELECT * FROM symbols WHERE file_path = ?\")\n .all(filePath) as SymbolRow[];\n }\n\n getSymbolById(id: number): SymbolRow | undefined {\n return this.db\n .prepare(\"SELECT * FROM symbols WHERE id = ?\")\n .get(id) as SymbolRow | undefined;\n }\n\n getSymbolByName(name: string): SymbolRow | undefined {\n return this.db\n .prepare(\"SELECT * FROM symbols WHERE name = ?\")\n .get(name) as SymbolRow | undefined;\n }\n\n findSymbolsByName(name: string): SymbolRow[] {\n return this.db\n .prepare(\"SELECT * FROM symbols WHERE name LIKE ?\")\n .all(`%${name}%`) as SymbolRow[];\n }\n\n findSymbols(name: string, kind?: string): SymbolRow[] {\n if (kind && kind !== \"all\") {\n return this.db\n .prepare(\n \"SELECT * FROM symbols WHERE name LIKE ? AND kind = ? ORDER BY file_path, start_line\",\n )\n .all(`%${name}%`, kind) as SymbolRow[];\n }\n return this.db\n .prepare(\n \"SELECT * FROM symbols WHERE name LIKE ? ORDER BY file_path, start_line\",\n )\n .all(`%${name}%`) as SymbolRow[];\n }\n\n /** Get all file-to-file import edges (for file-level graph). */\n getFileImportEdges(): Array<{\n source_file: string;\n target_file: string;\n specifiers: string;\n has_type_only: number;\n }> {\n return this.db\n .prepare(\n `SELECT file_path AS source_file,\n source_path AS target_file,\n GROUP_CONCAT(specifier, ', ') AS specifiers,\n MAX(is_type_only) AS has_type_only\n FROM imports\n WHERE source_path IS NOT NULL\n GROUP BY file_path, source_path\n ORDER BY file_path, source_path`,\n )\n .all() as Array<{\n source_file: string;\n target_file: string;\n specifiers: string;\n has_type_only: number;\n }>;\n }\n\n /** Get outgoing symbol references (symbols this symbol depends on). */\n getOutgoingReferences(\n symbolId: number,\n ): Array<{\n target_id: number;\n target_name: string;\n target_kind: string;\n target_file: string;\n target_line: number;\n ref_type: string;\n }> {\n return this.db\n .prepare(\n `SELECT s.id AS target_id, s.name AS target_name, s.kind AS target_kind,\n s.file_path AS target_file, s.start_line AS target_line, r.ref_type\n FROM \"references\" r\n JOIN symbols s ON r.target_symbol_id = s.id\n WHERE r.source_symbol_id = ?\n ORDER BY s.file_path, s.start_line`,\n )\n .all(symbolId) as Array<{\n target_id: number;\n target_name: string;\n target_kind: string;\n target_file: string;\n target_line: number;\n ref_type: string;\n }>;\n }\n\n /** Get incoming symbol references (symbols that depend on this symbol). */\n getIncomingReferences(\n symbolId: number,\n ): Array<{\n source_id: number;\n source_name: string;\n source_kind: string;\n source_file: string;\n source_line: number;\n ref_type: string;\n }> {\n return this.db\n .prepare(\n `SELECT s.id AS source_id, s.name AS source_name, s.kind AS source_kind,\n s.file_path AS source_file, s.start_line AS source_line, r.ref_type\n FROM \"references\" r\n JOIN symbols s ON r.source_symbol_id = s.id\n WHERE r.target_symbol_id = ?\n ORDER BY s.file_path, s.start_line`,\n )\n .all(symbolId) as Array<{\n source_id: number;\n source_name: string;\n source_kind: string;\n source_file: string;\n source_line: number;\n ref_type: string;\n }>;\n }\n\n /** Get in-degree (number of incoming references) for a symbol. */\n getSymbolInDegree(symbolId: number): number {\n const row = this.db\n .prepare(\n 'SELECT COUNT(*) as count FROM \"references\" WHERE target_symbol_id = ?',\n )\n .get(symbolId) as { count: number };\n return row.count;\n }\n\n /** Get all files in the index. */\n getAllFiles(): FileRow[] {\n return this.db\n .prepare(\"SELECT * FROM files ORDER BY path\")\n .all() as FileRow[];\n }\n\n countFiles(): number {\n const row = this.db\n .prepare(\"SELECT COUNT(*) as count FROM files\")\n .get() as { count: number };\n return row.count;\n }\n\n countSymbols(): number {\n const row = this.db\n .prepare(\"SELECT COUNT(*) as count FROM symbols\")\n .get() as { count: number };\n return row.count;\n }\n\n countImports(): number {\n const row = this.db\n .prepare(\"SELECT COUNT(*) as count FROM imports\")\n .get() as { count: number };\n return row.count;\n }\n\n countReferences(): number {\n const row = this.db\n .prepare('SELECT COUNT(*) as count FROM \"references\"')\n .get() as { count: number };\n return row.count;\n }\n\n transaction<T>(fn: () => T): T {\n return this.db.transaction(fn)();\n }\n\n close(): void {\n this.db.close();\n }\n}\n"],"mappings":"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","names":["require"]}
|