@zuvia-software-solutions/code-mapper 2.0.0 → 2.0.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/dist/core/incremental/refresh.js +78 -39
- package/dist/core/ingestion/parsing-processor.js +10 -5
- package/dist/core/ingestion/pipeline.js +5 -2
- package/dist/core/ingestion/workers/parse-worker.js +17 -8
- package/dist/core/semantic/tsgo-service.d.ts +2 -0
- package/dist/core/semantic/tsgo-service.js +4 -0
- package/package.json +1 -1
|
@@ -259,52 +259,91 @@ export async function refreshFiles(db, repoPath, dirtyFiles) {
|
|
|
259
259
|
catch (err) {
|
|
260
260
|
console.error(`Code Mapper: refresh tsgo failed: ${err instanceof Error ? err.message : err}`);
|
|
261
261
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
// Wrap Phase 4 + 5 in a transaction for performance
|
|
262
|
+
// Phase 4: Resolve call edges from dirty files
|
|
263
|
+
// Always runs — tsgo provides 0.99 confidence, heuristic fallback provides 0.5-0.95
|
|
264
|
+
if (callSites.length > 0) {
|
|
266
265
|
db.exec('BEGIN');
|
|
267
266
|
try {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
267
|
+
const { findNodesByName } = await import('../db/adapter.js');
|
|
268
|
+
// Notify tsgo about changed files if available
|
|
269
|
+
if (tsgoReady) {
|
|
270
|
+
for (const entry of filesToProcess) {
|
|
271
|
+
const absPath = path.resolve(repoPath, entry.relativePath);
|
|
272
|
+
if (isTypeScriptOrJavaScript(entry.relativePath)) {
|
|
273
|
+
await tsgoService.notifyFileChanged(absPath);
|
|
274
|
+
}
|
|
273
275
|
}
|
|
274
276
|
}
|
|
275
|
-
// Phase 4: Resolve call edges from dirty files
|
|
276
277
|
console.error(`Code Mapper: refresh Phase 4 — ${callSites.length} call sites to resolve`);
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
id: edgeId,
|
|
298
|
-
sourceId: sourceNode.id,
|
|
299
|
-
targetId: targetNode.id,
|
|
300
|
-
type: 'CALLS',
|
|
301
|
-
confidence: 0.99,
|
|
302
|
-
reason: 'tsgo-lsp',
|
|
303
|
-
callLine: callSite.line,
|
|
304
|
-
});
|
|
305
|
-
edgesInserted++;
|
|
278
|
+
for (const callSite of callSites) {
|
|
279
|
+
const sourceNode = findEnclosingNode(db, callSite.filePath, callSite.line);
|
|
280
|
+
if (!sourceNode)
|
|
281
|
+
continue;
|
|
282
|
+
let targetNode;
|
|
283
|
+
let confidence = 0.5;
|
|
284
|
+
let reason = 'global';
|
|
285
|
+
// Try tsgo first for TS/JS files
|
|
286
|
+
if (tsgoReady && isTypeScriptOrJavaScript(callSite.filePath)) {
|
|
287
|
+
try {
|
|
288
|
+
const def = await tsgoService.resolveDefinition(callSite.absPath, callSite.line, callSite.character);
|
|
289
|
+
if (def) {
|
|
290
|
+
targetNode = findNodeByFileAndLine(db, def.filePath, def.line);
|
|
291
|
+
if (targetNode) {
|
|
292
|
+
confidence = 0.99;
|
|
293
|
+
reason = 'tsgo-lsp';
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch { }
|
|
306
298
|
}
|
|
299
|
+
// Heuristic fallback: name-based lookup in DB
|
|
300
|
+
if (!targetNode) {
|
|
301
|
+
const candidates = findNodesByName(db, callSite.name, undefined, 5);
|
|
302
|
+
const sameFile = candidates.find(c => c.filePath === callSite.filePath);
|
|
303
|
+
if (sameFile) {
|
|
304
|
+
targetNode = sameFile;
|
|
305
|
+
confidence = 0.95;
|
|
306
|
+
reason = 'same-file';
|
|
307
|
+
}
|
|
308
|
+
else if (candidates.length === 1) {
|
|
309
|
+
targetNode = candidates[0];
|
|
310
|
+
confidence = 0.9;
|
|
311
|
+
reason = 'import-resolved';
|
|
312
|
+
}
|
|
313
|
+
else if (candidates.length > 0) {
|
|
314
|
+
targetNode = candidates[0];
|
|
315
|
+
confidence = 0.5;
|
|
316
|
+
reason = 'global';
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (!targetNode)
|
|
320
|
+
continue;
|
|
321
|
+
if (sourceNode.id === targetNode.id)
|
|
322
|
+
continue;
|
|
323
|
+
const edgeId = toEdgeId(`${sourceNode.id}_CALLS_${targetNode.id}`);
|
|
324
|
+
insertEdge(db, {
|
|
325
|
+
id: edgeId,
|
|
326
|
+
sourceId: sourceNode.id,
|
|
327
|
+
targetId: targetNode.id,
|
|
328
|
+
type: 'CALLS',
|
|
329
|
+
confidence,
|
|
330
|
+
reason,
|
|
331
|
+
callLine: callSite.line,
|
|
332
|
+
});
|
|
333
|
+
edgesInserted++;
|
|
307
334
|
}
|
|
335
|
+
db.exec('COMMIT');
|
|
336
|
+
}
|
|
337
|
+
catch (err) {
|
|
338
|
+
db.exec('ROLLBACK');
|
|
339
|
+
console.error(`Code Mapper: Phase 4 call resolution failed: ${err instanceof Error ? err.message : err}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// Phase 5: Repair cross-file edges (tsgo only — needs findReferences)
|
|
343
|
+
if (tsgoReady) {
|
|
344
|
+
const dirtyFilePaths = new Set(dirtyFiles.map(f => f.relativePath));
|
|
345
|
+
db.exec('BEGIN');
|
|
346
|
+
try {
|
|
308
347
|
// Phase 5: Repair cross-file edges
|
|
309
348
|
// For each newly inserted definition, find references from UNCHANGED files
|
|
310
349
|
// and create CALLS edges so the graph stays consistent.
|
|
@@ -59,11 +59,16 @@ const processParsingWithWorkers = async (graph, files, symbolTable, _astCache, w
|
|
|
59
59
|
...(sym.parameterTypes !== undefined ? { parameterTypes: sym.parameterTypes } : {}),
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
for (const item of result.imports)
|
|
63
|
+
allImports.push(item);
|
|
64
|
+
for (const item of result.calls)
|
|
65
|
+
allCalls.push(item);
|
|
66
|
+
for (const item of result.heritage)
|
|
67
|
+
allHeritage.push(item);
|
|
68
|
+
for (const item of result.routes)
|
|
69
|
+
allRoutes.push(item);
|
|
70
|
+
for (const item of result.constructorBindings)
|
|
71
|
+
allConstructorBindings.push(item);
|
|
67
72
|
}
|
|
68
73
|
// Merge and log skipped languages
|
|
69
74
|
const skippedLanguages = new Map();
|
|
@@ -201,9 +201,12 @@ export const runPipelineFromRepo = async (repoPath, onProgress, opts) => {
|
|
|
201
201
|
});
|
|
202
202
|
}, repoPath, importCtx);
|
|
203
203
|
// COLLECT calls for deferred resolution (don't resolve yet — callee may be in later chunk)
|
|
204
|
-
|
|
204
|
+
// Use loop instead of spread to avoid stack overflow on large codebases (100K+ calls)
|
|
205
|
+
for (const call of chunkWorkerData.calls)
|
|
206
|
+
allExtractedCalls.push(call);
|
|
205
207
|
if (chunkWorkerData.constructorBindings) {
|
|
206
|
-
|
|
208
|
+
for (const cb of chunkWorkerData.constructorBindings)
|
|
209
|
+
allConstructorBindings.push(cb);
|
|
207
210
|
}
|
|
208
211
|
// Heritage + Routes can resolve per-chunk (class-level, usually same-file)
|
|
209
212
|
await Promise.all([
|
|
@@ -1072,15 +1072,24 @@ let accumulated = {
|
|
|
1072
1072
|
imports: [], calls: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, fileCount: 0,
|
|
1073
1073
|
};
|
|
1074
1074
|
let cumulativeProcessed = 0;
|
|
1075
|
+
/** Append src arrays into target without spread (avoids stack overflow on large codebases) */
|
|
1075
1076
|
const mergeResult = (target, src) => {
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1077
|
+
for (const item of src.nodes)
|
|
1078
|
+
target.nodes.push(item);
|
|
1079
|
+
for (const item of src.relationships)
|
|
1080
|
+
target.relationships.push(item);
|
|
1081
|
+
for (const item of src.symbols)
|
|
1082
|
+
target.symbols.push(item);
|
|
1083
|
+
for (const item of src.imports)
|
|
1084
|
+
target.imports.push(item);
|
|
1085
|
+
for (const item of src.calls)
|
|
1086
|
+
target.calls.push(item);
|
|
1087
|
+
for (const item of src.heritage)
|
|
1088
|
+
target.heritage.push(item);
|
|
1089
|
+
for (const item of src.routes)
|
|
1090
|
+
target.routes.push(item);
|
|
1091
|
+
for (const item of src.constructorBindings)
|
|
1092
|
+
target.constructorBindings.push(item);
|
|
1084
1093
|
for (const [lang, count] of Object.entries(src.skippedLanguages)) {
|
|
1085
1094
|
target.skippedLanguages[lang] = (target.skippedLanguages[lang] || 0) + count;
|
|
1086
1095
|
}
|
|
@@ -39,6 +39,8 @@ export declare class TsgoService {
|
|
|
39
39
|
* @typescript/native-preview is not installed or tsgo fails to start.
|
|
40
40
|
*/
|
|
41
41
|
start(): Promise<boolean>;
|
|
42
|
+
/** Get the resolved project root (where tsconfig.json was found) */
|
|
43
|
+
getProjectRoot(): string;
|
|
42
44
|
/** Whether the server is running and ready for queries */
|
|
43
45
|
isReady(): boolean;
|
|
44
46
|
/** Resolve what a symbol at a given position points to (go-to-definition) */
|
|
@@ -59,6 +59,10 @@ export class TsgoService {
|
|
|
59
59
|
this.initPromise = this.doStart();
|
|
60
60
|
return this.initPromise;
|
|
61
61
|
}
|
|
62
|
+
/** Get the resolved project root (where tsconfig.json was found) */
|
|
63
|
+
getProjectRoot() {
|
|
64
|
+
return this.projectRoot;
|
|
65
|
+
}
|
|
62
66
|
/** Whether the server is running and ready for queries */
|
|
63
67
|
isReady() {
|
|
64
68
|
return this.ready;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zuvia-software-solutions/code-mapper",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
|
|
5
5
|
"author": "Abhigyan Patwari",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|