@monoes/monograph 1.6.11 → 1.6.12
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/dist/src/pipeline/phases/orm-helpers.d.ts +11 -0
- package/dist/src/pipeline/phases/orm-helpers.d.ts.map +1 -0
- package/dist/src/pipeline/phases/orm-helpers.js +72 -0
- package/dist/src/pipeline/phases/orm-helpers.js.map +1 -0
- package/dist/src/pipeline/phases/orm-mongoose.d.ts +8 -0
- package/dist/src/pipeline/phases/orm-mongoose.d.ts.map +1 -0
- package/dist/src/pipeline/phases/orm-mongoose.js +51 -0
- package/dist/src/pipeline/phases/orm-mongoose.js.map +1 -0
- package/dist/src/pipeline/phases/orm-prisma.d.ts +8 -0
- package/dist/src/pipeline/phases/orm-prisma.d.ts.map +1 -0
- package/dist/src/pipeline/phases/orm-prisma.js +41 -0
- package/dist/src/pipeline/phases/orm-prisma.js.map +1 -0
- package/dist/src/pipeline/phases/orm-sequelize.d.ts +12 -0
- package/dist/src/pipeline/phases/orm-sequelize.d.ts.map +1 -0
- package/dist/src/pipeline/phases/orm-sequelize.js +97 -0
- package/dist/src/pipeline/phases/orm-sequelize.js.map +1 -0
- package/dist/src/pipeline/phases/orm-sqlalchemy.d.ts +8 -0
- package/dist/src/pipeline/phases/orm-sqlalchemy.d.ts.map +1 -0
- package/dist/src/pipeline/phases/orm-sqlalchemy.js +42 -0
- package/dist/src/pipeline/phases/orm-sqlalchemy.js.map +1 -0
- package/dist/src/pipeline/phases/orm-typeorm.d.ts +10 -0
- package/dist/src/pipeline/phases/orm-typeorm.d.ts.map +1 -0
- package/dist/src/pipeline/phases/orm-typeorm.js +52 -0
- package/dist/src/pipeline/phases/orm-typeorm.js.map +1 -0
- package/dist/src/pipeline/phases/orm.d.ts.map +1 -1
- package/dist/src/pipeline/phases/orm.js +6 -336
- package/dist/src/pipeline/phases/orm.js.map +1 -1
- package/dist/src/pipeline/phases/scope-resolution-edges.d.ts +9 -0
- package/dist/src/pipeline/phases/scope-resolution-edges.d.ts.map +1 -0
- package/dist/src/pipeline/phases/scope-resolution-edges.js +30 -0
- package/dist/src/pipeline/phases/scope-resolution-edges.js.map +1 -0
- package/dist/src/pipeline/phases/scope-resolution-function-index.d.ts +31 -0
- package/dist/src/pipeline/phases/scope-resolution-function-index.d.ts.map +1 -0
- package/dist/src/pipeline/phases/scope-resolution-function-index.js +96 -0
- package/dist/src/pipeline/phases/scope-resolution-function-index.js.map +1 -0
- package/dist/src/pipeline/phases/scope-resolution-target.d.ts +6 -0
- package/dist/src/pipeline/phases/scope-resolution-target.d.ts.map +1 -0
- package/dist/src/pipeline/phases/scope-resolution-target.js +69 -0
- package/dist/src/pipeline/phases/scope-resolution-target.js.map +1 -0
- package/dist/src/pipeline/phases/scope-resolution.d.ts.map +1 -1
- package/dist/src/pipeline/phases/scope-resolution.js +3 -189
- package/dist/src/pipeline/phases/scope-resolution.js.map +1 -1
- package/dist/src/storage/fts-store-hybrid.d.ts +17 -0
- package/dist/src/storage/fts-store-hybrid.d.ts.map +1 -0
- package/dist/src/storage/fts-store-hybrid.js +119 -0
- package/dist/src/storage/fts-store-hybrid.js.map +1 -0
- package/dist/src/storage/fts-store-search.d.ts +29 -0
- package/dist/src/storage/fts-store-search.d.ts.map +1 -0
- package/dist/src/storage/fts-store-search.js +177 -0
- package/dist/src/storage/fts-store-search.js.map +1 -0
- package/dist/src/storage/fts-store-terms.d.ts +8 -0
- package/dist/src/storage/fts-store-terms.d.ts.map +1 -0
- package/dist/src/storage/fts-store-terms.js +174 -0
- package/dist/src/storage/fts-store-terms.js.map +1 -0
- package/dist/src/storage/fts-store.d.ts +12 -46
- package/dist/src/storage/fts-store.d.ts.map +1 -1
- package/dist/src/storage/fts-store.js +11 -459
- package/dist/src/storage/fts-store.js.map +1 -1
- package/dist/src/web/api-queries.d.ts +89 -0
- package/dist/src/web/api-queries.d.ts.map +1 -0
- package/dist/src/web/api-queries.js +245 -0
- package/dist/src/web/api-queries.js.map +1 -0
- package/dist/src/web/api.d.ts +2 -87
- package/dist/src/web/api.d.ts.map +1 -1
- package/dist/src/web/api.js +3 -244
- package/dist/src/web/api.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { readIndexScope } from '../pipeline/index-scope.js';
|
|
5
|
+
import { ftsSearch } from '../storage/fts-store.js';
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const PKG_VERSION = (() => {
|
|
8
|
+
try {
|
|
9
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8'));
|
|
10
|
+
return pkg.version ?? '0.0.0';
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return '0.0.0';
|
|
14
|
+
}
|
|
15
|
+
})();
|
|
16
|
+
// ── Query helpers (testable in isolation) ─────────────────────────────────────
|
|
17
|
+
export function rowToApiNode(row) {
|
|
18
|
+
return {
|
|
19
|
+
id: row.id,
|
|
20
|
+
name: row.name,
|
|
21
|
+
label: row.label,
|
|
22
|
+
filePath: row.file_path ?? null,
|
|
23
|
+
startLine: row.start_line ?? null,
|
|
24
|
+
endLine: row.end_line ?? null,
|
|
25
|
+
communityId: row.community_id ?? null,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function queryGraphData(db) {
|
|
29
|
+
const totalNodeCount = db.prepare('SELECT COUNT(*) as c FROM nodes').get().c;
|
|
30
|
+
const totalEdgeCount = db.prepare('SELECT COUNT(*) as c FROM edges').get().c;
|
|
31
|
+
// Select the most connected nodes for a representative visualization
|
|
32
|
+
const nodeRows = db
|
|
33
|
+
.prepare(`SELECT n.id, n.name, n.label, n.file_path, n.start_line, n.end_line, n.community_id
|
|
34
|
+
FROM nodes n
|
|
35
|
+
LEFT JOIN (
|
|
36
|
+
SELECT node_id, SUM(cnt) AS deg FROM (
|
|
37
|
+
SELECT source_id AS node_id, COUNT(*) AS cnt FROM edges GROUP BY source_id
|
|
38
|
+
UNION ALL
|
|
39
|
+
SELECT target_id AS node_id, COUNT(*) AS cnt FROM edges GROUP BY target_id
|
|
40
|
+
) GROUP BY node_id
|
|
41
|
+
) d ON d.node_id = n.id
|
|
42
|
+
ORDER BY d.deg DESC NULLS LAST
|
|
43
|
+
LIMIT 2000`)
|
|
44
|
+
.all();
|
|
45
|
+
const nodes = nodeRows.map(rowToApiNode);
|
|
46
|
+
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
47
|
+
const edges = [];
|
|
48
|
+
if (nodeIds.size > 0) {
|
|
49
|
+
// Use a temp table so SQLite filters edges instead of scanning full table in JS
|
|
50
|
+
db.exec('CREATE TEMP TABLE IF NOT EXISTS _vis_nodes (id TEXT PRIMARY KEY)');
|
|
51
|
+
db.exec('DELETE FROM _vis_nodes');
|
|
52
|
+
const insertVis = db.prepare('INSERT OR IGNORE INTO _vis_nodes (id) VALUES (?)');
|
|
53
|
+
const insertAll = db.transaction((ids) => {
|
|
54
|
+
for (const id of ids)
|
|
55
|
+
insertVis.run(id);
|
|
56
|
+
});
|
|
57
|
+
insertAll([...nodeIds]);
|
|
58
|
+
const edgeRows = db
|
|
59
|
+
.prepare(`SELECT e.source_id, e.target_id, e.relation, e.confidence_score FROM edges e
|
|
60
|
+
JOIN _vis_nodes s ON e.source_id = s.id
|
|
61
|
+
JOIN _vis_nodes t ON e.target_id = t.id
|
|
62
|
+
LIMIT 10000`)
|
|
63
|
+
.all();
|
|
64
|
+
for (const r of edgeRows) {
|
|
65
|
+
edges.push({
|
|
66
|
+
sourceId: r.source_id,
|
|
67
|
+
targetId: r.target_id,
|
|
68
|
+
relation: r.relation,
|
|
69
|
+
confidenceScore: r.confidence_score,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const communities = {};
|
|
74
|
+
for (const node of nodes) {
|
|
75
|
+
if (node.communityId != null) {
|
|
76
|
+
const key = String(node.communityId);
|
|
77
|
+
if (!communities[key])
|
|
78
|
+
communities[key] = [];
|
|
79
|
+
communities[key].push(node.id);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
nodes,
|
|
84
|
+
edges,
|
|
85
|
+
communities,
|
|
86
|
+
totalNodeCount,
|
|
87
|
+
totalEdgeCount,
|
|
88
|
+
truncated: totalNodeCount > nodes.length,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function queryNode(db, id) {
|
|
92
|
+
const nodeRow = db
|
|
93
|
+
.prepare('SELECT id, name, label, file_path, start_line, end_line, community_id FROM nodes WHERE id = ?')
|
|
94
|
+
.get(id);
|
|
95
|
+
if (!nodeRow)
|
|
96
|
+
return { node: null, callers: [], callees: [] };
|
|
97
|
+
const node = rowToApiNode(nodeRow);
|
|
98
|
+
const callerRows = db
|
|
99
|
+
.prepare(`SELECT n.id, n.name, n.label, n.file_path, n.start_line, n.end_line, n.community_id
|
|
100
|
+
FROM nodes n JOIN edges e ON n.id = e.source_id
|
|
101
|
+
WHERE e.target_id = ? AND e.relation = 'CALLS' LIMIT 20`)
|
|
102
|
+
.all(id);
|
|
103
|
+
const calleeRows = db
|
|
104
|
+
.prepare(`SELECT n.id, n.name, n.label, n.file_path, n.start_line, n.end_line, n.community_id
|
|
105
|
+
FROM nodes n JOIN edges e ON n.id = e.target_id
|
|
106
|
+
WHERE e.source_id = ? AND e.relation = 'CALLS' LIMIT 20`)
|
|
107
|
+
.all(id);
|
|
108
|
+
return {
|
|
109
|
+
node,
|
|
110
|
+
callers: callerRows.map(rowToApiNode),
|
|
111
|
+
callees: calleeRows.map(rowToApiNode),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export function querySearch(db, q) {
|
|
115
|
+
const results = ftsSearch(db, q, 10);
|
|
116
|
+
return results.map((r) => ({
|
|
117
|
+
id: r.id,
|
|
118
|
+
name: r.name,
|
|
119
|
+
label: r.label,
|
|
120
|
+
filePath: r.filePath,
|
|
121
|
+
startLine: r.startLine ?? null,
|
|
122
|
+
endLine: r.endLine ?? null,
|
|
123
|
+
communityId: null,
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
export function queryStats(db) {
|
|
127
|
+
const nodeCount = db.prepare('SELECT COUNT(*) as c FROM nodes').get().c;
|
|
128
|
+
const edgeCount = db.prepare('SELECT COUNT(*) as c FROM edges').get().c;
|
|
129
|
+
const communityCount = db
|
|
130
|
+
.prepare('SELECT COUNT(DISTINCT community_id) as c FROM nodes WHERE community_id IS NOT NULL')
|
|
131
|
+
.get().c;
|
|
132
|
+
const metaRow = db.prepare("SELECT value FROM index_meta WHERE key = 'indexed_at'").get();
|
|
133
|
+
return {
|
|
134
|
+
nodeCount,
|
|
135
|
+
edgeCount,
|
|
136
|
+
communityCount,
|
|
137
|
+
buildAt: metaRow?.value ?? null,
|
|
138
|
+
scope: readIndexScope(db),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
export function queryGrep(db, pattern, caseSensitive) {
|
|
142
|
+
const sql = caseSensitive
|
|
143
|
+
? `SELECT id, name, label, file_path, start_line FROM nodes WHERE name GLOB ? LIMIT 100`
|
|
144
|
+
: `SELECT id, name, label, file_path, start_line FROM nodes WHERE name LIKE ? LIMIT 100`;
|
|
145
|
+
const param = caseSensitive ? `*${pattern}*` : `%${pattern}%`;
|
|
146
|
+
const rows = db.prepare(sql).all(param);
|
|
147
|
+
return rows.map((r) => ({
|
|
148
|
+
id: r.id,
|
|
149
|
+
name: r.name,
|
|
150
|
+
label: r.label,
|
|
151
|
+
filePath: r.file_path ?? null,
|
|
152
|
+
startLine: r.start_line ?? null,
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
export function readFileContent(filePath, startLine, endLine) {
|
|
156
|
+
const raw = readFileSync(filePath, 'utf8');
|
|
157
|
+
const allLines = raw.split('\n');
|
|
158
|
+
// Remove trailing empty line from split
|
|
159
|
+
if (allLines.length > 0 && allLines[allLines.length - 1] === '')
|
|
160
|
+
allLines.pop();
|
|
161
|
+
const start = startLine ?? 1;
|
|
162
|
+
const end = endLine ?? allLines.length;
|
|
163
|
+
const lines = [];
|
|
164
|
+
for (let i = start - 1; i < end && i < allLines.length; i++) {
|
|
165
|
+
lines.push({ number: i + 1, content: allLines[i] });
|
|
166
|
+
}
|
|
167
|
+
return { path: filePath, totalLines: allLines.length, lines };
|
|
168
|
+
}
|
|
169
|
+
export function queryClusters(db) {
|
|
170
|
+
try {
|
|
171
|
+
const rows = db
|
|
172
|
+
.prepare(`SELECT c.id, c.label, COUNT(n.id) as memberCount
|
|
173
|
+
FROM communities c LEFT JOIN nodes n ON n.community_id = c.id
|
|
174
|
+
GROUP BY c.id ORDER BY memberCount DESC`)
|
|
175
|
+
.all();
|
|
176
|
+
return rows;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
const rows = db
|
|
180
|
+
.prepare(`SELECT community_id as id, COUNT(*) as memberCount FROM nodes WHERE community_id IS NOT NULL GROUP BY community_id ORDER BY memberCount DESC`)
|
|
181
|
+
.all();
|
|
182
|
+
return rows.map((r) => ({ id: r.id, label: null, memberCount: r.memberCount }));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export function queryCluster(db, name) {
|
|
186
|
+
try {
|
|
187
|
+
const comm = db
|
|
188
|
+
.prepare('SELECT id, label FROM communities WHERE label = ? LIMIT 1')
|
|
189
|
+
.get(name);
|
|
190
|
+
if (!comm)
|
|
191
|
+
return null;
|
|
192
|
+
const members = db
|
|
193
|
+
.prepare('SELECT id, name, label, file_path, start_line, end_line, community_id FROM nodes WHERE community_id = ? LIMIT 200')
|
|
194
|
+
.all(comm.id);
|
|
195
|
+
return { id: comm.id, label: comm.label, members: members.map(rowToApiNode) };
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
const msg = String(err);
|
|
199
|
+
if (!msg.includes('no such table'))
|
|
200
|
+
throw err;
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
export function queryProcessesList(db) {
|
|
205
|
+
const rows = db
|
|
206
|
+
.prepare(`SELECT id, name, file_path FROM nodes WHERE label = 'Process' LIMIT 200`)
|
|
207
|
+
.all();
|
|
208
|
+
return rows.map((r) => ({ id: r.id, name: r.name, filePath: r.file_path }));
|
|
209
|
+
}
|
|
210
|
+
export function queryProcess(db, name) {
|
|
211
|
+
const row = db
|
|
212
|
+
.prepare(`SELECT id, name, label, file_path, start_line, end_line, community_id FROM nodes WHERE label = 'Process' AND name = ? LIMIT 1`)
|
|
213
|
+
.get(name);
|
|
214
|
+
return row ?? null;
|
|
215
|
+
}
|
|
216
|
+
export function getServerInfo() {
|
|
217
|
+
return {
|
|
218
|
+
name: 'monograph',
|
|
219
|
+
version: PKG_VERSION,
|
|
220
|
+
nodeVersion: process.version,
|
|
221
|
+
uptimeSeconds: process.uptime(),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
// ── Streaming graph export ────────────────────────────────────────────────────
|
|
225
|
+
export async function streamGraph(db, onRecord) {
|
|
226
|
+
const nodeRows = db
|
|
227
|
+
.prepare('SELECT id, name, label, file_path, start_line, end_line, community_id FROM nodes')
|
|
228
|
+
.all();
|
|
229
|
+
for (const row of nodeRows) {
|
|
230
|
+
onRecord({ type: 'node', ...rowToApiNode(row) });
|
|
231
|
+
}
|
|
232
|
+
const edgeRows = db
|
|
233
|
+
.prepare('SELECT source_id, target_id, relation, confidence_score FROM edges')
|
|
234
|
+
.all();
|
|
235
|
+
for (const row of edgeRows) {
|
|
236
|
+
onRecord({
|
|
237
|
+
type: 'edge',
|
|
238
|
+
sourceId: row.source_id,
|
|
239
|
+
targetId: row.target_id,
|
|
240
|
+
relation: row.relation,
|
|
241
|
+
confidenceScore: row.confidence_score,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=api-queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-queries.js","sourceRoot":"","sources":["../../../src/web/api-queries.ts"],"names":[],"mappings":"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"}
|
package/dist/src/web/api.d.ts
CHANGED
|
@@ -1,91 +1,6 @@
|
|
|
1
1
|
import type Database from 'better-sqlite3';
|
|
2
2
|
import type { Application } from 'express';
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
id: string;
|
|
6
|
-
name: string;
|
|
7
|
-
label: string;
|
|
8
|
-
filePath: string | null;
|
|
9
|
-
startLine: number | null;
|
|
10
|
-
endLine: number | null;
|
|
11
|
-
communityId: number | null;
|
|
12
|
-
}
|
|
13
|
-
export interface ApiEdge {
|
|
14
|
-
sourceId: string;
|
|
15
|
-
targetId: string;
|
|
16
|
-
relation: string;
|
|
17
|
-
confidenceScore: number;
|
|
18
|
-
}
|
|
19
|
-
export interface GraphData {
|
|
20
|
-
nodes: ApiNode[];
|
|
21
|
-
edges: ApiEdge[];
|
|
22
|
-
communities: Record<string, string[]>;
|
|
23
|
-
totalNodeCount: number;
|
|
24
|
-
totalEdgeCount: number;
|
|
25
|
-
truncated: boolean;
|
|
26
|
-
}
|
|
27
|
-
export interface NodeDetail {
|
|
28
|
-
node: ApiNode | null;
|
|
29
|
-
callers: ApiNode[];
|
|
30
|
-
callees: ApiNode[];
|
|
31
|
-
}
|
|
32
|
-
export interface StatsData {
|
|
33
|
-
nodeCount: number;
|
|
34
|
-
edgeCount: number;
|
|
35
|
-
communityCount: number;
|
|
36
|
-
buildAt: string | null;
|
|
37
|
-
/** Source-selection domain the index was built with; null for indexes built before it was recorded. */
|
|
38
|
-
scope: IndexScope | null;
|
|
39
|
-
}
|
|
40
|
-
export declare function rowToApiNode(row: Record<string, unknown>): ApiNode;
|
|
41
|
-
export declare function queryGraphData(db: Database.Database): GraphData;
|
|
42
|
-
export declare function queryNode(db: Database.Database, id: string): NodeDetail;
|
|
43
|
-
export declare function querySearch(db: Database.Database, q: string): ApiNode[];
|
|
44
|
-
export declare function queryStats(db: Database.Database): StatsData;
|
|
45
|
-
export interface GrepResult {
|
|
46
|
-
id: string;
|
|
47
|
-
name: string;
|
|
48
|
-
label: string;
|
|
49
|
-
filePath: string | null;
|
|
50
|
-
startLine: number | null;
|
|
51
|
-
}
|
|
52
|
-
export declare function queryGrep(db: Database.Database, pattern: string, caseSensitive: boolean): GrepResult[];
|
|
53
|
-
export interface FileLine {
|
|
54
|
-
number: number;
|
|
55
|
-
content: string;
|
|
56
|
-
}
|
|
57
|
-
export interface FileContent {
|
|
58
|
-
path: string;
|
|
59
|
-
totalLines: number;
|
|
60
|
-
lines: FileLine[];
|
|
61
|
-
}
|
|
62
|
-
export declare function readFileContent(filePath: string, startLine?: number, endLine?: number): FileContent;
|
|
63
|
-
export interface ClusterSummary {
|
|
64
|
-
id: number;
|
|
65
|
-
label: string | null;
|
|
66
|
-
memberCount: number;
|
|
67
|
-
}
|
|
68
|
-
export interface ClusterDetail {
|
|
69
|
-
id: number;
|
|
70
|
-
label: string | null;
|
|
71
|
-
members: unknown[];
|
|
72
|
-
}
|
|
73
|
-
export declare function queryClusters(db: Database.Database): ClusterSummary[];
|
|
74
|
-
export declare function queryCluster(db: Database.Database, name: string): ClusterDetail | null;
|
|
75
|
-
export interface ProcessSummary {
|
|
76
|
-
id: string;
|
|
77
|
-
name: string;
|
|
78
|
-
filePath: string | null;
|
|
79
|
-
}
|
|
80
|
-
export declare function queryProcessesList(db: Database.Database): ProcessSummary[];
|
|
81
|
-
export declare function queryProcess(db: Database.Database, name: string): Record<string, unknown> | null;
|
|
82
|
-
export interface ServerInfo {
|
|
83
|
-
name: string;
|
|
84
|
-
version: string;
|
|
85
|
-
nodeVersion: string;
|
|
86
|
-
uptimeSeconds: number;
|
|
87
|
-
}
|
|
88
|
-
export declare function getServerInfo(): ServerInfo;
|
|
89
|
-
export declare function streamGraph(db: Database.Database, onRecord: (record: unknown) => void): Promise<void>;
|
|
3
|
+
export type { ApiEdge, ApiNode, ClusterDetail, ClusterSummary, FileContent, FileLine, GraphData, GrepResult, NodeDetail, ProcessSummary, ServerInfo, StatsData, } from './api-queries.js';
|
|
4
|
+
export { getServerInfo, queryCluster, queryClusters, queryGraphData, queryGrep, queryNode, queryProcess, queryProcessesList, querySearch, queryStats, readFileContent, rowToApiNode, streamGraph, } from './api-queries.js';
|
|
90
5
|
export declare function setupApiRoutes(app: Application, db: Database.Database): void;
|
|
91
6
|
//# sourceMappingURL=api.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/web/api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/web/api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAoB3C,YAAY,EACV,OAAO,EACP,OAAO,EACP,aAAa,EACb,cAAc,EACd,WAAW,EACX,QAAQ,EACR,SAAS,EACT,UAAU,EACV,UAAU,EACV,cAAc,EACd,UAAU,EACV,SAAS,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,EACb,YAAY,EACZ,aAAa,EACb,cAAc,EACd,SAAS,EACT,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,eAAe,EACf,YAAY,EACZ,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAI1B,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CA2R5E"}
|
package/dist/src/web/api.js
CHANGED
|
@@ -1,248 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { readIndexScope } from '../pipeline/index-scope.js';
|
|
5
|
-
import { ftsSearch } from '../storage/fts-store.js';
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { getServerInfo, queryCluster, queryClusters, queryGraphData, queryGrep, queryNode, queryProcess, queryProcessesList, querySearch, queryStats, readFileContent, streamGraph, } from './api-queries.js';
|
|
6
3
|
import { globalJobRegistry } from './async-jobs.js';
|
|
7
|
-
|
|
8
|
-
const PKG_VERSION = (() => {
|
|
9
|
-
try {
|
|
10
|
-
const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8'));
|
|
11
|
-
return pkg.version ?? '0.0.0';
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return '0.0.0';
|
|
15
|
-
}
|
|
16
|
-
})();
|
|
17
|
-
// ── Query helpers (testable in isolation) ─────────────────────────────────────
|
|
18
|
-
export function rowToApiNode(row) {
|
|
19
|
-
return {
|
|
20
|
-
id: row.id,
|
|
21
|
-
name: row.name,
|
|
22
|
-
label: row.label,
|
|
23
|
-
filePath: row.file_path ?? null,
|
|
24
|
-
startLine: row.start_line ?? null,
|
|
25
|
-
endLine: row.end_line ?? null,
|
|
26
|
-
communityId: row.community_id ?? null,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
export function queryGraphData(db) {
|
|
30
|
-
const totalNodeCount = db.prepare('SELECT COUNT(*) as c FROM nodes').get().c;
|
|
31
|
-
const totalEdgeCount = db.prepare('SELECT COUNT(*) as c FROM edges').get().c;
|
|
32
|
-
// Select the most connected nodes for a representative visualization
|
|
33
|
-
const nodeRows = db
|
|
34
|
-
.prepare(`SELECT n.id, n.name, n.label, n.file_path, n.start_line, n.end_line, n.community_id
|
|
35
|
-
FROM nodes n
|
|
36
|
-
LEFT JOIN (
|
|
37
|
-
SELECT node_id, SUM(cnt) AS deg FROM (
|
|
38
|
-
SELECT source_id AS node_id, COUNT(*) AS cnt FROM edges GROUP BY source_id
|
|
39
|
-
UNION ALL
|
|
40
|
-
SELECT target_id AS node_id, COUNT(*) AS cnt FROM edges GROUP BY target_id
|
|
41
|
-
) GROUP BY node_id
|
|
42
|
-
) d ON d.node_id = n.id
|
|
43
|
-
ORDER BY d.deg DESC NULLS LAST
|
|
44
|
-
LIMIT 2000`)
|
|
45
|
-
.all();
|
|
46
|
-
const nodes = nodeRows.map(rowToApiNode);
|
|
47
|
-
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
48
|
-
const edges = [];
|
|
49
|
-
if (nodeIds.size > 0) {
|
|
50
|
-
// Use a temp table so SQLite filters edges instead of scanning full table in JS
|
|
51
|
-
db.exec('CREATE TEMP TABLE IF NOT EXISTS _vis_nodes (id TEXT PRIMARY KEY)');
|
|
52
|
-
db.exec('DELETE FROM _vis_nodes');
|
|
53
|
-
const insertVis = db.prepare('INSERT OR IGNORE INTO _vis_nodes (id) VALUES (?)');
|
|
54
|
-
const insertAll = db.transaction((ids) => {
|
|
55
|
-
for (const id of ids)
|
|
56
|
-
insertVis.run(id);
|
|
57
|
-
});
|
|
58
|
-
insertAll([...nodeIds]);
|
|
59
|
-
const edgeRows = db
|
|
60
|
-
.prepare(`SELECT e.source_id, e.target_id, e.relation, e.confidence_score FROM edges e
|
|
61
|
-
JOIN _vis_nodes s ON e.source_id = s.id
|
|
62
|
-
JOIN _vis_nodes t ON e.target_id = t.id
|
|
63
|
-
LIMIT 10000`)
|
|
64
|
-
.all();
|
|
65
|
-
for (const r of edgeRows) {
|
|
66
|
-
edges.push({
|
|
67
|
-
sourceId: r.source_id,
|
|
68
|
-
targetId: r.target_id,
|
|
69
|
-
relation: r.relation,
|
|
70
|
-
confidenceScore: r.confidence_score,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const communities = {};
|
|
75
|
-
for (const node of nodes) {
|
|
76
|
-
if (node.communityId != null) {
|
|
77
|
-
const key = String(node.communityId);
|
|
78
|
-
if (!communities[key])
|
|
79
|
-
communities[key] = [];
|
|
80
|
-
communities[key].push(node.id);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
nodes,
|
|
85
|
-
edges,
|
|
86
|
-
communities,
|
|
87
|
-
totalNodeCount,
|
|
88
|
-
totalEdgeCount,
|
|
89
|
-
truncated: totalNodeCount > nodes.length,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
export function queryNode(db, id) {
|
|
93
|
-
const nodeRow = db
|
|
94
|
-
.prepare('SELECT id, name, label, file_path, start_line, end_line, community_id FROM nodes WHERE id = ?')
|
|
95
|
-
.get(id);
|
|
96
|
-
if (!nodeRow)
|
|
97
|
-
return { node: null, callers: [], callees: [] };
|
|
98
|
-
const node = rowToApiNode(nodeRow);
|
|
99
|
-
const callerRows = db
|
|
100
|
-
.prepare(`SELECT n.id, n.name, n.label, n.file_path, n.start_line, n.end_line, n.community_id
|
|
101
|
-
FROM nodes n JOIN edges e ON n.id = e.source_id
|
|
102
|
-
WHERE e.target_id = ? AND e.relation = 'CALLS' LIMIT 20`)
|
|
103
|
-
.all(id);
|
|
104
|
-
const calleeRows = db
|
|
105
|
-
.prepare(`SELECT n.id, n.name, n.label, n.file_path, n.start_line, n.end_line, n.community_id
|
|
106
|
-
FROM nodes n JOIN edges e ON n.id = e.target_id
|
|
107
|
-
WHERE e.source_id = ? AND e.relation = 'CALLS' LIMIT 20`)
|
|
108
|
-
.all(id);
|
|
109
|
-
return {
|
|
110
|
-
node,
|
|
111
|
-
callers: callerRows.map(rowToApiNode),
|
|
112
|
-
callees: calleeRows.map(rowToApiNode),
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
export function querySearch(db, q) {
|
|
116
|
-
const results = ftsSearch(db, q, 10);
|
|
117
|
-
return results.map((r) => ({
|
|
118
|
-
id: r.id,
|
|
119
|
-
name: r.name,
|
|
120
|
-
label: r.label,
|
|
121
|
-
filePath: r.filePath,
|
|
122
|
-
startLine: r.startLine ?? null,
|
|
123
|
-
endLine: r.endLine ?? null,
|
|
124
|
-
communityId: null,
|
|
125
|
-
}));
|
|
126
|
-
}
|
|
127
|
-
export function queryStats(db) {
|
|
128
|
-
const nodeCount = db.prepare('SELECT COUNT(*) as c FROM nodes').get().c;
|
|
129
|
-
const edgeCount = db.prepare('SELECT COUNT(*) as c FROM edges').get().c;
|
|
130
|
-
const communityCount = db
|
|
131
|
-
.prepare('SELECT COUNT(DISTINCT community_id) as c FROM nodes WHERE community_id IS NOT NULL')
|
|
132
|
-
.get().c;
|
|
133
|
-
const metaRow = db.prepare("SELECT value FROM index_meta WHERE key = 'indexed_at'").get();
|
|
134
|
-
return {
|
|
135
|
-
nodeCount,
|
|
136
|
-
edgeCount,
|
|
137
|
-
communityCount,
|
|
138
|
-
buildAt: metaRow?.value ?? null,
|
|
139
|
-
scope: readIndexScope(db),
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
export function queryGrep(db, pattern, caseSensitive) {
|
|
143
|
-
const sql = caseSensitive
|
|
144
|
-
? `SELECT id, name, label, file_path, start_line FROM nodes WHERE name GLOB ? LIMIT 100`
|
|
145
|
-
: `SELECT id, name, label, file_path, start_line FROM nodes WHERE name LIKE ? LIMIT 100`;
|
|
146
|
-
const param = caseSensitive ? `*${pattern}*` : `%${pattern}%`;
|
|
147
|
-
const rows = db.prepare(sql).all(param);
|
|
148
|
-
return rows.map((r) => ({
|
|
149
|
-
id: r.id,
|
|
150
|
-
name: r.name,
|
|
151
|
-
label: r.label,
|
|
152
|
-
filePath: r.file_path ?? null,
|
|
153
|
-
startLine: r.start_line ?? null,
|
|
154
|
-
}));
|
|
155
|
-
}
|
|
156
|
-
export function readFileContent(filePath, startLine, endLine) {
|
|
157
|
-
const raw = readFileSync(filePath, 'utf8');
|
|
158
|
-
const allLines = raw.split('\n');
|
|
159
|
-
// Remove trailing empty line from split
|
|
160
|
-
if (allLines.length > 0 && allLines[allLines.length - 1] === '')
|
|
161
|
-
allLines.pop();
|
|
162
|
-
const start = startLine ?? 1;
|
|
163
|
-
const end = endLine ?? allLines.length;
|
|
164
|
-
const lines = [];
|
|
165
|
-
for (let i = start - 1; i < end && i < allLines.length; i++) {
|
|
166
|
-
lines.push({ number: i + 1, content: allLines[i] });
|
|
167
|
-
}
|
|
168
|
-
return { path: filePath, totalLines: allLines.length, lines };
|
|
169
|
-
}
|
|
170
|
-
export function queryClusters(db) {
|
|
171
|
-
try {
|
|
172
|
-
const rows = db
|
|
173
|
-
.prepare(`SELECT c.id, c.label, COUNT(n.id) as memberCount
|
|
174
|
-
FROM communities c LEFT JOIN nodes n ON n.community_id = c.id
|
|
175
|
-
GROUP BY c.id ORDER BY memberCount DESC`)
|
|
176
|
-
.all();
|
|
177
|
-
return rows;
|
|
178
|
-
}
|
|
179
|
-
catch {
|
|
180
|
-
const rows = db
|
|
181
|
-
.prepare(`SELECT community_id as id, COUNT(*) as memberCount FROM nodes WHERE community_id IS NOT NULL GROUP BY community_id ORDER BY memberCount DESC`)
|
|
182
|
-
.all();
|
|
183
|
-
return rows.map((r) => ({ id: r.id, label: null, memberCount: r.memberCount }));
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
export function queryCluster(db, name) {
|
|
187
|
-
try {
|
|
188
|
-
const comm = db
|
|
189
|
-
.prepare('SELECT id, label FROM communities WHERE label = ? LIMIT 1')
|
|
190
|
-
.get(name);
|
|
191
|
-
if (!comm)
|
|
192
|
-
return null;
|
|
193
|
-
const members = db
|
|
194
|
-
.prepare('SELECT id, name, label, file_path, start_line, end_line, community_id FROM nodes WHERE community_id = ? LIMIT 200')
|
|
195
|
-
.all(comm.id);
|
|
196
|
-
return { id: comm.id, label: comm.label, members: members.map(rowToApiNode) };
|
|
197
|
-
}
|
|
198
|
-
catch (err) {
|
|
199
|
-
const msg = String(err);
|
|
200
|
-
if (!msg.includes('no such table'))
|
|
201
|
-
throw err;
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
export function queryProcessesList(db) {
|
|
206
|
-
const rows = db
|
|
207
|
-
.prepare(`SELECT id, name, file_path FROM nodes WHERE label = 'Process' LIMIT 200`)
|
|
208
|
-
.all();
|
|
209
|
-
return rows.map((r) => ({ id: r.id, name: r.name, filePath: r.file_path }));
|
|
210
|
-
}
|
|
211
|
-
export function queryProcess(db, name) {
|
|
212
|
-
const row = db
|
|
213
|
-
.prepare(`SELECT id, name, label, file_path, start_line, end_line, community_id FROM nodes WHERE label = 'Process' AND name = ? LIMIT 1`)
|
|
214
|
-
.get(name);
|
|
215
|
-
return row ?? null;
|
|
216
|
-
}
|
|
217
|
-
export function getServerInfo() {
|
|
218
|
-
return {
|
|
219
|
-
name: 'monograph',
|
|
220
|
-
version: PKG_VERSION,
|
|
221
|
-
nodeVersion: process.version,
|
|
222
|
-
uptimeSeconds: process.uptime(),
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
// ── Streaming graph export ────────────────────────────────────────────────────
|
|
226
|
-
export async function streamGraph(db, onRecord) {
|
|
227
|
-
const nodeRows = db
|
|
228
|
-
.prepare('SELECT id, name, label, file_path, start_line, end_line, community_id FROM nodes')
|
|
229
|
-
.all();
|
|
230
|
-
for (const row of nodeRows) {
|
|
231
|
-
onRecord({ type: 'node', ...rowToApiNode(row) });
|
|
232
|
-
}
|
|
233
|
-
const edgeRows = db
|
|
234
|
-
.prepare('SELECT source_id, target_id, relation, confidence_score FROM edges')
|
|
235
|
-
.all();
|
|
236
|
-
for (const row of edgeRows) {
|
|
237
|
-
onRecord({
|
|
238
|
-
type: 'edge',
|
|
239
|
-
sourceId: row.source_id,
|
|
240
|
-
targetId: row.target_id,
|
|
241
|
-
relation: row.relation,
|
|
242
|
-
confidenceScore: row.confidence_score,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
}
|
|
4
|
+
export { getServerInfo, queryCluster, queryClusters, queryGraphData, queryGrep, queryNode, queryProcess, queryProcessesList, querySearch, queryStats, readFileContent, rowToApiNode, streamGraph, } from './api-queries.js';
|
|
246
5
|
// ── Route setup ───────────────────────────────────────────────────────────────
|
|
247
6
|
export function setupApiRoutes(app, db) {
|
|
248
7
|
app.get('/api/graph', (req, res) => {
|