@veewo/gitnexus 1.3.11 → 1.4.6-rc
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 +37 -80
- package/dist/benchmark/agent-context/tool-runner.js +2 -2
- package/dist/benchmark/neonspark-candidates.js +3 -3
- package/dist/benchmark/tool-runner.js +2 -2
- package/dist/cli/ai-context.d.ts +2 -1
- package/dist/cli/ai-context.js +16 -12
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +68 -48
- package/dist/cli/augment.js +1 -1
- package/dist/cli/eval-server.d.ts +8 -1
- package/dist/cli/eval-server.js +30 -13
- package/dist/cli/index.js +28 -82
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +18 -0
- package/dist/cli/mcp.js +3 -1
- package/dist/cli/setup.js +87 -48
- package/dist/cli/setup.test.js +18 -13
- package/dist/cli/skill-gen.d.ts +26 -0
- package/dist/cli/skill-gen.js +549 -0
- package/dist/cli/status.js +13 -4
- package/dist/cli/tool.d.ts +3 -2
- package/dist/cli/tool.js +50 -16
- package/dist/cli/wiki.js +8 -4
- package/dist/config/ignore-service.d.ts +25 -0
- package/dist/config/ignore-service.js +76 -0
- package/dist/config/supported-languages.d.ts +4 -1
- package/dist/config/supported-languages.js +3 -2
- package/dist/core/augmentation/engine.js +94 -67
- package/dist/core/embeddings/embedder.d.ts +1 -1
- package/dist/core/embeddings/embedder.js +1 -1
- package/dist/core/embeddings/embedding-pipeline.d.ts +3 -3
- package/dist/core/embeddings/embedding-pipeline.js +52 -25
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/types.d.ts +7 -2
- package/dist/core/ingestion/ast-cache.js +3 -2
- package/dist/core/ingestion/call-processor.d.ts +8 -6
- package/dist/core/ingestion/call-processor.js +468 -206
- package/dist/core/ingestion/call-routing.d.ts +53 -0
- package/dist/core/ingestion/call-routing.js +108 -0
- package/dist/core/ingestion/constants.d.ts +16 -0
- package/dist/core/ingestion/constants.js +16 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +2 -1
- package/dist/core/ingestion/entry-point-scoring.js +116 -23
- package/dist/core/ingestion/export-detection.d.ts +18 -0
- package/dist/core/ingestion/export-detection.js +231 -0
- package/dist/core/ingestion/filesystem-walker.js +4 -3
- package/dist/core/ingestion/framework-detection.d.ts +19 -4
- package/dist/core/ingestion/framework-detection.js +182 -6
- package/dist/core/ingestion/heritage-processor.d.ts +13 -5
- package/dist/core/ingestion/heritage-processor.js +109 -55
- package/dist/core/ingestion/import-processor.d.ts +16 -20
- package/dist/core/ingestion/import-processor.js +199 -579
- package/dist/core/ingestion/language-config.d.ts +46 -0
- package/dist/core/ingestion/language-config.js +167 -0
- package/dist/core/ingestion/mro-processor.d.ts +45 -0
- package/dist/core/ingestion/mro-processor.js +369 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
- package/dist/core/ingestion/named-binding-extraction.js +363 -0
- package/dist/core/ingestion/parsing-processor.d.ts +4 -1
- package/dist/core/ingestion/parsing-processor.js +107 -109
- package/dist/core/ingestion/pipeline.d.ts +6 -3
- package/dist/core/ingestion/pipeline.js +208 -114
- package/dist/core/ingestion/process-processor.js +8 -2
- package/dist/core/ingestion/resolution-context.d.ts +53 -0
- package/dist/core/ingestion/resolution-context.js +132 -0
- package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
- package/dist/core/ingestion/resolvers/csharp.js +109 -0
- package/dist/core/ingestion/resolvers/go.d.ts +19 -0
- package/dist/core/ingestion/resolvers/go.js +42 -0
- package/dist/core/ingestion/resolvers/index.d.ts +18 -0
- package/dist/core/ingestion/resolvers/index.js +13 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
- package/dist/core/ingestion/resolvers/jvm.js +87 -0
- package/dist/core/ingestion/resolvers/php.d.ts +15 -0
- package/dist/core/ingestion/resolvers/php.js +35 -0
- package/dist/core/ingestion/resolvers/python.d.ts +19 -0
- package/dist/core/ingestion/resolvers/python.js +52 -0
- package/dist/core/ingestion/resolvers/ruby.d.ts +12 -0
- package/dist/core/ingestion/resolvers/ruby.js +15 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
- package/dist/core/ingestion/resolvers/rust.js +73 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
- package/dist/core/ingestion/resolvers/standard.js +123 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
- package/dist/core/ingestion/resolvers/utils.js +122 -0
- package/dist/core/ingestion/symbol-table.d.ts +21 -1
- package/dist/core/ingestion/symbol-table.js +40 -12
- package/dist/core/ingestion/tree-sitter-queries.d.ts +13 -10
- package/dist/core/ingestion/tree-sitter-queries.js +297 -7
- package/dist/core/ingestion/type-env.d.ts +49 -0
- package/dist/core/ingestion/type-env.js +611 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +385 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/csharp.js +383 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/go.js +467 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +22 -0
- package/dist/core/ingestion/type-extractors/index.js +31 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
- package/dist/core/ingestion/type-extractors/jvm.js +681 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/php.js +549 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/python.js +406 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/ruby.js +389 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/rust.js +449 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +133 -0
- package/dist/core/ingestion/type-extractors/shared.js +703 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/swift.js +137 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +127 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/typescript.js +494 -0
- package/dist/core/ingestion/utils.d.ts +103 -0
- package/dist/core/ingestion/utils.js +1085 -4
- package/dist/core/ingestion/workers/parse-worker.d.ts +51 -4
- package/dist/core/ingestion/workers/parse-worker.js +634 -222
- package/dist/core/ingestion/workers/worker-pool.js +8 -0
- package/dist/core/{kuzu → lbug}/csv-generator.d.ts +12 -10
- package/dist/core/{kuzu → lbug}/csv-generator.js +82 -101
- package/dist/core/{kuzu/kuzu-adapter.d.ts → lbug/lbug-adapter.d.ts} +20 -25
- package/dist/core/{kuzu/kuzu-adapter.js → lbug/lbug-adapter.js} +150 -122
- package/dist/core/{kuzu → lbug}/schema.d.ts +4 -4
- package/dist/core/{kuzu → lbug}/schema.js +23 -22
- package/dist/core/lbug/schema.test.d.ts +1 -0
- package/dist/core/search/bm25-index.d.ts +4 -4
- package/dist/core/search/bm25-index.js +12 -11
- package/dist/core/search/hybrid-search.d.ts +2 -2
- package/dist/core/search/hybrid-search.js +6 -6
- package/dist/core/tree-sitter/parser-loader.d.ts +1 -0
- package/dist/core/tree-sitter/parser-loader.js +19 -0
- package/dist/core/wiki/generator.d.ts +2 -2
- package/dist/core/wiki/generator.js +6 -6
- package/dist/core/wiki/graph-queries.d.ts +4 -4
- package/dist/core/wiki/graph-queries.js +7 -7
- package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
- package/dist/mcp/compatible-stdio-transport.js +200 -0
- package/dist/mcp/core/{kuzu-adapter.d.ts → lbug-adapter.d.ts} +11 -10
- package/dist/mcp/core/lbug-adapter.js +327 -0
- package/dist/mcp/local/local-backend.d.ts +21 -16
- package/dist/mcp/local/local-backend.js +306 -706
- package/dist/mcp/local/unity-parity-seed-loader.d.ts +6 -1
- package/dist/mcp/local/unity-parity-seed-loader.js +119 -9
- package/dist/mcp/local/unity-parity-seed-loader.test.js +95 -7
- package/dist/mcp/resources.js +2 -2
- package/dist/mcp/server.js +28 -13
- package/dist/mcp/staleness.js +2 -2
- package/dist/mcp/tools.js +12 -3
- package/dist/server/api.js +12 -12
- package/dist/server/mcp-http.d.ts +1 -1
- package/dist/server/mcp-http.js +1 -1
- package/dist/storage/git.js +4 -1
- package/dist/storage/repo-manager.d.ts +20 -2
- package/dist/storage/repo-manager.js +74 -4
- package/dist/types/pipeline.d.ts +1 -1
- package/hooks/claude/gitnexus-hook.cjs +149 -46
- package/hooks/claude/pre-tool-use.sh +2 -1
- package/hooks/claude/session-start.sh +0 -0
- package/package.json +20 -4
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/skills/gitnexus-cli.md +8 -8
- package/skills/gitnexus-debugging.md +1 -1
- package/skills/gitnexus-exploring.md +1 -1
- package/skills/gitnexus-guide.md +1 -1
- package/skills/gitnexus-impact-analysis.md +1 -1
- package/skills/gitnexus-pr-review.md +163 -0
- package/skills/gitnexus-refactoring.md +1 -1
- package/dist/cli/claude-hooks.d.ts +0 -22
- package/dist/cli/claude-hooks.js +0 -97
- package/dist/mcp/core/kuzu-adapter.js +0 -231
- /package/dist/core/{kuzu/csv-generator.test.d.ts → ingestion/type-extractors/types.js} +0 -0
- /package/dist/core/{kuzu/relationship-pair-buckets.test.d.ts → lbug/csv-generator.test.d.ts} +0 -0
- /package/dist/core/{kuzu → lbug}/csv-generator.test.js +0 -0
- /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.d.ts +0 -0
- /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.js +0 -0
- /package/dist/core/{kuzu/schema.test.d.ts → lbug/relationship-pair-buckets.test.d.ts} +0 -0
- /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.test.js +0 -0
- /package/dist/core/{kuzu → lbug}/schema.test.js +0 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
import { PipelineProgress, PipelineResult } from '../../types/pipeline.js';
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { PipelineProgress, PipelineResult, type PipelineRunOptions } from '../../types/pipeline.js';
|
|
2
|
+
export interface PipelineOptions extends PipelineRunOptions {
|
|
3
|
+
/** Skip MRO, community detection, and process extraction for faster test runs. */
|
|
4
|
+
skipGraphPhases?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare const runPipelineFromRepo: (repoPath: string, onProgress: (progress: PipelineProgress) => void, options?: PipelineOptions) => Promise<PipelineResult>;
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { createKnowledgeGraph } from '../graph/graph.js';
|
|
2
2
|
import { processStructure } from './structure-processor.js';
|
|
3
3
|
import { processParsing } from './parsing-processor.js';
|
|
4
|
-
import { processImports, processImportsFromExtracted,
|
|
5
|
-
import { processCalls, processCallsFromExtracted } from './call-processor.js';
|
|
4
|
+
import { processImports, processImportsFromExtracted, buildImportResolutionContext } from './import-processor.js';
|
|
5
|
+
import { processCalls, processCallsFromExtracted, processRoutesFromExtracted } from './call-processor.js';
|
|
6
6
|
import { processHeritage, processHeritageFromExtracted } from './heritage-processor.js';
|
|
7
|
+
import { computeMRO } from './mro-processor.js';
|
|
7
8
|
import { processCommunities } from './community-processor.js';
|
|
8
9
|
import { processProcesses } from './process-processor.js';
|
|
9
10
|
import { processUnityResources } from './unity-resource-processor.js';
|
|
10
|
-
import {
|
|
11
|
+
import { createResolutionContext } from './resolution-context.js';
|
|
11
12
|
import { createASTCache } from './ast-cache.js';
|
|
12
13
|
import { walkRepositoryPaths, readFileContents, walkUnityResourcePaths } from './filesystem-walker.js';
|
|
13
14
|
import { getLanguageFromFilename } from './utils.js';
|
|
15
|
+
import { isLanguageAvailable } from '../tree-sitter/parser-loader.js';
|
|
14
16
|
import { createWorkerPool } from './workers/worker-pool.js';
|
|
15
17
|
import { selectEntriesByScopeRules } from './scope-filter.js';
|
|
16
|
-
import
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
17
21
|
const isDev = process.env.NODE_ENV === 'development';
|
|
18
22
|
/** Max bytes of source content to load per parse chunk. Each chunk's source +
|
|
19
23
|
* parsed ASTs + extracted records + worker serialization overhead all live in
|
|
@@ -24,12 +28,12 @@ const CHUNK_BYTE_BUDGET = 20 * 1024 * 1024; // 20MB
|
|
|
24
28
|
const AST_CACHE_CAP = 50;
|
|
25
29
|
export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
26
30
|
const graph = createKnowledgeGraph();
|
|
27
|
-
const
|
|
31
|
+
const ctx = createResolutionContext();
|
|
32
|
+
const symbolTable = ctx.symbols;
|
|
28
33
|
let astCache = createASTCache(AST_CACHE_CAP);
|
|
29
|
-
const importMap = createImportMap();
|
|
30
34
|
const cleanup = () => {
|
|
31
35
|
astCache.clear();
|
|
32
|
-
|
|
36
|
+
ctx.clear();
|
|
33
37
|
};
|
|
34
38
|
try {
|
|
35
39
|
// ── Phase 1: Scan paths only (no content read) ─────────────────────
|
|
@@ -89,8 +93,30 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
89
93
|
// ── Phase 3+4: Chunked read + parse ────────────────────────────────
|
|
90
94
|
// Group parseable files into byte-budget chunks so only ~20MB of source
|
|
91
95
|
// is in memory at a time. Each chunk is: read → parse → extract → free.
|
|
92
|
-
const parseableScanned = extensionFiltered.filter(f =>
|
|
96
|
+
const parseableScanned = extensionFiltered.filter(f => {
|
|
97
|
+
const lang = getLanguageFromFilename(f.path);
|
|
98
|
+
return lang && isLanguageAvailable(lang);
|
|
99
|
+
});
|
|
100
|
+
// Warn about files skipped due to unavailable parsers
|
|
101
|
+
const skippedByLang = new Map();
|
|
102
|
+
for (const f of extensionFiltered) {
|
|
103
|
+
const lang = getLanguageFromFilename(f.path);
|
|
104
|
+
if (lang && !isLanguageAvailable(lang)) {
|
|
105
|
+
skippedByLang.set(lang, (skippedByLang.get(lang) || 0) + 1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const [lang, count] of skippedByLang) {
|
|
109
|
+
console.warn(`Skipping ${count} ${lang} file(s) — ${lang} parser not available (native binding may not have built). Try: npm rebuild tree-sitter-${lang}`);
|
|
110
|
+
}
|
|
93
111
|
const totalParseable = parseableScanned.length;
|
|
112
|
+
if (totalParseable === 0) {
|
|
113
|
+
onProgress({
|
|
114
|
+
phase: 'parsing',
|
|
115
|
+
percent: 82,
|
|
116
|
+
message: 'No parseable files found — skipping parsing phase',
|
|
117
|
+
stats: { filesProcessed: 0, totalFiles: 0, nodesCreated: graph.nodeCount },
|
|
118
|
+
});
|
|
119
|
+
}
|
|
94
120
|
// Build byte-budget chunks
|
|
95
121
|
const chunks = [];
|
|
96
122
|
let currentChunk = [];
|
|
@@ -117,14 +143,30 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
117
143
|
message: `Parsing ${totalParseable} files in ${numChunks} chunk${numChunks !== 1 ? 's' : ''}...`,
|
|
118
144
|
stats: { filesProcessed: 0, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
|
|
119
145
|
});
|
|
146
|
+
// Don't spawn workers for tiny repos — overhead exceeds benefit
|
|
147
|
+
const MIN_FILES_FOR_WORKERS = 15;
|
|
148
|
+
const MIN_BYTES_FOR_WORKERS = 512 * 1024;
|
|
149
|
+
const totalBytes = parseableScanned.reduce((s, f) => s + f.size, 0);
|
|
120
150
|
// Create worker pool once, reuse across chunks
|
|
121
151
|
let workerPool;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
152
|
+
if (totalParseable >= MIN_FILES_FOR_WORKERS || totalBytes >= MIN_BYTES_FOR_WORKERS) {
|
|
153
|
+
try {
|
|
154
|
+
let workerUrl = new URL('./workers/parse-worker.js', import.meta.url);
|
|
155
|
+
// When running under vitest, import.meta.url points to src/ where no .js exists.
|
|
156
|
+
// Fall back to the compiled dist/ worker so the pool can spawn real worker threads.
|
|
157
|
+
const thisDir = fileURLToPath(new URL('.', import.meta.url));
|
|
158
|
+
if (!fs.existsSync(fileURLToPath(workerUrl))) {
|
|
159
|
+
const distWorker = path.resolve(thisDir, '..', '..', '..', 'dist', 'core', 'ingestion', 'workers', 'parse-worker.js');
|
|
160
|
+
if (fs.existsSync(distWorker)) {
|
|
161
|
+
workerUrl = pathToFileURL(distWorker);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
workerPool = createWorkerPool(workerUrl);
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
if (isDev)
|
|
168
|
+
console.warn('Worker pool creation failed, using sequential fallback:', err.message);
|
|
169
|
+
}
|
|
128
170
|
}
|
|
129
171
|
let filesParsedSoFar = 0;
|
|
130
172
|
// AST cache sized for one chunk (sequential fallback uses it for import/call/heritage)
|
|
@@ -159,20 +201,53 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
159
201
|
stats: { filesProcessed: globalCurrent, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
|
|
160
202
|
});
|
|
161
203
|
}, workerPool);
|
|
204
|
+
const chunkBasePercent = 20 + ((filesParsedSoFar / totalParseable) * 62);
|
|
162
205
|
if (chunkWorkerData) {
|
|
163
206
|
// Imports
|
|
164
|
-
await processImportsFromExtracted(graph, allPathObjects, chunkWorkerData.imports,
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
207
|
+
await processImportsFromExtracted(graph, allPathObjects, chunkWorkerData.imports, ctx, (current, total) => {
|
|
208
|
+
onProgress({
|
|
209
|
+
phase: 'parsing',
|
|
210
|
+
percent: Math.round(chunkBasePercent),
|
|
211
|
+
message: `Resolving imports (chunk ${chunkIdx + 1}/${numChunks})...`,
|
|
212
|
+
detail: `${current}/${total} files`,
|
|
213
|
+
stats: { filesProcessed: filesParsedSoFar, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
|
|
214
|
+
});
|
|
215
|
+
}, repoPath, importCtx);
|
|
216
|
+
// Calls + Heritage + Routes — resolve in parallel (no shared mutable state between them)
|
|
217
|
+
// This is safe because each writes disjoint relationship types into idempotent id-keyed Maps,
|
|
218
|
+
// and the single-threaded event loop prevents races between synchronous addRelationship calls.
|
|
219
|
+
await Promise.all([
|
|
220
|
+
processCallsFromExtracted(graph, chunkWorkerData.calls, ctx, (current, total) => {
|
|
221
|
+
onProgress({
|
|
222
|
+
phase: 'parsing',
|
|
223
|
+
percent: Math.round(chunkBasePercent),
|
|
224
|
+
message: `Resolving calls (chunk ${chunkIdx + 1}/${numChunks})...`,
|
|
225
|
+
detail: `${current}/${total} files`,
|
|
226
|
+
stats: { filesProcessed: filesParsedSoFar, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
|
|
227
|
+
});
|
|
228
|
+
}, chunkWorkerData.constructorBindings),
|
|
229
|
+
processHeritageFromExtracted(graph, chunkWorkerData.heritage, ctx, (current, total) => {
|
|
230
|
+
onProgress({
|
|
231
|
+
phase: 'parsing',
|
|
232
|
+
percent: Math.round(chunkBasePercent),
|
|
233
|
+
message: `Resolving heritage (chunk ${chunkIdx + 1}/${numChunks})...`,
|
|
234
|
+
detail: `${current}/${total} records`,
|
|
235
|
+
stats: { filesProcessed: filesParsedSoFar, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
|
|
236
|
+
});
|
|
237
|
+
}),
|
|
238
|
+
processRoutesFromExtracted(graph, chunkWorkerData.routes ?? [], ctx, (current, total) => {
|
|
239
|
+
onProgress({
|
|
240
|
+
phase: 'parsing',
|
|
241
|
+
percent: Math.round(chunkBasePercent),
|
|
242
|
+
message: `Resolving routes (chunk ${chunkIdx + 1}/${numChunks})...`,
|
|
243
|
+
detail: `${current}/${total} routes`,
|
|
244
|
+
stats: { filesProcessed: filesParsedSoFar, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
|
|
245
|
+
});
|
|
246
|
+
}),
|
|
247
|
+
]);
|
|
173
248
|
}
|
|
174
249
|
else {
|
|
175
|
-
await processImports(graph, chunkFiles, astCache,
|
|
250
|
+
await processImports(graph, chunkFiles, astCache, ctx, undefined, repoPath, allPaths);
|
|
176
251
|
sequentialChunkPaths.push(chunkPaths);
|
|
177
252
|
}
|
|
178
253
|
filesParsedSoFar += chunkFiles.length;
|
|
@@ -191,116 +266,133 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
191
266
|
.filter(p => chunkContents.has(p))
|
|
192
267
|
.map(p => ({ path: p, content: chunkContents.get(p) }));
|
|
193
268
|
astCache = createASTCache(chunkFiles.length);
|
|
194
|
-
await processCalls(graph, chunkFiles, astCache,
|
|
195
|
-
await processHeritage(graph, chunkFiles, astCache,
|
|
269
|
+
const rubyHeritage = await processCalls(graph, chunkFiles, astCache, ctx);
|
|
270
|
+
await processHeritage(graph, chunkFiles, astCache, ctx);
|
|
271
|
+
if (rubyHeritage.length > 0) {
|
|
272
|
+
await processHeritageFromExtracted(graph, rubyHeritage, ctx);
|
|
273
|
+
}
|
|
196
274
|
astCache.clear();
|
|
197
275
|
}
|
|
276
|
+
// Log resolution cache stats
|
|
277
|
+
if (isDev) {
|
|
278
|
+
const rcStats = ctx.getStats();
|
|
279
|
+
const total = rcStats.cacheHits + rcStats.cacheMisses;
|
|
280
|
+
const hitRate = total > 0 ? ((rcStats.cacheHits / total) * 100).toFixed(1) : '0';
|
|
281
|
+
console.log(`🔍 Resolution cache: ${rcStats.cacheHits} hits, ${rcStats.cacheMisses} misses (${hitRate}% hit rate)`);
|
|
282
|
+
}
|
|
198
283
|
// Free import resolution context — suffix index + resolve cache no longer needed
|
|
199
284
|
// (allPathObjects and importCtx hold ~94MB+ for large repos)
|
|
200
285
|
allPathObjects.length = 0;
|
|
201
286
|
importCtx.resolveCache.clear();
|
|
202
287
|
importCtx.suffixIndex = null;
|
|
203
288
|
importCtx.normalizedFileList = null;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
289
|
+
let communityResult;
|
|
290
|
+
let processResult;
|
|
291
|
+
if (!options?.skipGraphPhases) {
|
|
292
|
+
// ── Phase 4.5: Method Resolution Order ──────────────────────────────
|
|
293
|
+
onProgress({
|
|
294
|
+
phase: 'parsing',
|
|
295
|
+
percent: 81,
|
|
296
|
+
message: 'Computing method resolution order...',
|
|
297
|
+
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
298
|
+
});
|
|
299
|
+
const mroResult = computeMRO(graph);
|
|
300
|
+
if (isDev && mroResult.entries.length > 0) {
|
|
301
|
+
console.log(`🔀 MRO: ${mroResult.entries.length} classes analyzed, ${mroResult.ambiguityCount} ambiguities found, ${mroResult.overrideEdges} OVERRIDES edges`);
|
|
209
302
|
}
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
// ── Phase 5: Communities ───────────────────────────────────────────
|
|
213
|
-
onProgress({
|
|
214
|
-
phase: 'communities',
|
|
215
|
-
percent: 82,
|
|
216
|
-
message: 'Detecting code communities...',
|
|
217
|
-
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
218
|
-
});
|
|
219
|
-
const communityResult = await processCommunities(graph, (message, progress) => {
|
|
220
|
-
const communityProgress = 82 + (progress * 0.10);
|
|
303
|
+
// ── Phase 5: Communities ───────────────────────────────────────────
|
|
221
304
|
onProgress({
|
|
222
305
|
phase: 'communities',
|
|
223
|
-
percent:
|
|
224
|
-
message,
|
|
306
|
+
percent: 82,
|
|
307
|
+
message: 'Detecting code communities...',
|
|
225
308
|
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
226
309
|
});
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
properties: {
|
|
236
|
-
name: comm.label,
|
|
237
|
-
filePath: '',
|
|
238
|
-
heuristicLabel: comm.heuristicLabel,
|
|
239
|
-
cohesion: comm.cohesion,
|
|
240
|
-
symbolCount: comm.symbolCount,
|
|
241
|
-
}
|
|
310
|
+
communityResult = await processCommunities(graph, (message, progress) => {
|
|
311
|
+
const communityProgress = 82 + (progress * 0.10);
|
|
312
|
+
onProgress({
|
|
313
|
+
phase: 'communities',
|
|
314
|
+
percent: Math.round(communityProgress),
|
|
315
|
+
message,
|
|
316
|
+
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
317
|
+
});
|
|
242
318
|
});
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
319
|
+
if (isDev) {
|
|
320
|
+
console.log(`🏘️ Community detection: ${communityResult.stats.totalCommunities} communities found (modularity: ${communityResult.stats.modularity.toFixed(3)})`);
|
|
321
|
+
}
|
|
322
|
+
communityResult.communities.forEach(comm => {
|
|
323
|
+
graph.addNode({
|
|
324
|
+
id: comm.id,
|
|
325
|
+
label: 'Community',
|
|
326
|
+
properties: {
|
|
327
|
+
name: comm.label,
|
|
328
|
+
filePath: '',
|
|
329
|
+
heuristicLabel: comm.heuristicLabel,
|
|
330
|
+
cohesion: comm.cohesion,
|
|
331
|
+
symbolCount: comm.symbolCount,
|
|
332
|
+
}
|
|
333
|
+
});
|
|
252
334
|
});
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const dynamicMaxProcesses = Math.max(20, Math.min(300, Math.round(symbolCount / 10)));
|
|
265
|
-
const processResult = await processProcesses(graph, communityResult.memberships, (message, progress) => {
|
|
266
|
-
const processProgress = 94 + (progress * 0.05);
|
|
335
|
+
communityResult.memberships.forEach(membership => {
|
|
336
|
+
graph.addRelationship({
|
|
337
|
+
id: `${membership.nodeId}_member_of_${membership.communityId}`,
|
|
338
|
+
type: 'MEMBER_OF',
|
|
339
|
+
sourceId: membership.nodeId,
|
|
340
|
+
targetId: membership.communityId,
|
|
341
|
+
confidence: 1.0,
|
|
342
|
+
reason: 'leiden-algorithm',
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
// ── Phase 6: Processes ─────────────────────────────────────────────
|
|
267
346
|
onProgress({
|
|
268
347
|
phase: 'processes',
|
|
269
|
-
percent:
|
|
270
|
-
message,
|
|
348
|
+
percent: 94,
|
|
349
|
+
message: 'Detecting execution flows...',
|
|
271
350
|
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
272
351
|
});
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
352
|
+
let symbolCount = 0;
|
|
353
|
+
graph.forEachNode(n => { if (n.label !== 'File')
|
|
354
|
+
symbolCount++; });
|
|
355
|
+
const dynamicMaxProcesses = Math.max(20, Math.min(300, Math.round(symbolCount / 10)));
|
|
356
|
+
processResult = await processProcesses(graph, communityResult.memberships, (message, progress) => {
|
|
357
|
+
const processProgress = 94 + (progress * 0.05);
|
|
358
|
+
onProgress({
|
|
359
|
+
phase: 'processes',
|
|
360
|
+
percent: Math.round(processProgress),
|
|
361
|
+
message,
|
|
362
|
+
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
363
|
+
});
|
|
364
|
+
}, { maxProcesses: dynamicMaxProcesses, minSteps: 3 });
|
|
365
|
+
if (isDev) {
|
|
366
|
+
console.log(`🔄 Process detection: ${processResult.stats.totalProcesses} processes found (${processResult.stats.crossCommunityCount} cross-community)`);
|
|
367
|
+
}
|
|
368
|
+
processResult.processes.forEach(proc => {
|
|
369
|
+
graph.addNode({
|
|
370
|
+
id: proc.id,
|
|
371
|
+
label: 'Process',
|
|
372
|
+
properties: {
|
|
373
|
+
name: proc.label,
|
|
374
|
+
filePath: '',
|
|
375
|
+
heuristicLabel: proc.heuristicLabel,
|
|
376
|
+
processType: proc.processType,
|
|
377
|
+
stepCount: proc.stepCount,
|
|
378
|
+
communities: proc.communities,
|
|
379
|
+
entryPointId: proc.entryPointId,
|
|
380
|
+
terminalId: proc.terminalId,
|
|
381
|
+
}
|
|
382
|
+
});
|
|
291
383
|
});
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
384
|
+
processResult.steps.forEach(step => {
|
|
385
|
+
graph.addRelationship({
|
|
386
|
+
id: `${step.nodeId}_step_${step.step}_${step.processId}`,
|
|
387
|
+
type: 'STEP_IN_PROCESS',
|
|
388
|
+
sourceId: step.nodeId,
|
|
389
|
+
targetId: step.processId,
|
|
390
|
+
confidence: 1.0,
|
|
391
|
+
reason: 'trace-detection',
|
|
392
|
+
step: step.step,
|
|
393
|
+
});
|
|
302
394
|
});
|
|
303
|
-
}
|
|
395
|
+
}
|
|
304
396
|
onProgress({
|
|
305
397
|
phase: 'enriching',
|
|
306
398
|
percent: 99,
|
|
@@ -311,7 +403,9 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
311
403
|
onProgress({
|
|
312
404
|
phase: 'complete',
|
|
313
405
|
percent: 100,
|
|
314
|
-
message:
|
|
406
|
+
message: communityResult && processResult
|
|
407
|
+
? `Graph complete! ${communityResult.stats.totalCommunities} communities, ${processResult.stats.totalProcesses} processes detected.`
|
|
408
|
+
: 'Graph complete! (graph phases skipped)',
|
|
315
409
|
stats: {
|
|
316
410
|
filesProcessed: totalFiles,
|
|
317
411
|
totalFiles,
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* Processes help agents understand how features work through the codebase.
|
|
11
11
|
*/
|
|
12
12
|
import { calculateEntryPointScore, isTestFile } from './entry-point-scoring.js';
|
|
13
|
+
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
13
14
|
const isDev = process.env.NODE_ENV === 'development';
|
|
14
15
|
const DEFAULT_CONFIG = {
|
|
15
16
|
maxTraceDepth: 10,
|
|
@@ -178,8 +179,14 @@ const findEntryPoints = (graph, reverseCallsEdges, callsEdges) => {
|
|
|
178
179
|
if (callees.length === 0)
|
|
179
180
|
continue;
|
|
180
181
|
// Calculate entry point score using new scoring system
|
|
181
|
-
const { score, reasons } = calculateEntryPointScore(node.properties.name, node.properties.language
|
|
182
|
+
const { score: baseScore, reasons } = calculateEntryPointScore(node.properties.name, node.properties.language ?? SupportedLanguages.JavaScript, node.properties.isExported ?? false, callers.length, callees.length, filePath // Pass filePath for framework detection
|
|
182
183
|
);
|
|
184
|
+
let score = baseScore;
|
|
185
|
+
const astFrameworkMultiplier = node.properties.astFrameworkMultiplier ?? 1.0;
|
|
186
|
+
if (astFrameworkMultiplier > 1.0) {
|
|
187
|
+
score *= astFrameworkMultiplier;
|
|
188
|
+
reasons.push(`framework-ast:${node.properties.astFrameworkReason || 'decorator'}`);
|
|
189
|
+
}
|
|
183
190
|
if (score > 0) {
|
|
184
191
|
entryPointCandidates.push({ id: node.id, score, reasons });
|
|
185
192
|
}
|
|
@@ -213,7 +220,6 @@ const traceFromEntryPoint = (entryId, callsEdges, config) => {
|
|
|
213
220
|
// BFS with path tracking
|
|
214
221
|
// Each queue item: [currentNodeId, pathSoFar]
|
|
215
222
|
const queue = [[entryId, [entryId]]];
|
|
216
|
-
const visited = new Set();
|
|
217
223
|
while (queue.length > 0 && traces.length < config.maxBranching * 3) {
|
|
218
224
|
const [currentId, path] = queue.shift();
|
|
219
225
|
// Get outgoing calls
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolution Context
|
|
3
|
+
*
|
|
4
|
+
* Single implementation of tiered name resolution. Replaces the duplicated
|
|
5
|
+
* tier-selection logic previously split between symbol-resolver.ts and
|
|
6
|
+
* call-processor.ts.
|
|
7
|
+
*
|
|
8
|
+
* Resolution tiers (highest confidence first):
|
|
9
|
+
* 1. Same file (lookupExactFull — authoritative)
|
|
10
|
+
* 2a-named. Named binding chain (walkBindingChain via NamedImportMap)
|
|
11
|
+
* 2a. Import-scoped (lookupFuzzy filtered by ImportMap)
|
|
12
|
+
* 2b. Package-scoped (lookupFuzzy filtered by PackageMap)
|
|
13
|
+
* 3. Global (all candidates — consumers must check candidate count)
|
|
14
|
+
*/
|
|
15
|
+
import type { SymbolTable, SymbolDefinition } from './symbol-table.js';
|
|
16
|
+
import type { NamedImportBinding } from './import-processor.js';
|
|
17
|
+
/** Resolution tier for tracking, logging, and test assertions. */
|
|
18
|
+
export type ResolutionTier = 'same-file' | 'import-scoped' | 'global';
|
|
19
|
+
/** Tier-selected candidates with metadata. */
|
|
20
|
+
export interface TieredCandidates {
|
|
21
|
+
readonly candidates: readonly SymbolDefinition[];
|
|
22
|
+
readonly tier: ResolutionTier;
|
|
23
|
+
}
|
|
24
|
+
/** Confidence scores per resolution tier. */
|
|
25
|
+
export declare const TIER_CONFIDENCE: Record<ResolutionTier, number>;
|
|
26
|
+
export type ImportMap = Map<string, Set<string>>;
|
|
27
|
+
export type PackageMap = Map<string, Set<string>>;
|
|
28
|
+
export type NamedImportMap = Map<string, Map<string, NamedImportBinding>>;
|
|
29
|
+
export interface ResolutionContext {
|
|
30
|
+
/**
|
|
31
|
+
* The only resolution API. Returns all candidates at the winning tier.
|
|
32
|
+
*
|
|
33
|
+
* Tier 3 ('global') returns ALL candidates regardless of count —
|
|
34
|
+
* consumers must check candidates.length and refuse ambiguous matches.
|
|
35
|
+
*/
|
|
36
|
+
resolve(name: string, fromFile: string): TieredCandidates | null;
|
|
37
|
+
/** Symbol table — used by parsing-processor to populate symbols. */
|
|
38
|
+
readonly symbols: SymbolTable;
|
|
39
|
+
/** Raw maps — used by import-processor to populate import data. */
|
|
40
|
+
readonly importMap: ImportMap;
|
|
41
|
+
readonly packageMap: PackageMap;
|
|
42
|
+
readonly namedImportMap: NamedImportMap;
|
|
43
|
+
enableCache(filePath: string): void;
|
|
44
|
+
clearCache(): void;
|
|
45
|
+
getStats(): {
|
|
46
|
+
fileCount: number;
|
|
47
|
+
globalSymbolCount: number;
|
|
48
|
+
cacheHits: number;
|
|
49
|
+
cacheMisses: number;
|
|
50
|
+
};
|
|
51
|
+
clear(): void;
|
|
52
|
+
}
|
|
53
|
+
export declare const createResolutionContext: () => ResolutionContext;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolution Context
|
|
3
|
+
*
|
|
4
|
+
* Single implementation of tiered name resolution. Replaces the duplicated
|
|
5
|
+
* tier-selection logic previously split between symbol-resolver.ts and
|
|
6
|
+
* call-processor.ts.
|
|
7
|
+
*
|
|
8
|
+
* Resolution tiers (highest confidence first):
|
|
9
|
+
* 1. Same file (lookupExactFull — authoritative)
|
|
10
|
+
* 2a-named. Named binding chain (walkBindingChain via NamedImportMap)
|
|
11
|
+
* 2a. Import-scoped (lookupFuzzy filtered by ImportMap)
|
|
12
|
+
* 2b. Package-scoped (lookupFuzzy filtered by PackageMap)
|
|
13
|
+
* 3. Global (all candidates — consumers must check candidate count)
|
|
14
|
+
*/
|
|
15
|
+
import { createSymbolTable } from './symbol-table.js';
|
|
16
|
+
import { isFileInPackageDir } from './import-processor.js';
|
|
17
|
+
import { walkBindingChain } from './named-binding-extraction.js';
|
|
18
|
+
/** Confidence scores per resolution tier. */
|
|
19
|
+
export const TIER_CONFIDENCE = {
|
|
20
|
+
'same-file': 0.95,
|
|
21
|
+
'import-scoped': 0.9,
|
|
22
|
+
'global': 0.5,
|
|
23
|
+
};
|
|
24
|
+
export const createResolutionContext = () => {
|
|
25
|
+
const symbols = createSymbolTable();
|
|
26
|
+
const importMap = new Map();
|
|
27
|
+
const packageMap = new Map();
|
|
28
|
+
const namedImportMap = new Map();
|
|
29
|
+
// Per-file cache state
|
|
30
|
+
let cacheFile = null;
|
|
31
|
+
let cache = null;
|
|
32
|
+
let cacheHits = 0;
|
|
33
|
+
let cacheMisses = 0;
|
|
34
|
+
// --- Core resolution (single implementation of tier logic) ---
|
|
35
|
+
const resolveUncached = (name, fromFile) => {
|
|
36
|
+
// Tier 1: Same file — authoritative match
|
|
37
|
+
const localDef = symbols.lookupExactFull(fromFile, name);
|
|
38
|
+
if (localDef) {
|
|
39
|
+
return { candidates: [localDef], tier: 'same-file' };
|
|
40
|
+
}
|
|
41
|
+
// Get all global definitions for subsequent tiers
|
|
42
|
+
const allDefs = symbols.lookupFuzzy(name);
|
|
43
|
+
// Tier 2a-named: Check named bindings BEFORE empty-allDefs early return
|
|
44
|
+
// because aliased imports mean lookupFuzzy('U') returns empty but we
|
|
45
|
+
// can resolve via the exported name.
|
|
46
|
+
const chainResult = walkBindingChain(name, fromFile, symbols, namedImportMap, allDefs);
|
|
47
|
+
if (chainResult && chainResult.length > 0) {
|
|
48
|
+
return { candidates: chainResult, tier: 'import-scoped' };
|
|
49
|
+
}
|
|
50
|
+
if (allDefs.length === 0)
|
|
51
|
+
return null;
|
|
52
|
+
// Tier 2a: Import-scoped — definition in a file imported by fromFile
|
|
53
|
+
const importedFiles = importMap.get(fromFile);
|
|
54
|
+
if (importedFiles) {
|
|
55
|
+
const importedDefs = allDefs.filter(def => importedFiles.has(def.filePath));
|
|
56
|
+
if (importedDefs.length > 0) {
|
|
57
|
+
return { candidates: importedDefs, tier: 'import-scoped' };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Tier 2b: Package-scoped — definition in a package dir imported by fromFile
|
|
61
|
+
const importedPackages = packageMap.get(fromFile);
|
|
62
|
+
if (importedPackages) {
|
|
63
|
+
const packageDefs = allDefs.filter(def => {
|
|
64
|
+
for (const dirSuffix of importedPackages) {
|
|
65
|
+
if (isFileInPackageDir(def.filePath, dirSuffix))
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
});
|
|
70
|
+
if (packageDefs.length > 0) {
|
|
71
|
+
return { candidates: packageDefs, tier: 'import-scoped' };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Tier 3: Global — pass all candidates through.
|
|
75
|
+
// Consumers must check candidate count and refuse ambiguous matches.
|
|
76
|
+
return { candidates: allDefs, tier: 'global' };
|
|
77
|
+
};
|
|
78
|
+
const resolve = (name, fromFile) => {
|
|
79
|
+
// Check cache (only when enabled AND fromFile matches cached file)
|
|
80
|
+
if (cache && cacheFile === fromFile) {
|
|
81
|
+
if (cache.has(name)) {
|
|
82
|
+
cacheHits++;
|
|
83
|
+
return cache.get(name);
|
|
84
|
+
}
|
|
85
|
+
cacheMisses++;
|
|
86
|
+
}
|
|
87
|
+
const result = resolveUncached(name, fromFile);
|
|
88
|
+
// Store in cache if active and file matches
|
|
89
|
+
if (cache && cacheFile === fromFile) {
|
|
90
|
+
cache.set(name, result);
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
};
|
|
94
|
+
// --- Cache lifecycle ---
|
|
95
|
+
const enableCache = (filePath) => {
|
|
96
|
+
cacheFile = filePath;
|
|
97
|
+
if (!cache)
|
|
98
|
+
cache = new Map();
|
|
99
|
+
else
|
|
100
|
+
cache.clear();
|
|
101
|
+
};
|
|
102
|
+
const clearCache = () => {
|
|
103
|
+
cacheFile = null;
|
|
104
|
+
// Reuse the Map instance — just clear entries to reduce GC pressure at scale.
|
|
105
|
+
cache?.clear();
|
|
106
|
+
};
|
|
107
|
+
const getStats = () => ({
|
|
108
|
+
...symbols.getStats(),
|
|
109
|
+
cacheHits,
|
|
110
|
+
cacheMisses,
|
|
111
|
+
});
|
|
112
|
+
const clear = () => {
|
|
113
|
+
symbols.clear();
|
|
114
|
+
importMap.clear();
|
|
115
|
+
packageMap.clear();
|
|
116
|
+
namedImportMap.clear();
|
|
117
|
+
clearCache();
|
|
118
|
+
cacheHits = 0;
|
|
119
|
+
cacheMisses = 0;
|
|
120
|
+
};
|
|
121
|
+
return {
|
|
122
|
+
resolve,
|
|
123
|
+
symbols,
|
|
124
|
+
importMap,
|
|
125
|
+
packageMap,
|
|
126
|
+
namedImportMap,
|
|
127
|
+
enableCache,
|
|
128
|
+
clearCache,
|
|
129
|
+
getStats,
|
|
130
|
+
clear,
|
|
131
|
+
};
|
|
132
|
+
};
|