gitnexus 1.5.2 → 1.6.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.
- package/README.md +10 -0
- package/dist/_shared/graph/types.d.ts +1 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +1 -0
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +2 -0
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/languages.d.ts +1 -0
- package/dist/_shared/languages.d.ts.map +1 -1
- package/dist/_shared/languages.js +1 -0
- package/dist/_shared/languages.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +3 -1
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/mro-strategy.d.ts +19 -0
- package/dist/_shared/mro-strategy.d.ts.map +1 -0
- package/dist/_shared/mro-strategy.js +2 -0
- package/dist/_shared/mro-strategy.js.map +1 -0
- package/dist/cli/ai-context.d.ts +1 -0
- package/dist/cli/ai-context.js +28 -4
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +2 -1
- package/dist/cli/group.d.ts +2 -0
- package/dist/cli/group.js +233 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/serve.js +4 -1
- package/dist/cli/setup.js +34 -3
- package/dist/cli/wiki.js +15 -44
- package/dist/config/ignore-service.js +8 -3
- package/dist/core/augmentation/engine.js +1 -1
- package/dist/core/git-staleness.d.ts +13 -0
- package/dist/core/git-staleness.js +29 -0
- package/dist/core/group/bridge-db.d.ts +82 -0
- package/dist/core/group/bridge-db.js +460 -0
- package/dist/core/group/bridge-schema.d.ts +27 -0
- package/dist/core/group/bridge-schema.js +55 -0
- package/dist/core/group/config-parser.d.ts +3 -0
- package/dist/core/group/config-parser.js +83 -0
- package/dist/core/group/contract-extractor.d.ts +7 -0
- package/dist/core/group/contract-extractor.js +1 -0
- package/dist/core/group/extractors/grpc-extractor.d.ts +16 -0
- package/dist/core/group/extractors/grpc-extractor.js +264 -0
- package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
- package/dist/core/group/extractors/http-route-extractor.js +428 -0
- package/dist/core/group/extractors/topic-extractor.d.ts +9 -0
- package/dist/core/group/extractors/topic-extractor.js +234 -0
- package/dist/core/group/matching.d.ts +13 -0
- package/dist/core/group/matching.js +198 -0
- package/dist/core/group/normalization.d.ts +3 -0
- package/dist/core/group/normalization.js +115 -0
- package/dist/core/group/service-boundary-detector.d.ts +8 -0
- package/dist/core/group/service-boundary-detector.js +155 -0
- package/dist/core/group/service.d.ts +46 -0
- package/dist/core/group/service.js +160 -0
- package/dist/core/group/storage.d.ts +9 -0
- package/dist/core/group/storage.js +91 -0
- package/dist/core/group/sync.d.ts +21 -0
- package/dist/core/group/sync.js +148 -0
- package/dist/core/group/types.d.ts +130 -0
- package/dist/core/group/types.js +1 -0
- package/dist/core/ingestion/binding-accumulator.d.ts +207 -0
- package/dist/core/ingestion/binding-accumulator.js +332 -0
- package/dist/core/ingestion/call-processor.d.ts +155 -24
- package/dist/core/ingestion/call-processor.js +1129 -247
- package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/generic.js +135 -0
- package/dist/core/ingestion/class-types.d.ts +34 -0
- package/dist/core/ingestion/class-types.js +1 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +1 -0
- package/dist/core/ingestion/entry-point-scoring.js +1 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +5 -1
- package/dist/core/ingestion/field-extractors/configs/helpers.js +13 -3
- package/dist/core/ingestion/field-types.d.ts +2 -2
- package/dist/core/ingestion/filesystem-walker.js +8 -0
- package/dist/core/ingestion/framework-detection.d.ts +1 -0
- package/dist/core/ingestion/framework-detection.js +1 -0
- package/dist/core/ingestion/heritage-processor.d.ts +8 -15
- package/dist/core/ingestion/heritage-processor.js +15 -28
- package/dist/core/ingestion/import-processor.d.ts +1 -11
- package/dist/core/ingestion/import-processor.js +0 -12
- package/dist/core/ingestion/import-resolvers/utils.js +1 -0
- package/dist/core/ingestion/import-resolvers/vue.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/vue.js +9 -0
- package/dist/core/ingestion/language-provider.d.ts +6 -3
- package/dist/core/ingestion/languages/c-cpp.js +168 -1
- package/dist/core/ingestion/languages/csharp.js +20 -0
- package/dist/core/ingestion/languages/dart.js +26 -4
- package/dist/core/ingestion/languages/go.js +22 -0
- package/dist/core/ingestion/languages/index.d.ts +1 -0
- package/dist/core/ingestion/languages/index.js +2 -0
- package/dist/core/ingestion/languages/java.js +17 -0
- package/dist/core/ingestion/languages/kotlin.js +24 -1
- package/dist/core/ingestion/languages/php.js +23 -11
- package/dist/core/ingestion/languages/python.js +9 -0
- package/dist/core/ingestion/languages/ruby.js +28 -0
- package/dist/core/ingestion/languages/rust.js +38 -0
- package/dist/core/ingestion/languages/swift.js +31 -0
- package/dist/core/ingestion/languages/typescript.d.ts +1 -0
- package/dist/core/ingestion/languages/typescript.js +54 -1
- package/dist/core/ingestion/languages/vue.d.ts +13 -0
- package/dist/core/ingestion/languages/vue.js +81 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
- package/dist/core/ingestion/method-extractors/configs/csharp.js +5 -1
- package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
- package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.js +13 -4
- package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
- package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.js +285 -0
- package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
- package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
- package/dist/core/ingestion/method-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +338 -0
- package/dist/core/ingestion/method-extractors/generic.js +38 -15
- package/dist/core/ingestion/method-types.d.ts +25 -0
- package/dist/core/ingestion/model/field-registry.d.ts +18 -0
- package/dist/core/ingestion/model/field-registry.js +22 -0
- package/dist/core/ingestion/model/heritage-map.d.ts +70 -0
- package/dist/core/ingestion/model/heritage-map.js +159 -0
- package/dist/core/ingestion/model/index.d.ts +20 -0
- package/dist/core/ingestion/model/index.js +41 -0
- package/dist/core/ingestion/model/method-registry.d.ts +62 -0
- package/dist/core/ingestion/model/method-registry.js +130 -0
- package/dist/core/ingestion/model/registration-table.d.ts +139 -0
- package/dist/core/ingestion/model/registration-table.js +224 -0
- package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
- package/dist/core/ingestion/model/resolution-context.js +337 -0
- package/dist/core/ingestion/model/resolve.d.ts +56 -0
- package/dist/core/ingestion/model/resolve.js +242 -0
- package/dist/core/ingestion/model/semantic-model.d.ts +86 -0
- package/dist/core/ingestion/model/semantic-model.js +120 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +222 -0
- package/dist/core/ingestion/model/symbol-table.js +206 -0
- package/dist/core/ingestion/model/type-registry.d.ts +39 -0
- package/dist/core/ingestion/model/type-registry.js +62 -0
- package/dist/core/ingestion/mro-processor.d.ts +4 -3
- package/dist/core/ingestion/mro-processor.js +310 -106
- package/dist/core/ingestion/parsing-processor.d.ts +5 -4
- package/dist/core/ingestion/parsing-processor.js +210 -85
- package/dist/core/ingestion/pipeline.d.ts +2 -0
- package/dist/core/ingestion/pipeline.js +192 -68
- package/dist/core/ingestion/tree-sitter-queries.d.ts +6 -6
- package/dist/core/ingestion/tree-sitter-queries.js +37 -0
- package/dist/core/ingestion/type-env.d.ts +15 -2
- package/dist/core/ingestion/type-env.js +163 -102
- package/dist/core/ingestion/type-extractors/csharp.js +17 -0
- package/dist/core/ingestion/type-extractors/jvm.js +11 -0
- package/dist/core/ingestion/type-extractors/php.js +0 -55
- package/dist/core/ingestion/type-extractors/ruby.js +0 -32
- package/dist/core/ingestion/type-extractors/swift.js +13 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +8 -8
- package/dist/core/ingestion/type-extractors/typescript.js +66 -69
- package/dist/core/ingestion/utils/ast-helpers.d.ts +33 -43
- package/dist/core/ingestion/utils/ast-helpers.js +129 -565
- package/dist/core/ingestion/utils/method-props.d.ts +32 -0
- package/dist/core/ingestion/utils/method-props.js +147 -0
- package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
- package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +31 -19
- package/dist/core/ingestion/workers/parse-worker.js +463 -198
- package/dist/core/lbug/lbug-adapter.d.ts +6 -0
- package/dist/core/lbug/lbug-adapter.js +68 -3
- package/dist/core/lbug/pool-adapter.d.ts +76 -0
- package/dist/core/lbug/pool-adapter.js +522 -0
- package/dist/core/run-analyze.d.ts +2 -0
- package/dist/core/run-analyze.js +1 -1
- package/dist/core/search/bm25-index.js +1 -1
- package/dist/core/tree-sitter/parser-loader.js +1 -0
- package/dist/core/wiki/graph-queries.js +1 -1
- package/dist/core/wiki/html-viewer.js +6 -4
- package/dist/core/wiki/llm-client.js +4 -6
- package/dist/mcp/core/embedder.js +6 -5
- package/dist/mcp/core/lbug-adapter.d.ts +3 -63
- package/dist/mcp/core/lbug-adapter.js +3 -484
- package/dist/mcp/local/local-backend.d.ts +31 -2
- package/dist/mcp/local/local-backend.js +255 -46
- package/dist/mcp/resources.js +5 -4
- package/dist/mcp/staleness.d.ts +3 -13
- package/dist/mcp/staleness.js +2 -31
- package/dist/mcp/tools.js +80 -4
- package/dist/server/analyze-job.d.ts +2 -0
- package/dist/server/analyze-job.js +4 -0
- package/dist/server/api.d.ts +20 -1
- package/dist/server/api.js +306 -71
- package/dist/server/git-clone.d.ts +2 -1
- package/dist/server/git-clone.js +98 -5
- package/dist/storage/git.d.ts +13 -0
- package/dist/storage/git.js +25 -0
- package/dist/storage/repo-manager.js +1 -1
- package/package.json +8 -2
- package/scripts/patch-tree-sitter-swift.cjs +78 -0
- package/dist/core/ingestion/named-binding-processor.d.ts +0 -18
- package/dist/core/ingestion/named-binding-processor.js +0 -42
- package/dist/core/ingestion/resolution-context.d.ts +0 -58
- package/dist/core/ingestion/resolution-context.js +0 -135
- package/dist/core/ingestion/symbol-table.d.ts +0 -79
- package/dist/core/ingestion/symbol-table.js +0 -115
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { createKnowledgeGraph } from '../graph/graph.js';
|
|
2
|
+
import { BindingAccumulator, enrichExportedTypeMap, } from './binding-accumulator.js';
|
|
2
3
|
import { processStructure } from './structure-processor.js';
|
|
3
4
|
import { processMarkdown } from './markdown-processor.js';
|
|
4
5
|
import { processCobol, isCobolFile, isJclFile } from './cobol-processor.js';
|
|
5
6
|
import { processParsing } from './parsing-processor.js';
|
|
6
7
|
import { processImports, processImportsFromExtracted, buildImportResolutionContext, } from './import-processor.js';
|
|
7
8
|
import { EMPTY_INDEX } from './import-resolvers/utils.js';
|
|
8
|
-
import { processCalls, processCallsFromExtracted, processAssignmentsFromExtracted, processRoutesFromExtracted, processNextjsFetchRoutes, extractFetchCallsFromFiles, seedCrossFileReceiverTypes, buildImportedReturnTypes, buildImportedRawReturnTypes, buildExportedTypeMapFromGraph,
|
|
9
|
+
import { processCalls, processCallsFromExtracted, processAssignmentsFromExtracted, processRoutesFromExtracted, processNextjsFetchRoutes, extractFetchCallsFromFiles, seedCrossFileReceiverTypes, buildImportedReturnTypes, buildImportedRawReturnTypes, buildExportedTypeMapFromGraph, } from './call-processor.js';
|
|
10
|
+
import { buildHeritageMap } from './model/heritage-map.js';
|
|
9
11
|
import { nextjsFileToRouteURL, normalizeFetchURL } from './route-extractors/nextjs.js';
|
|
10
12
|
import { expoFileToRouteURL } from './route-extractors/expo.js';
|
|
11
13
|
import { phpFileToRouteURL } from './route-extractors/php.js';
|
|
12
14
|
import { extractResponseShapes, extractPHPResponseShapes, } from './route-extractors/response-shapes.js';
|
|
13
15
|
import { extractMiddlewareChain, extractNextjsMiddlewareConfig, compileMatcher, compiledMatcherMatchesRoute, } from './route-extractors/middleware.js';
|
|
14
16
|
import { generateId } from '../../lib/utils.js';
|
|
15
|
-
import { processHeritage, processHeritageFromExtracted, extractExtractedHeritageFromFiles, } from './heritage-processor.js';
|
|
17
|
+
import { processHeritage, processHeritageFromExtracted, extractExtractedHeritageFromFiles, getHeritageStrategyForLanguage, } from './heritage-processor.js';
|
|
16
18
|
import { computeMRO } from './mro-processor.js';
|
|
17
19
|
import { processCommunities } from './community-processor.js';
|
|
18
20
|
import { processProcesses } from './process-processor.js';
|
|
19
|
-
import { createResolutionContext } from './resolution-context.js';
|
|
21
|
+
import { createResolutionContext } from './model/resolution-context.js';
|
|
20
22
|
import { createASTCache } from './ast-cache.js';
|
|
21
23
|
import { getLanguageFromFilename } from '../../_shared/index.js';
|
|
22
24
|
import { walkRepositoryPaths, readFileContents } from './filesystem-walker.js';
|
|
@@ -252,7 +254,7 @@ async function runCrossFileBindingPropagation(graph, ctx, exportedTypeMap, allPa
|
|
|
252
254
|
// For the worker path, buildTypeEnv runs inside workers without SymbolTable,
|
|
253
255
|
// so exported bindings must be collected from graph + SymbolTable in main thread.
|
|
254
256
|
if (exportedTypeMap.size === 0 && graph.nodeCount > 0) {
|
|
255
|
-
const graphExports = buildExportedTypeMapFromGraph(graph, ctx.symbols);
|
|
257
|
+
const graphExports = buildExportedTypeMapFromGraph(graph, ctx.model.symbols);
|
|
256
258
|
for (const [fp, exports] of graphExports)
|
|
257
259
|
exportedTypeMap.set(fp, exports);
|
|
258
260
|
}
|
|
@@ -278,7 +280,7 @@ async function runCrossFileBindingPropagation(graph, ctx, exportedTypeMap, allPa
|
|
|
278
280
|
filesWithGaps++;
|
|
279
281
|
break;
|
|
280
282
|
}
|
|
281
|
-
const def = ctx.symbols.lookupExactFull(binding.sourcePath, binding.exportedName);
|
|
283
|
+
const def = ctx.model.symbols.lookupExactFull(binding.sourcePath, binding.exportedName);
|
|
282
284
|
if (def?.returnType) {
|
|
283
285
|
filesWithGaps++;
|
|
284
286
|
break;
|
|
@@ -321,8 +323,8 @@ async function runCrossFileBindingPropagation(graph, ctx, exportedTypeMap, allPa
|
|
|
321
323
|
seeded.set(localName, type);
|
|
322
324
|
}
|
|
323
325
|
}
|
|
324
|
-
const importedReturns = buildImportedReturnTypes(filePath, ctx.namedImportMap, ctx.symbols);
|
|
325
|
-
const importedRawReturns = buildImportedRawReturnTypes(filePath, ctx.namedImportMap, ctx.symbols);
|
|
326
|
+
const importedReturns = buildImportedReturnTypes(filePath, ctx.namedImportMap, ctx.model.symbols);
|
|
327
|
+
const importedRawReturns = buildImportedRawReturnTypes(filePath, ctx.namedImportMap, ctx.model.symbols);
|
|
326
328
|
if (seeded.size === 0 && importedReturns.size === 0)
|
|
327
329
|
continue;
|
|
328
330
|
if (!allPathSet.has(filePath))
|
|
@@ -480,8 +482,8 @@ async function runScanAndStructure(repoPath, graph, onProgress) {
|
|
|
480
482
|
* Follow-up from PR review: MethodExtractor (FieldExtractor parity) and optional
|
|
481
483
|
* METHOD_IMPLEMENTS graph edges to make dispatch queryable without an in-memory map.
|
|
482
484
|
*/
|
|
483
|
-
async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, totalFiles, repoPath, pipelineStart, onProgress) {
|
|
484
|
-
const symbolTable = ctx.symbols;
|
|
485
|
+
async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, totalFiles, repoPath, pipelineStart, onProgress, options) {
|
|
486
|
+
const symbolTable = ctx.model.symbols;
|
|
485
487
|
const parseableScanned = scannedFiles.filter((f) => {
|
|
486
488
|
const lang = getLanguageFromFilename(f.path);
|
|
487
489
|
return lang && isLanguageAvailable(lang);
|
|
@@ -538,7 +540,8 @@ async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, tot
|
|
|
538
540
|
const totalBytes = parseableScanned.reduce((s, f) => s + f.size, 0);
|
|
539
541
|
// Create worker pool once, reuse across chunks
|
|
540
542
|
let workerPool;
|
|
541
|
-
if (
|
|
543
|
+
if (!options?.skipWorkers &&
|
|
544
|
+
(totalParseable >= MIN_FILES_FOR_WORKERS || totalBytes >= MIN_BYTES_FOR_WORKERS)) {
|
|
542
545
|
try {
|
|
543
546
|
let workerUrl = new URL('./workers/parse-worker.js', import.meta.url);
|
|
544
547
|
// When running under vitest, import.meta.url points to src/ where no .js exists.
|
|
@@ -578,7 +581,7 @@ async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, tot
|
|
|
578
581
|
// Phase 14: Collect exported type bindings for cross-file propagation
|
|
579
582
|
const exportedTypeMap = new Map();
|
|
580
583
|
// Accumulate file-scope TypeEnv bindings from workers (closes worker/sequential quality gap)
|
|
581
|
-
const
|
|
584
|
+
const bindingAccumulator = new BindingAccumulator();
|
|
582
585
|
// Accumulate fetch() calls from workers for Next.js route matching
|
|
583
586
|
const allFetchCalls = [];
|
|
584
587
|
// Accumulate framework-extracted routes (Laravel, etc.) for Route node creation
|
|
@@ -649,11 +652,15 @@ async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, tot
|
|
|
649
652
|
console.log(`🔗 E1: Seeded ${enrichedCount} cross-file receiver types (chunk ${chunkIdx + 1})`);
|
|
650
653
|
}
|
|
651
654
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
+
for (const _item of chunkWorkerData.calls)
|
|
656
|
+
deferredWorkerCalls.push(_item);
|
|
657
|
+
for (const _item of chunkWorkerData.heritage)
|
|
658
|
+
deferredWorkerHeritage.push(_item);
|
|
659
|
+
for (const _item of chunkWorkerData.constructorBindings)
|
|
660
|
+
deferredConstructorBindings.push(_item);
|
|
655
661
|
if (chunkWorkerData.assignments?.length) {
|
|
656
|
-
|
|
662
|
+
for (const _item of chunkWorkerData.assignments)
|
|
663
|
+
deferredAssignments.push(_item);
|
|
657
664
|
}
|
|
658
665
|
// Heritage + Routes — calls deferred until all chunks have contributed heritage
|
|
659
666
|
// (complete implementor map for interface dispatch).
|
|
@@ -685,25 +692,56 @@ async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, tot
|
|
|
685
692
|
});
|
|
686
693
|
}),
|
|
687
694
|
]);
|
|
688
|
-
// Collect
|
|
689
|
-
|
|
690
|
-
|
|
695
|
+
// Collect file-scope bindings into BindingAccumulator. The worker
|
|
696
|
+
// IPC payload carries only file-scope entries (`scope = ''`
|
|
697
|
+
// hardcoded here). See the FileScopeBindings JSDoc in
|
|
698
|
+
// parse-worker.ts for the rationale and Phase 9 reversion path.
|
|
699
|
+
//
|
|
700
|
+
// Defensive validation at the IPC boundary: silently skip entries
|
|
701
|
+
// with non-string varName/typeName. If a future worker regression
|
|
702
|
+
// (or a Phase 9 reversion mistake that emits 3-tuples into the
|
|
703
|
+
// 2-tuple consumer) produces malformed data, logging is better
|
|
704
|
+
// than silently writing `undefined` into the enrichment map.
|
|
705
|
+
if (chunkWorkerData.fileScopeBindings?.length) {
|
|
706
|
+
for (const { filePath, bindings } of chunkWorkerData.fileScopeBindings) {
|
|
707
|
+
if (typeof filePath !== 'string' || filePath.length === 0)
|
|
708
|
+
continue;
|
|
709
|
+
if (!Array.isArray(bindings))
|
|
710
|
+
continue;
|
|
711
|
+
const entries = [];
|
|
712
|
+
for (const tuple of bindings) {
|
|
713
|
+
if (!Array.isArray(tuple) || tuple.length !== 2)
|
|
714
|
+
continue;
|
|
715
|
+
const [varName, typeName] = tuple;
|
|
716
|
+
if (typeof varName !== 'string' || typeof typeName !== 'string')
|
|
717
|
+
continue;
|
|
718
|
+
entries.push({ scope: '', varName, typeName });
|
|
719
|
+
}
|
|
720
|
+
if (entries.length > 0) {
|
|
721
|
+
bindingAccumulator.appendFile(filePath, entries);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
691
724
|
}
|
|
692
725
|
// Collect fetch() calls for Next.js route matching
|
|
693
726
|
if (chunkWorkerData.fetchCalls?.length) {
|
|
694
|
-
|
|
727
|
+
for (const _item of chunkWorkerData.fetchCalls)
|
|
728
|
+
allFetchCalls.push(_item);
|
|
695
729
|
}
|
|
696
730
|
if (chunkWorkerData.routes?.length) {
|
|
697
|
-
|
|
731
|
+
for (const _item of chunkWorkerData.routes)
|
|
732
|
+
allExtractedRoutes.push(_item);
|
|
698
733
|
}
|
|
699
734
|
if (chunkWorkerData.decoratorRoutes?.length) {
|
|
700
|
-
|
|
735
|
+
for (const _item of chunkWorkerData.decoratorRoutes)
|
|
736
|
+
allDecoratorRoutes.push(_item);
|
|
701
737
|
}
|
|
702
738
|
if (chunkWorkerData.toolDefs?.length) {
|
|
703
|
-
|
|
739
|
+
for (const _item of chunkWorkerData.toolDefs)
|
|
740
|
+
allToolDefs.push(_item);
|
|
704
741
|
}
|
|
705
742
|
if (chunkWorkerData.ormQueries?.length) {
|
|
706
|
-
|
|
743
|
+
for (const _item of chunkWorkerData.ormQueries)
|
|
744
|
+
allORMQueries.push(_item);
|
|
707
745
|
}
|
|
708
746
|
}
|
|
709
747
|
else {
|
|
@@ -715,10 +753,10 @@ async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, tot
|
|
|
715
753
|
astCache.clear();
|
|
716
754
|
// chunkContents + chunkFiles + chunkWorkerData go out of scope → GC reclaims
|
|
717
755
|
}
|
|
718
|
-
//
|
|
719
|
-
const
|
|
720
|
-
?
|
|
721
|
-
:
|
|
756
|
+
// Build unified HeritageMap (parent lookup + implementor index) after all chunks.
|
|
757
|
+
const fullWorkerHeritageMap = deferredWorkerHeritage.length > 0
|
|
758
|
+
? buildHeritageMap(deferredWorkerHeritage, ctx, getHeritageStrategyForLanguage)
|
|
759
|
+
: undefined;
|
|
722
760
|
if (deferredWorkerCalls.length > 0) {
|
|
723
761
|
await processCallsFromExtracted(graph, deferredWorkerCalls, ctx, (current, total) => {
|
|
724
762
|
onProgress({
|
|
@@ -732,10 +770,23 @@ async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, tot
|
|
|
732
770
|
nodesCreated: graph.nodeCount,
|
|
733
771
|
},
|
|
734
772
|
});
|
|
735
|
-
}, deferredConstructorBindings.length > 0 ? deferredConstructorBindings : undefined,
|
|
773
|
+
}, deferredConstructorBindings.length > 0 ? deferredConstructorBindings : undefined, fullWorkerHeritageMap,
|
|
774
|
+
// Phase 9: pass the accumulator so processCallsFromExtracted can fall back
|
|
775
|
+
// to file-scope TypeEnv bindings when the SymbolTable lacks a return type
|
|
776
|
+
// for a cross-file callee (e.g. var x = getUser() → x: User).
|
|
777
|
+
//
|
|
778
|
+
// Lifecycle ordering: the accumulator is populated but NOT yet finalized
|
|
779
|
+
// at this seam. finalize() is called later (after the sequential-path
|
|
780
|
+
// processCalls which also appends via typeEnv.flush()). Moving finalize()
|
|
781
|
+
// before this call would break sequential-path repos. Pre-finalize reads
|
|
782
|
+
// are safe because finalize() is a write-lock-only operation with no side
|
|
783
|
+
// effects on stored data. All worker-path appendFile calls complete in the
|
|
784
|
+
// chunk loop above, so every worker-contributed binding is available via
|
|
785
|
+
// fileScopeGet().
|
|
786
|
+
bindingAccumulator);
|
|
736
787
|
}
|
|
737
788
|
if (deferredAssignments.length > 0) {
|
|
738
|
-
processAssignmentsFromExtracted(graph, deferredAssignments, ctx, deferredConstructorBindings.length > 0 ? deferredConstructorBindings : undefined);
|
|
789
|
+
processAssignmentsFromExtracted(graph, deferredAssignments, ctx, deferredConstructorBindings.length > 0 ? deferredConstructorBindings : undefined, bindingAccumulator);
|
|
739
790
|
}
|
|
740
791
|
}
|
|
741
792
|
finally {
|
|
@@ -746,18 +797,40 @@ async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, tot
|
|
|
746
797
|
// before any call resolution — same rationale as the worker-path inline synthesis.
|
|
747
798
|
if (sequentialChunkPaths.length > 0)
|
|
748
799
|
synthesizeWildcardImportBindings(graph, ctx);
|
|
749
|
-
//
|
|
750
|
-
//
|
|
751
|
-
|
|
800
|
+
// Pass 1: Extract heritage from all sequential chunks.
|
|
801
|
+
// Heritage must be fully accumulated BEFORE call resolution so the HeritageMap
|
|
802
|
+
// has the complete ancestor chain and implementor index (same constraint as
|
|
803
|
+
// the worker path).
|
|
804
|
+
//
|
|
805
|
+
// File contents are read once here and cached for Pass 2 to avoid a 2× I/O
|
|
806
|
+
// cost on the sequential path (ASTs are intentionally NOT cached — rebuilding
|
|
807
|
+
// them in Pass 2 keeps peak memory bounded to one chunk at a time).
|
|
808
|
+
const allSequentialHeritage = [];
|
|
809
|
+
const cachedSequentialChunkFiles = [];
|
|
752
810
|
for (const chunkPaths of sequentialChunkPaths) {
|
|
753
811
|
const chunkContents = await readFileContents(repoPath, chunkPaths);
|
|
754
812
|
const chunkFiles = chunkPaths
|
|
755
813
|
.filter((p) => chunkContents.has(p))
|
|
756
814
|
.map((p) => ({ path: p, content: chunkContents.get(p) }));
|
|
815
|
+
cachedSequentialChunkFiles.push(chunkFiles);
|
|
757
816
|
astCache = createASTCache(chunkFiles.length);
|
|
758
817
|
const sequentialHeritage = await extractExtractedHeritageFromFiles(chunkFiles, astCache);
|
|
759
|
-
|
|
760
|
-
|
|
818
|
+
// Manual loop (not spread) — `push(...arr)` blows the stack on very large
|
|
819
|
+
// arrays, see #650. Pay the explicit iteration cost for safety.
|
|
820
|
+
for (const h of sequentialHeritage)
|
|
821
|
+
allSequentialHeritage.push(h);
|
|
822
|
+
astCache.clear();
|
|
823
|
+
}
|
|
824
|
+
// Build unified HeritageMap from all sequential heritage (parent lookup + implementor index).
|
|
825
|
+
const sequentialHeritageMap = allSequentialHeritage.length > 0
|
|
826
|
+
? buildHeritageMap(allSequentialHeritage, ctx, getHeritageStrategyForLanguage)
|
|
827
|
+
: undefined;
|
|
828
|
+
// Pass 2: Process calls, heritage edges, fetch calls, and ORM queries per chunk.
|
|
829
|
+
// Reuse the file contents cached in Pass 1 instead of re-reading from disk.
|
|
830
|
+
for (let chunkIdx = 0; chunkIdx < sequentialChunkPaths.length; chunkIdx++) {
|
|
831
|
+
const chunkFiles = cachedSequentialChunkFiles[chunkIdx];
|
|
832
|
+
astCache = createASTCache(chunkFiles.length);
|
|
833
|
+
const rubyHeritage = await processCalls(graph, chunkFiles, astCache, ctx, undefined, exportedTypeMap, undefined, undefined, undefined, sequentialHeritageMap, bindingAccumulator);
|
|
761
834
|
await processHeritage(graph, chunkFiles, astCache, ctx);
|
|
762
835
|
if (rubyHeritage.length > 0) {
|
|
763
836
|
await processHeritageFromExtracted(graph, rubyHeritage, ctx);
|
|
@@ -765,13 +838,18 @@ async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, tot
|
|
|
765
838
|
// Extract fetch() calls for Next.js route matching (sequential path)
|
|
766
839
|
const chunkFetchCalls = await extractFetchCallsFromFiles(chunkFiles, astCache);
|
|
767
840
|
if (chunkFetchCalls.length > 0) {
|
|
768
|
-
|
|
841
|
+
for (const _item of chunkFetchCalls)
|
|
842
|
+
allFetchCalls.push(_item);
|
|
769
843
|
}
|
|
770
844
|
// Extract ORM queries (sequential path)
|
|
771
845
|
for (const f of chunkFiles) {
|
|
772
846
|
extractORMQueriesInline(f.path, f.content, allORMQueries);
|
|
773
847
|
}
|
|
774
848
|
astCache.clear();
|
|
849
|
+
// Release cached chunk content as soon as Pass 2 finishes with it so the
|
|
850
|
+
// Pass-1 content map drains incrementally rather than being held for the
|
|
851
|
+
// full duration of Pass 2.
|
|
852
|
+
cachedSequentialChunkFiles[chunkIdx] = [];
|
|
775
853
|
}
|
|
776
854
|
// Log resolution cache stats
|
|
777
855
|
if (isDev) {
|
|
@@ -780,36 +858,28 @@ async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, tot
|
|
|
780
858
|
const hitRate = total > 0 ? ((rcStats.cacheHits / total) * 100).toFixed(1) : '0';
|
|
781
859
|
console.log(`🔍 Resolution cache: ${rcStats.cacheHits} hits, ${rcStats.cacheMisses} misses (${hitRate}% hit rate)`);
|
|
782
860
|
}
|
|
783
|
-
// ──
|
|
784
|
-
//
|
|
785
|
-
//
|
|
786
|
-
// the
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
fileExports.set(name, type);
|
|
806
|
-
enriched++;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
if (isDev && enriched > 0) {
|
|
811
|
-
console.log(`🔗 Worker TypeEnv enrichment: ${enriched} fixpoint-inferred exports added to ExportedTypeMap`);
|
|
812
|
-
}
|
|
861
|
+
// ── Finalize the accumulator before the read phase begins. All worker-path
|
|
862
|
+
// appends (line ~934) and sequential-path flushes (via `processCalls` →
|
|
863
|
+
// `typeEnv.flush()` earlier in this function) have completed by here,
|
|
864
|
+
// so the finalize-write-lock is correct at this seam. Making the
|
|
865
|
+
// lifecycle contract explicit — `append → finalize → consume → dispose`.
|
|
866
|
+
// Previously `finalize()` was called much later in `runPipelineFromRepo`
|
|
867
|
+
// after the enrichment loop had already read the mutable accumulator.
|
|
868
|
+
bindingAccumulator.finalize();
|
|
869
|
+
// ── Worker path quality enrichment: merge file-scope bindings into ExportedTypeMap ──
|
|
870
|
+
// Counterpart to `collectExportedBindings()` in call-processor.ts which
|
|
871
|
+
// handles the sequential path (main thread, full SymbolTable access).
|
|
872
|
+
// This call handles the worker path via the accumulator. Both sites
|
|
873
|
+
// populate the same `exportedTypeMap` with subtly different export-check
|
|
874
|
+
// semantics — sequential uses SymbolTable + graph lookup, `enrichExportedTypeMap`
|
|
875
|
+
// uses a three-candidate-ID graph lookup. They must stay in sync until
|
|
876
|
+
// Phase 9 unifies them. If you edit one, check the other.
|
|
877
|
+
//
|
|
878
|
+
// The enrichment loop itself lives in `binding-accumulator.ts` so tests
|
|
879
|
+
// can exercise the real production code instead of reimplementing it.
|
|
880
|
+
const enriched = enrichExportedTypeMap(bindingAccumulator, graph, exportedTypeMap);
|
|
881
|
+
if (isDev && enriched > 0) {
|
|
882
|
+
console.log(`🔗 Worker TypeEnv enrichment: ${enriched} fixpoint-inferred exports added to ExportedTypeMap`);
|
|
813
883
|
}
|
|
814
884
|
// ── Final synthesis pass for whole-module-import languages ──
|
|
815
885
|
// Per-chunk synthesis (above) already ran incrementally. This final pass ensures
|
|
@@ -832,13 +902,14 @@ async function runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, tot
|
|
|
832
902
|
allDecoratorRoutes,
|
|
833
903
|
allToolDefs,
|
|
834
904
|
allORMQueries,
|
|
905
|
+
bindingAccumulator,
|
|
835
906
|
};
|
|
836
907
|
}
|
|
837
908
|
/**
|
|
838
909
|
* Post-parse graph analysis: MRO, community detection, process extraction.
|
|
839
910
|
*
|
|
840
911
|
* @reads graph (all nodes and relationships from parse + resolve phases)
|
|
841
|
-
* @writes graph (Community nodes, Process nodes, MEMBER_OF edges, STEP_IN_PROCESS edges,
|
|
912
|
+
* @writes graph (Community nodes, Process nodes, MEMBER_OF edges, STEP_IN_PROCESS edges, METHOD_OVERRIDES edges)
|
|
842
913
|
*/
|
|
843
914
|
async function runGraphAnalysisPhases(graph, totalFiles, onProgress, routeRegistry, toolDefs) {
|
|
844
915
|
// ── Phase 4.5: Method Resolution Order ──────────────────────────────
|
|
@@ -850,7 +921,7 @@ async function runGraphAnalysisPhases(graph, totalFiles, onProgress, routeRegist
|
|
|
850
921
|
});
|
|
851
922
|
const mroResult = computeMRO(graph);
|
|
852
923
|
if (isDev && mroResult.entries.length > 0) {
|
|
853
|
-
console.log(`🔀 MRO: ${mroResult.entries.length} classes analyzed, ${mroResult.ambiguityCount} ambiguities
|
|
924
|
+
console.log(`🔀 MRO: ${mroResult.entries.length} classes analyzed, ${mroResult.ambiguityCount} ambiguities, ${mroResult.overrideEdges} METHOD_OVERRIDES, ${mroResult.methodImplementsEdges} METHOD_IMPLEMENTS`);
|
|
854
925
|
}
|
|
855
926
|
// ── Phase 5: Communities ───────────────────────────────────────────
|
|
856
927
|
onProgress({
|
|
@@ -1023,11 +1094,24 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
1023
1094
|
const graph = createKnowledgeGraph();
|
|
1024
1095
|
const ctx = createResolutionContext();
|
|
1025
1096
|
const pipelineStart = Date.now();
|
|
1097
|
+
// Hoisted reference for error-path cleanup. The accumulator is normally
|
|
1098
|
+
// disposed at the happy-path seam after the dev telemetry log, but if any
|
|
1099
|
+
// step between the runChunkedParseAndResolve return and that seam throws
|
|
1100
|
+
// (ORM processing, tool node creation, Phase 14, graph analysis), the
|
|
1101
|
+
// catch handler disposes it here so the heap footprint does not leak
|
|
1102
|
+
// through the rethrow. See binding-accumulator.ts dispose() JSDoc for the
|
|
1103
|
+
// lifecycle contract.
|
|
1104
|
+
let bindingAccumulatorForCleanup;
|
|
1026
1105
|
try {
|
|
1027
1106
|
// Phase 1+2: Scan paths, build structure, process markdown
|
|
1028
1107
|
const { scannedFiles, allPaths, totalFiles } = await runScanAndStructure(repoPath, graph, onProgress);
|
|
1029
1108
|
// Phase 3+4: Chunked parse + resolve (imports, calls, heritage, routes)
|
|
1030
|
-
const { exportedTypeMap, allFetchCalls, allExtractedRoutes, allDecoratorRoutes, allToolDefs, allORMQueries, } = await runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, totalFiles, repoPath, pipelineStart, onProgress);
|
|
1109
|
+
const { exportedTypeMap, allFetchCalls, allExtractedRoutes, allDecoratorRoutes, allToolDefs, allORMQueries, bindingAccumulator, } = await runChunkedParseAndResolve(graph, ctx, scannedFiles, allPaths, totalFiles, repoPath, pipelineStart, onProgress, options);
|
|
1110
|
+
// Track the accumulator for error-path cleanup — the happy-path dispose
|
|
1111
|
+
// is still at the post-telemetry seam below, this reference is only
|
|
1112
|
+
// consulted by the catch handler if any step between here and there
|
|
1113
|
+
// throws.
|
|
1114
|
+
bindingAccumulatorForCleanup = bindingAccumulator;
|
|
1031
1115
|
const routeRegistry = new Map();
|
|
1032
1116
|
// Detect Expo Router app/ roots vs Next.js app/ roots (monorepo-safe).
|
|
1033
1117
|
const expoAppRoots = new Set();
|
|
@@ -1303,6 +1387,40 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
1303
1387
|
if (allORMQueries.length > 0) {
|
|
1304
1388
|
processORMQueries(graph, allORMQueries, isDev);
|
|
1305
1389
|
}
|
|
1390
|
+
// `bindingAccumulator.finalize()` was moved inside `runChunkedParseAndResolve`
|
|
1391
|
+
// to immediately precede the enrichment loop — see the comment there for
|
|
1392
|
+
// the ordering rationale. By the time execution
|
|
1393
|
+
// reaches this point, the accumulator has already been finalized, consumed
|
|
1394
|
+
// by the enrichment loop, and is ready for dispose() below after the dev
|
|
1395
|
+
// telemetry log captures peak state.
|
|
1396
|
+
if (isDev) {
|
|
1397
|
+
if (bindingAccumulator.totalBindings > 0) {
|
|
1398
|
+
const memKB = Math.round(bindingAccumulator.estimateMemoryBytes() / 1024);
|
|
1399
|
+
console.log(`📦 BindingAccumulator: ${bindingAccumulator.totalBindings} bindings across ${bindingAccumulator.fileCount} files (~${memKB} KB)`);
|
|
1400
|
+
}
|
|
1401
|
+
else if (totalFiles > 0) {
|
|
1402
|
+
// Zero-binding signal: if the pipeline parsed files but the
|
|
1403
|
+
// accumulator is empty, something upstream dropped all bindings.
|
|
1404
|
+
// Flag it so operators can spot a regression (e.g. a worker path
|
|
1405
|
+
// that accidentally emits empty fileScopeBindings arrays for every
|
|
1406
|
+
// file, or a TypeEnv build failure). Dev-mode only.
|
|
1407
|
+
console.log(`📦 BindingAccumulator: EMPTY — 0 bindings across 0 files despite ${totalFiles} parsed files. If the codebase has typed bindings, this indicates an upstream regression.`);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
// Release the accumulator's heap footprint now. Both consumers of the
|
|
1411
|
+
// accumulator have completed:
|
|
1412
|
+
// 1. ExportedTypeMap enrichment loop (enrichExportedTypeMap, above).
|
|
1413
|
+
// 2. Phase 9: processCallsFromExtracted in runChunkedParseAndResolve,
|
|
1414
|
+
// which uses the accumulator as a BindingAccumulator fallback for
|
|
1415
|
+
// cross-file return types when the SymbolTable has no returnType.
|
|
1416
|
+
// Phase 14 (runCrossFileBindingPropagation) and runGraphAnalysisPhases
|
|
1417
|
+
// do not read the accumulator — keeping it alive through those long-
|
|
1418
|
+
// running phases pins heap for no reason.
|
|
1419
|
+
bindingAccumulator.dispose();
|
|
1420
|
+
// Happy-path dispose completed — clear the cleanup ref so the catch
|
|
1421
|
+
// handler doesn't attempt a second (harmless but noisy) dispose if a
|
|
1422
|
+
// later phase throws.
|
|
1423
|
+
bindingAccumulatorForCleanup = undefined;
|
|
1306
1424
|
// ── Phase 14: Cross-file binding propagation (topological level sort) ──
|
|
1307
1425
|
await runCrossFileBindingPropagation(graph, ctx, exportedTypeMap, allPaths, totalFiles, repoPath, pipelineStart, onProgress);
|
|
1308
1426
|
// Post-parse graph analysis (MRO, communities, processes)
|
|
@@ -1328,6 +1446,12 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
1328
1446
|
return { graph, repoPath, totalFileCount: totalFiles, communityResult, processResult };
|
|
1329
1447
|
}
|
|
1330
1448
|
catch (error) {
|
|
1449
|
+
// Error-path cleanup: dispose the accumulator if a step after the
|
|
1450
|
+
// destructure from runChunkedParseAndResolve but before the happy-path
|
|
1451
|
+
// dispose threw. The reference is cleared on the happy path, so this
|
|
1452
|
+
// is a no-op when the pipeline completed successfully and then threw
|
|
1453
|
+
// from an unrelated post-dispose step (e.g., future cleanup code).
|
|
1454
|
+
bindingAccumulatorForCleanup?.dispose();
|
|
1331
1455
|
ctx.clear();
|
|
1332
1456
|
throw error;
|
|
1333
1457
|
}
|