@zuvia-software-solutions/code-mapper 2.3.3 → 2.3.5
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.
|
@@ -739,7 +739,7 @@ function isTypeScriptOrJavaScript(filePath) {
|
|
|
739
739
|
*
|
|
740
740
|
* Call key format: "sourceId\0calledName\0callLine" — unique per call site.
|
|
741
741
|
*/
|
|
742
|
-
async function batchResolveTsgo(tsgoService, extractedCalls, ctx, graph, repoPath) {
|
|
742
|
+
async function batchResolveTsgo(tsgoService, extractedCalls, ctx, graph, repoPath, onProgress) {
|
|
743
743
|
const results = new Map();
|
|
744
744
|
// Collect eligible calls (TS/JS files with line+column info)
|
|
745
745
|
const eligible = [];
|
|
@@ -764,13 +764,46 @@ async function batchResolveTsgo(tsgoService, extractedCalls, ctx, graph, repoPat
|
|
|
764
764
|
}
|
|
765
765
|
list.push(call);
|
|
766
766
|
}
|
|
767
|
+
// Pre-filter: skip free-form calls ONLY when the function name is unambiguous
|
|
768
|
+
// in the symbol table. Heuristic resolves unique names perfectly.
|
|
769
|
+
// Ambiguous names (multiple symbols with same name) need tsgo for disambiguation.
|
|
770
|
+
const tsgoEligible = [];
|
|
771
|
+
let skippedHeuristic = 0;
|
|
772
|
+
for (const call of eligible) {
|
|
773
|
+
if (call.callForm === 'free' || call.callForm === undefined) {
|
|
774
|
+
const resolved = ctx.resolve(call.calledName, call.filePath);
|
|
775
|
+
// Unique match — heuristic handles this at high confidence
|
|
776
|
+
if (resolved && resolved.candidates.length === 1) {
|
|
777
|
+
skippedHeuristic++;
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
tsgoEligible.push(call);
|
|
782
|
+
}
|
|
783
|
+
// Regroup filtered calls by file
|
|
784
|
+
const tsgoByFile = new Map();
|
|
785
|
+
for (const call of tsgoEligible) {
|
|
786
|
+
let list = tsgoByFile.get(call.filePath);
|
|
787
|
+
if (!list) {
|
|
788
|
+
list = [];
|
|
789
|
+
tsgoByFile.set(call.filePath, list);
|
|
790
|
+
}
|
|
791
|
+
list.push(call);
|
|
792
|
+
}
|
|
767
793
|
let resolved = 0;
|
|
768
794
|
let failed = 0;
|
|
769
795
|
const t0 = Date.now();
|
|
770
|
-
console.error(`Code Mapper: tsgo resolving ${
|
|
771
|
-
|
|
796
|
+
console.error(`Code Mapper: tsgo resolving ${tsgoEligible.length} calls across ${tsgoByFile.size} files (skipped ${skippedHeuristic} heuristic-resolvable)...`);
|
|
797
|
+
let tsgoFilesProcessed = 0;
|
|
798
|
+
const tsgoTotalFiles = tsgoByFile.size;
|
|
799
|
+
for (const [filePath, calls] of tsgoByFile) {
|
|
800
|
+
tsgoFilesProcessed++;
|
|
801
|
+
if (tsgoFilesProcessed % 25 === 0) {
|
|
802
|
+
onProgress?.(tsgoFilesProcessed, tsgoTotalFiles);
|
|
803
|
+
await yieldToEventLoop();
|
|
804
|
+
}
|
|
772
805
|
const absFilePath = path.resolve(repoPath, filePath);
|
|
773
|
-
//
|
|
806
|
+
// Sequential LSP requests — tsgo processes over stdio, concurrent floods cause hangs
|
|
774
807
|
for (const call of calls) {
|
|
775
808
|
try {
|
|
776
809
|
const def = await tsgoService.resolveDefinition(absFilePath, call.callLine - 1, call.callColumn);
|
|
@@ -806,10 +839,7 @@ async function batchResolveTsgo(tsgoService, extractedCalls, ctx, graph, repoPat
|
|
|
806
839
|
}
|
|
807
840
|
}
|
|
808
841
|
if (bestMatch) {
|
|
809
|
-
// Drop self-referencing tsgo edges
|
|
810
|
-
// on parameters (req.params, res.json) that tsgo resolves back to
|
|
811
|
-
// the enclosing function's definition. Legitimate recursion is captured
|
|
812
|
-
// by the heuristic path (free-form calls to the function's own name).
|
|
842
|
+
// Drop self-referencing tsgo edges
|
|
813
843
|
if (bestMatch.nodeId === call.sourceId) {
|
|
814
844
|
failed++;
|
|
815
845
|
continue;
|
|
@@ -871,13 +901,13 @@ export const processCallsFromExtracted = async (graph, extractedCalls, ctx, onPr
|
|
|
871
901
|
// Batch pre-resolve via tsgo LSP (highest confidence, TS/JS only)
|
|
872
902
|
let tsgoResolved;
|
|
873
903
|
if (tsgoService?.isReady() && repoPath) {
|
|
874
|
-
tsgoResolved = await batchResolveTsgo(tsgoService, extractedCalls, ctx, graph, repoPath);
|
|
904
|
+
tsgoResolved = await batchResolveTsgo(tsgoService, extractedCalls, ctx, graph, repoPath, onProgress);
|
|
875
905
|
}
|
|
876
906
|
const totalFiles = byFile.size;
|
|
877
907
|
let filesProcessed = 0;
|
|
878
908
|
for (const [filePath, calls] of byFile) {
|
|
879
909
|
filesProcessed++;
|
|
880
|
-
if (filesProcessed %
|
|
910
|
+
if (filesProcessed % 25 === 0) {
|
|
881
911
|
onProgress?.(filesProcessed, totalFiles);
|
|
882
912
|
await yieldToEventLoop();
|
|
883
913
|
}
|
|
@@ -103,7 +103,7 @@ export const runPipelineFromRepo = async (repoPath, onProgress, opts) => {
|
|
|
103
103
|
if (totalParseable === 0) {
|
|
104
104
|
onProgress({
|
|
105
105
|
phase: 'parsing',
|
|
106
|
-
percent:
|
|
106
|
+
percent: 70,
|
|
107
107
|
message: 'No parseable files found — skipping parsing phase',
|
|
108
108
|
stats: { filesProcessed: 0, totalFiles: 0, nodesCreated: graph.nodeCount },
|
|
109
109
|
});
|
|
@@ -186,7 +186,7 @@ export const runPipelineFromRepo = async (repoPath, onProgress, opts) => {
|
|
|
186
186
|
const parseStart = Date.now();
|
|
187
187
|
const chunkWorkerData = await processParsing(graph, chunkFiles, symbolTable, astCache, (current, _total, filePath) => {
|
|
188
188
|
const globalCurrent = filesParsedSoFar + current;
|
|
189
|
-
const parsingProgress = 20 + ((globalCurrent / totalParseable) *
|
|
189
|
+
const parsingProgress = 20 + ((globalCurrent / totalParseable) * 50);
|
|
190
190
|
onProgress({
|
|
191
191
|
phase: 'parsing',
|
|
192
192
|
percent: Math.round(parsingProgress),
|
|
@@ -197,7 +197,7 @@ export const runPipelineFromRepo = async (repoPath, onProgress, opts) => {
|
|
|
197
197
|
}, workerPool);
|
|
198
198
|
const parseMs = Date.now() - parseStart;
|
|
199
199
|
verbose(`[parse] chunk ${chunkIdx + 1}/${numChunks}: parsed ${parseMs}ms (${memoryGuard.summary()})`);
|
|
200
|
-
const chunkBasePercent = 20 + ((filesParsedSoFar / totalParseable) *
|
|
200
|
+
const chunkBasePercent = 20 + ((filesParsedSoFar / totalParseable) * 50);
|
|
201
201
|
if (chunkWorkerData) {
|
|
202
202
|
// Resolve imports per-chunk (file-level, doesn't need full symbol table)
|
|
203
203
|
await processImportsFromExtracted(graph, allPathObjects, chunkWorkerData.imports, ctx, (current, total) => {
|
|
@@ -274,10 +274,11 @@ export const runPipelineFromRepo = async (repoPath, onProgress, opts) => {
|
|
|
274
274
|
}
|
|
275
275
|
let tsgoWasUsed = false;
|
|
276
276
|
// Phase B: Resolve ALL deferred calls now that every symbol is registered
|
|
277
|
+
// Progress range: 70-82% (advancing, not fixed)
|
|
277
278
|
if (allExtractedCalls.length > 0) {
|
|
278
279
|
onProgress({
|
|
279
|
-
phase: '
|
|
280
|
-
percent:
|
|
280
|
+
phase: 'calls',
|
|
281
|
+
percent: 70,
|
|
281
282
|
message: `Resolving ${allExtractedCalls.length} calls across all files...`,
|
|
282
283
|
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
283
284
|
});
|
|
@@ -297,9 +298,10 @@ export const runPipelineFromRepo = async (repoPath, onProgress, opts) => {
|
|
|
297
298
|
}
|
|
298
299
|
try {
|
|
299
300
|
await processCallsFromExtracted(graph, allExtractedCalls, ctx, (current, total) => {
|
|
301
|
+
const callPercent = 70 + Math.round((current / Math.max(total, 1)) * 12);
|
|
300
302
|
onProgress({
|
|
301
|
-
phase: '
|
|
302
|
-
percent:
|
|
303
|
+
phase: 'calls',
|
|
304
|
+
percent: callPercent,
|
|
303
305
|
message: `Resolving calls: ${current}/${total} files...`,
|
|
304
306
|
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
305
307
|
});
|
|
@@ -337,7 +339,7 @@ export const runPipelineFromRepo = async (repoPath, onProgress, opts) => {
|
|
|
337
339
|
// Phase 4.5b: Method Resolution Order
|
|
338
340
|
onProgress({
|
|
339
341
|
phase: 'parsing',
|
|
340
|
-
percent:
|
|
342
|
+
percent: 82,
|
|
341
343
|
message: 'Computing method resolution order...',
|
|
342
344
|
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
343
345
|
});
|
|
@@ -46,7 +46,8 @@ export const createWorkerPool = (workerUrl, poolSize) => {
|
|
|
46
46
|
if (!fs.existsSync(workerPath)) {
|
|
47
47
|
throw new Error(`Worker script not found: ${workerPath}`);
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
// Use 75% of cores — leaves headroom for main thread, OS, and other processes
|
|
50
|
+
const size = poolSize ?? Math.max(1, Math.floor(os.cpus().length * 0.75));
|
|
50
51
|
const workers = [];
|
|
51
52
|
for (let i = 0; i < size; i++) {
|
|
52
53
|
workers.push(new Worker(workerUrl));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zuvia-software-solutions/code-mapper",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.5",
|
|
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",
|