@zuvia-software-solutions/code-mapper 2.3.7 → 2.3.9
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/cli/analyze.js +50 -18
- package/dist/core/ingestion/call-processor.d.ts +2 -2
- package/dist/core/ingestion/call-processor.js +144 -62
- package/dist/core/ingestion/pipeline.js +3 -3
- package/dist/core/semantic/tsgo-service.js +3 -3
- package/dist/types/pipeline.d.ts +1 -0
- package/package.json +1 -1
package/dist/cli/analyze.js
CHANGED
|
@@ -136,10 +136,12 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
136
136
|
const t0Global = Date.now();
|
|
137
137
|
const cpuStart = process.cpuUsage();
|
|
138
138
|
let peakRssMB = 0;
|
|
139
|
-
// Phase timing tracker — records wall time and
|
|
139
|
+
// Phase timing tracker — records wall time, RSS, file count, and worker count per phase
|
|
140
140
|
const phaseTimes = [];
|
|
141
141
|
let currentPhaseName = 'init';
|
|
142
142
|
let currentPhaseStart = Date.now();
|
|
143
|
+
let currentPhaseFiles = 0;
|
|
144
|
+
let currentPhaseWorkers = 0;
|
|
143
145
|
const recordPhase = (nextPhase) => {
|
|
144
146
|
const now = Date.now();
|
|
145
147
|
const elapsed = now - currentPhaseStart;
|
|
@@ -148,10 +150,14 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
148
150
|
name: currentPhaseName,
|
|
149
151
|
ms: elapsed,
|
|
150
152
|
rssMB: Math.round(process.memoryUsage.rss() / (1024 * 1024)),
|
|
153
|
+
...(currentPhaseFiles > 0 ? { fileCount: currentPhaseFiles } : {}),
|
|
154
|
+
...(currentPhaseWorkers > 0 ? { workerCount: currentPhaseWorkers } : {}),
|
|
151
155
|
});
|
|
152
156
|
}
|
|
153
157
|
currentPhaseName = nextPhase;
|
|
154
158
|
currentPhaseStart = now;
|
|
159
|
+
currentPhaseFiles = 0;
|
|
160
|
+
currentPhaseWorkers = 0;
|
|
155
161
|
};
|
|
156
162
|
// Live resource stats for the progress bar
|
|
157
163
|
const cpuCount = os.cpus().length;
|
|
@@ -164,26 +170,27 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
164
170
|
const cpuPct = Math.round(((cpuDelta.user + cpuDelta.system) / 1e3) / wallMs * 100);
|
|
165
171
|
return `${rssMB}MB | CPU ${cpuPct}%`;
|
|
166
172
|
};
|
|
167
|
-
// Track elapsed time per phase
|
|
168
|
-
//
|
|
169
|
-
let
|
|
173
|
+
// Track elapsed time per BASE phase (without counts) so the timer
|
|
174
|
+
// doesn't reset every time the count updates
|
|
175
|
+
let lastBasePhase = 'Initializing...';
|
|
176
|
+
let lastFullLabel = 'Initializing...';
|
|
170
177
|
let phaseStart = Date.now();
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
|
|
178
|
+
const updateBar = (value, phaseLabel, basePhase) => {
|
|
179
|
+
const base = basePhase ?? phaseLabel;
|
|
180
|
+
if (base !== lastBasePhase) {
|
|
181
|
+
lastBasePhase = base;
|
|
175
182
|
phaseStart = Date.now();
|
|
176
183
|
}
|
|
184
|
+
lastFullLabel = phaseLabel;
|
|
177
185
|
const elapsed = Math.round((Date.now() - phaseStart) / 1000);
|
|
178
|
-
const display = elapsed >=
|
|
186
|
+
const display = elapsed >= 1 ? `${phaseLabel} (${elapsed}s)` : phaseLabel;
|
|
179
187
|
bar.update(value, { phase: display, resources: getResourceStats() });
|
|
180
188
|
};
|
|
181
189
|
// Tick elapsed seconds for phases with infrequent progress callbacks
|
|
182
|
-
// (e.g. CSV streaming, FTS indexing) — uses the same display format as updateBar
|
|
183
190
|
const elapsedTimer = setInterval(() => {
|
|
184
191
|
const elapsed = Math.round((Date.now() - phaseStart) / 1000);
|
|
185
|
-
if (elapsed >=
|
|
186
|
-
bar.update({ phase: `${
|
|
192
|
+
if (elapsed >= 1) {
|
|
193
|
+
bar.update({ phase: `${lastFullLabel} (${elapsed}s)`, resources: getResourceStats() });
|
|
187
194
|
}
|
|
188
195
|
}, 1000);
|
|
189
196
|
// Cache embeddings from existing index before rebuild
|
|
@@ -218,13 +225,29 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
218
225
|
recordPhase(progress.phase);
|
|
219
226
|
lastPipelinePhase = progress.phase;
|
|
220
227
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
228
|
+
const baseLabel = PHASE_LABELS[progress.phase] || progress.phase;
|
|
229
|
+
let phaseLabel = baseLabel;
|
|
230
|
+
if (progress.stats && progress.stats.totalFiles > 0) {
|
|
231
|
+
const current = progress.stats.filesProcessed;
|
|
232
|
+
const total = progress.stats.totalFiles;
|
|
233
|
+
// Track peak file count and worker count for the summary
|
|
234
|
+
currentPhaseFiles = Math.max(currentPhaseFiles, total);
|
|
235
|
+
if (progress.stats.workerCount)
|
|
236
|
+
currentPhaseWorkers = Math.max(currentPhaseWorkers, progress.stats.workerCount);
|
|
237
|
+
phaseLabel += ` (${current.toLocaleString()}/${total.toLocaleString()})`;
|
|
238
|
+
// Show rate (files/s) after 1s
|
|
239
|
+
const elapsedSec = (Date.now() - phaseStart) / 1000;
|
|
240
|
+
if (elapsedSec >= 1 && current > 0) {
|
|
241
|
+
const rate = Math.round(current / elapsedSec);
|
|
242
|
+
phaseLabel += ` ${rate}/s`;
|
|
243
|
+
}
|
|
244
|
+
// Show worker/process count if available
|
|
245
|
+
if (progress.stats.workerCount && progress.stats.workerCount > 1) {
|
|
246
|
+
phaseLabel += ` [${progress.stats.workerCount}p]`;
|
|
247
|
+
}
|
|
225
248
|
}
|
|
226
249
|
const scaled = Math.round(progress.percent * 0.6);
|
|
227
|
-
updateBar(scaled, phaseLabel);
|
|
250
|
+
updateBar(scaled, phaseLabel, baseLabel);
|
|
228
251
|
}, options?.tsgo === false ? { tsgo: false } : {});
|
|
229
252
|
// Phase 2: SQLite (60-85%)
|
|
230
253
|
recordPhase('sqlite');
|
|
@@ -432,7 +455,16 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
432
455
|
const pct = Math.round((phase.ms / totalMs) * 100);
|
|
433
456
|
const name = PHASE_DISPLAY_NAMES[phase.name] || phase.name;
|
|
434
457
|
const bar = pct >= 2 ? ' ' + '█'.repeat(Math.max(1, Math.round(pct / 3))) : '';
|
|
435
|
-
|
|
458
|
+
// Build extra stats: rate + workers
|
|
459
|
+
let extra = '';
|
|
460
|
+
if (phase.fileCount && phase.ms > 0) {
|
|
461
|
+
const rate = Math.round(phase.fileCount / (phase.ms / 1000));
|
|
462
|
+
extra += ` ${phase.fileCount.toLocaleString()} files (${rate}/s)`;
|
|
463
|
+
}
|
|
464
|
+
if (phase.workerCount && phase.workerCount > 1) {
|
|
465
|
+
extra += ` [${phase.workerCount}p]`;
|
|
466
|
+
}
|
|
467
|
+
console.log(` ${name.padEnd(22)} ${sec.padStart(6)}s ${String(pct).padStart(3)}% ${phase.rssMB}MB${bar}${extra}`);
|
|
436
468
|
}
|
|
437
469
|
console.log(` ${'─'.repeat(50)}`);
|
|
438
470
|
console.log(` ${'Total'.padEnd(22)} ${totalTime.padStart(6)}s 100% ${peakRssMB}MB peak`);
|
|
@@ -3,14 +3,14 @@ import type { KnowledgeGraph } from '../graph/types.js';
|
|
|
3
3
|
import type { ASTCache } from './ast-cache.js';
|
|
4
4
|
import type { ResolutionContext } from './resolution-context.js';
|
|
5
5
|
import type { ExtractedCall, ExtractedHeritage, ExtractedRoute, FileConstructorBindings } from './workers/parse-worker.js';
|
|
6
|
-
import
|
|
6
|
+
import { TsgoService } from '../semantic/tsgo-service.js';
|
|
7
7
|
export declare const processCalls: (graph: KnowledgeGraph, files: {
|
|
8
8
|
path: string;
|
|
9
9
|
content: string;
|
|
10
10
|
}[], astCache: ASTCache, ctx: ResolutionContext, onProgress?: (current: number, total: number) => void) => Promise<ExtractedHeritage[]>;
|
|
11
11
|
export declare const extractReturnTypeName: (raw: string, depth?: number) => string | undefined;
|
|
12
12
|
/** Resolve pre-extracted call sites from workers (no AST parsing needed) */
|
|
13
|
-
export declare const processCallsFromExtracted: (graph: KnowledgeGraph, extractedCalls: ExtractedCall[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void, constructorBindings?: FileConstructorBindings[], tsgoService?: TsgoService | null, repoPath?: string) => Promise<void>;
|
|
13
|
+
export declare const processCallsFromExtracted: (graph: KnowledgeGraph, extractedCalls: ExtractedCall[], ctx: ResolutionContext, onProgress?: (current: number, total: number, workerCount?: number) => void, constructorBindings?: FileConstructorBindings[], tsgoService?: TsgoService | null, repoPath?: string) => Promise<void>;
|
|
14
14
|
/** Resolve pre-extracted Laravel routes to CALLS edges from route files to controller methods */
|
|
15
15
|
export declare const processRoutesFromExtracted: (graph: KnowledgeGraph, extractedRoutes: ExtractedRoute[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void) => Promise<void>;
|
|
16
16
|
/**
|
|
@@ -10,6 +10,7 @@ import { getLanguageFromFilename, isVerboseIngestionEnabled, yieldToEventLoop, F
|
|
|
10
10
|
import { buildTypeEnv } from './type-env.js';
|
|
11
11
|
import { getTreeSitterBufferSize } from './constants.js';
|
|
12
12
|
import { callRouters } from './call-routing.js';
|
|
13
|
+
import { TsgoService } from '../semantic/tsgo-service.js';
|
|
13
14
|
import path from 'node:path';
|
|
14
15
|
/** Walk up the AST to find the enclosing function/method, or null for top-level code */
|
|
15
16
|
const findEnclosingFunction = (node, filePath, ctx) => {
|
|
@@ -777,11 +778,11 @@ async function batchResolveTsgo(tsgoService, extractedCalls, ctx, graph, repoPat
|
|
|
777
778
|
]);
|
|
778
779
|
// Pre-filter calls where tsgo won't add value:
|
|
779
780
|
// A. Free-form calls with unambiguous name — heuristic resolves perfectly
|
|
780
|
-
// B. Member calls
|
|
781
|
-
//
|
|
781
|
+
// B. Member calls on built-in receivers — tsgo always fails on these
|
|
782
|
+
// Note: member calls with known receiver types are NOT skipped — tsgo provides
|
|
783
|
+
// compiler-verified 0.99 confidence that the heuristic can't match.
|
|
782
784
|
const tsgoEligible = [];
|
|
783
785
|
let skippedUnambiguous = 0;
|
|
784
|
-
const skippedKnownType = 0;
|
|
785
786
|
let skippedBuiltin = 0;
|
|
786
787
|
for (const call of eligible) {
|
|
787
788
|
// A. Free-form, unique name match
|
|
@@ -809,80 +810,161 @@ async function batchResolveTsgo(tsgoService, extractedCalls, ctx, graph, repoPat
|
|
|
809
810
|
}
|
|
810
811
|
list.push(call);
|
|
811
812
|
}
|
|
812
|
-
let resolved = 0;
|
|
813
|
-
let failed = 0;
|
|
814
813
|
const t0 = Date.now();
|
|
815
|
-
const skippedTotal = skippedUnambiguous +
|
|
816
|
-
|
|
817
|
-
|
|
814
|
+
const skippedTotal = skippedUnambiguous + skippedBuiltin;
|
|
815
|
+
// Adaptive parallelism based on three constraints:
|
|
816
|
+
// 1. CPU: 75% of cores — parsing workers are done, leave 25% for Node.js event loop + OS
|
|
817
|
+
// 2. Memory: each tsgo loads the full project (~500MB estimate) — cap by free system memory
|
|
818
|
+
// 3. Workload: at least 50 files per process to amortize ~0.5s startup cost
|
|
819
|
+
const osModule = await import('os');
|
|
820
|
+
const cpuCount = osModule.cpus().length;
|
|
821
|
+
const freeMemGB = osModule.freemem() / (1024 * 1024 * 1024);
|
|
822
|
+
const maxByCpu = Math.max(1, Math.floor(cpuCount * 0.75));
|
|
823
|
+
const maxByMemory = Math.max(1, Math.floor(freeMemGB / 0.5));
|
|
824
|
+
const maxByWorkload = Math.max(1, Math.floor(tsgoByFile.size / 50));
|
|
825
|
+
const actualWorkers = Math.min(maxByCpu, maxByMemory, maxByWorkload);
|
|
826
|
+
if (process.env['CODE_MAPPER_VERBOSE']) {
|
|
827
|
+
console.error(`Code Mapper: tsgo resolving ${tsgoEligible.length} calls across ${tsgoByFile.size} files with ${actualWorkers} process${actualWorkers > 1 ? 'es' : ''} (skipped ${skippedTotal}: ${skippedUnambiguous} unambiguous, ${skippedBuiltin} builtin)...`);
|
|
828
|
+
}
|
|
829
|
+
// Dynamic dispatch: shared queue, each process grabs the next file when done.
|
|
830
|
+
// Naturally self-balancing — fast processes get more work, zero idle time.
|
|
831
|
+
// Sort heaviest files first so they're assigned early (avoids tail latency).
|
|
832
|
+
const fileEntries = [...tsgoByFile.entries()];
|
|
833
|
+
fileEntries.sort((a, b) => b[1].length - a[1].length);
|
|
834
|
+
// Shared progress counter and file queue (single-threaded, no mutex needed)
|
|
835
|
+
let totalFilesProcessed = 0;
|
|
836
|
+
let nextFileIdx = 0;
|
|
818
837
|
const tsgoTotalFiles = tsgoByFile.size;
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
if (node && node.properties.startLine === def.line) {
|
|
844
|
-
bestMatch = sym;
|
|
845
|
-
break;
|
|
838
|
+
const getNextFile = () => {
|
|
839
|
+
if (nextFileIdx >= fileEntries.length)
|
|
840
|
+
return null;
|
|
841
|
+
return fileEntries[nextFileIdx++];
|
|
842
|
+
};
|
|
843
|
+
/** Resolve files from the shared queue using a single tsgo service */
|
|
844
|
+
const resolveWorker = async (service) => {
|
|
845
|
+
const sliceResults = new Map();
|
|
846
|
+
let sliceResolved = 0;
|
|
847
|
+
let sliceFailed = 0;
|
|
848
|
+
let entry;
|
|
849
|
+
while ((entry = getNextFile()) !== null) {
|
|
850
|
+
const [filePath, calls] = entry;
|
|
851
|
+
totalFilesProcessed++;
|
|
852
|
+
if (totalFilesProcessed % 25 === 0) {
|
|
853
|
+
onProgress?.(totalFilesProcessed, tsgoTotalFiles, actualWorkers);
|
|
854
|
+
}
|
|
855
|
+
const absFilePath = path.resolve(repoPath, filePath);
|
|
856
|
+
for (const call of calls) {
|
|
857
|
+
try {
|
|
858
|
+
const def = await service.resolveDefinition(absFilePath, call.callLine - 1, call.callColumn);
|
|
859
|
+
if (!def) {
|
|
860
|
+
sliceFailed++;
|
|
861
|
+
continue;
|
|
846
862
|
}
|
|
847
|
-
|
|
848
|
-
|
|
863
|
+
const targetSymbols = ctx.symbols.lookupAllInFile(def.filePath);
|
|
864
|
+
if (targetSymbols.length === 0) {
|
|
865
|
+
sliceFailed++;
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
// Match by exact startLine, then by range containment
|
|
869
|
+
let bestMatch;
|
|
849
870
|
for (const sym of targetSymbols) {
|
|
850
871
|
const node = graph.getNode(toNodeId(sym.nodeId));
|
|
851
|
-
if (node) {
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
872
|
+
if (node && node.properties.startLine === def.line) {
|
|
873
|
+
bestMatch = sym;
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
if (!bestMatch) {
|
|
878
|
+
for (const sym of targetSymbols) {
|
|
879
|
+
const node = graph.getNode(toNodeId(sym.nodeId));
|
|
880
|
+
if (node) {
|
|
881
|
+
const sl = node.properties.startLine;
|
|
882
|
+
const el = node.properties.endLine;
|
|
883
|
+
if (sl !== undefined && el !== undefined && def.line >= sl && def.line <= el) {
|
|
884
|
+
bestMatch = sym;
|
|
885
|
+
break;
|
|
886
|
+
}
|
|
857
887
|
}
|
|
858
888
|
}
|
|
859
889
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
890
|
+
if (bestMatch) {
|
|
891
|
+
if (bestMatch.nodeId === call.sourceId) {
|
|
892
|
+
sliceFailed++;
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
const callKey = `${call.sourceId}\0${call.calledName}\0${call.callLine}`;
|
|
896
|
+
sliceResults.set(callKey, {
|
|
897
|
+
nodeId: bestMatch.nodeId,
|
|
898
|
+
confidence: TIER_CONFIDENCE['tsgo-resolved'],
|
|
899
|
+
reason: 'tsgo-lsp',
|
|
900
|
+
});
|
|
901
|
+
sliceResolved++;
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
sliceFailed++;
|
|
866
905
|
}
|
|
867
|
-
const callKey = `${call.sourceId}\0${call.calledName}\0${call.callLine}`;
|
|
868
|
-
results.set(callKey, {
|
|
869
|
-
nodeId: bestMatch.nodeId,
|
|
870
|
-
confidence: TIER_CONFIDENCE['tsgo-resolved'],
|
|
871
|
-
reason: 'tsgo-lsp',
|
|
872
|
-
});
|
|
873
|
-
resolved++;
|
|
874
906
|
}
|
|
875
|
-
|
|
876
|
-
|
|
907
|
+
catch {
|
|
908
|
+
sliceFailed++;
|
|
877
909
|
}
|
|
878
910
|
}
|
|
879
|
-
|
|
880
|
-
|
|
911
|
+
// Close file after all its calls are resolved — frees tsgo memory,
|
|
912
|
+
// prevents progressive slowdown as the type graph grows
|
|
913
|
+
service.notifyFileDeleted(absFilePath);
|
|
914
|
+
}
|
|
915
|
+
return { resolved: sliceResolved, failed: sliceFailed, results: sliceResults };
|
|
916
|
+
};
|
|
917
|
+
let resolved = 0;
|
|
918
|
+
let failed = 0;
|
|
919
|
+
if (actualWorkers === 1) {
|
|
920
|
+
// Single process — use the existing service (already started)
|
|
921
|
+
const outcome = await resolveWorker(tsgoService);
|
|
922
|
+
resolved = outcome.resolved;
|
|
923
|
+
failed = outcome.failed;
|
|
924
|
+
for (const [k, v] of outcome.results)
|
|
925
|
+
results.set(k, v);
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
// Parallel — spawn extra services, all pull from shared queue
|
|
929
|
+
const extraServices = [];
|
|
930
|
+
try {
|
|
931
|
+
// Start extra tsgo processes in parallel
|
|
932
|
+
const startPromises = [];
|
|
933
|
+
for (let i = 1; i < actualWorkers; i++) {
|
|
934
|
+
startPromises.push((async () => {
|
|
935
|
+
const svc = new TsgoService(repoPath);
|
|
936
|
+
if (await svc.start())
|
|
937
|
+
return svc;
|
|
938
|
+
return null;
|
|
939
|
+
})());
|
|
940
|
+
}
|
|
941
|
+
const started = await Promise.all(startPromises);
|
|
942
|
+
for (const svc of started) {
|
|
943
|
+
if (svc)
|
|
944
|
+
extraServices.push(svc);
|
|
881
945
|
}
|
|
946
|
+
// Build final service list: original + extras that started successfully
|
|
947
|
+
const services = [tsgoService, ...extraServices];
|
|
948
|
+
if (process.env['CODE_MAPPER_VERBOSE'])
|
|
949
|
+
console.error(`Code Mapper: ${services.length} tsgo processes ready, resolving with dynamic dispatch...`);
|
|
950
|
+
// All workers pull from the shared queue — naturally self-balancing
|
|
951
|
+
const outcomes = await Promise.all(services.map(svc => resolveWorker(svc)));
|
|
952
|
+
for (const outcome of outcomes) {
|
|
953
|
+
resolved += outcome.resolved;
|
|
954
|
+
failed += outcome.failed;
|
|
955
|
+
for (const [k, v] of outcome.results)
|
|
956
|
+
results.set(k, v);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
finally {
|
|
960
|
+
// Stop extra services (the original is stopped by the caller)
|
|
961
|
+
for (const svc of extraServices)
|
|
962
|
+
svc.stop();
|
|
882
963
|
}
|
|
883
964
|
}
|
|
884
965
|
const elapsed = Date.now() - t0;
|
|
885
|
-
|
|
966
|
+
if (process.env['CODE_MAPPER_VERBOSE'])
|
|
967
|
+
console.error(`Code Mapper: tsgo resolved ${resolved}/${eligible.length} calls in ${elapsed}ms (${failed} unresolvable, ${actualWorkers} process${actualWorkers > 1 ? 'es' : ''})`);
|
|
886
968
|
return results;
|
|
887
969
|
}
|
|
888
970
|
/** Generic method names that produce false edges when receiver type is unknown (worker-extracted path) */
|
|
@@ -928,7 +1010,7 @@ export const processCallsFromExtracted = async (graph, extractedCalls, ctx, onPr
|
|
|
928
1010
|
for (const [filePath, calls] of byFile) {
|
|
929
1011
|
filesProcessed++;
|
|
930
1012
|
if (filesProcessed % 25 === 0) {
|
|
931
|
-
onProgress?.(filesProcessed, totalFiles);
|
|
1013
|
+
onProgress?.(filesProcessed, totalFiles, 1);
|
|
932
1014
|
await yieldToEventLoop();
|
|
933
1015
|
}
|
|
934
1016
|
ctx.enableCache(filePath);
|
|
@@ -192,7 +192,7 @@ export const runPipelineFromRepo = async (repoPath, onProgress, opts) => {
|
|
|
192
192
|
percent: Math.round(parsingProgress),
|
|
193
193
|
message: `Parsing chunk ${chunkIdx + 1}/${numChunks}...`,
|
|
194
194
|
detail: filePath,
|
|
195
|
-
stats: { filesProcessed: globalCurrent, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
|
|
195
|
+
stats: { filesProcessed: globalCurrent, totalFiles: totalParseable, nodesCreated: graph.nodeCount, ...(workerPool ? { workerCount: workerPool.size } : {}) },
|
|
196
196
|
});
|
|
197
197
|
}, workerPool);
|
|
198
198
|
const parseMs = Date.now() - parseStart;
|
|
@@ -297,13 +297,13 @@ export const runPipelineFromRepo = async (repoPath, onProgress, opts) => {
|
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
try {
|
|
300
|
-
await processCallsFromExtracted(graph, allExtractedCalls, ctx, (current, total) => {
|
|
300
|
+
await processCallsFromExtracted(graph, allExtractedCalls, ctx, (current, total, workerCount) => {
|
|
301
301
|
const callPercent = 70 + Math.round((current / Math.max(total, 1)) * 12);
|
|
302
302
|
onProgress({
|
|
303
303
|
phase: 'calls',
|
|
304
304
|
percent: callPercent,
|
|
305
305
|
message: `Resolving calls: ${current}/${total} files...`,
|
|
306
|
-
stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount },
|
|
306
|
+
stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount, ...(workerCount ? { workerCount } : {}) },
|
|
307
307
|
});
|
|
308
308
|
}, allConstructorBindings.length > 0 ? allConstructorBindings : undefined, tsgoService, repoPath);
|
|
309
309
|
}
|
|
@@ -258,10 +258,10 @@ export class TsgoService {
|
|
|
258
258
|
this.process.stderr.on('data', (chunk) => {
|
|
259
259
|
const msg = chunk.toString().trim();
|
|
260
260
|
if (msg)
|
|
261
|
-
|
|
261
|
+
verbose('stderr:', msg);
|
|
262
262
|
});
|
|
263
263
|
this.process.on('exit', (code, signal) => {
|
|
264
|
-
|
|
264
|
+
verbose(`process exited (code=${code}, signal=${signal})`);
|
|
265
265
|
this.ready = false;
|
|
266
266
|
this.process = null;
|
|
267
267
|
});
|
|
@@ -284,7 +284,7 @@ export class TsgoService {
|
|
|
284
284
|
// Send initialized notification
|
|
285
285
|
this.send({ jsonrpc: '2.0', method: 'initialized', params: {} });
|
|
286
286
|
this.ready = true;
|
|
287
|
-
|
|
287
|
+
verbose('LSP ready');
|
|
288
288
|
return true;
|
|
289
289
|
}
|
|
290
290
|
catch (err) {
|
package/dist/types/pipeline.d.ts
CHANGED
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.9",
|
|
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",
|