codesearch-mcp 0.2.3-alpha → 0.3.0-alpha
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/CHANGELOG.md +31 -1
- package/README.md +46 -26
- package/dist/app.js +5 -19
- package/dist/src/chunking/AstChunker.d.ts +12 -0
- package/dist/src/chunking/AstChunker.js +30 -0
- package/dist/src/chunking/CharacterChunkStrategy.d.ts +8 -0
- package/dist/src/chunking/CharacterChunkStrategy.js +15 -0
- package/dist/src/chunking/DeclarationChunkStrategy.d.ts +15 -0
- package/dist/src/chunking/DeclarationChunkStrategy.js +87 -0
- package/dist/src/chunking/FragmentMerger.d.ts +11 -0
- package/dist/src/chunking/FragmentMerger.js +61 -0
- package/dist/src/chunking/ImportOnlyFilter.d.ts +5 -0
- package/dist/src/chunking/ImportOnlyFilter.js +14 -0
- package/dist/src/chunking/LangchainTextSplitter.d.ts +21 -0
- package/dist/src/chunking/LangchainTextSplitter.js +133 -0
- package/dist/src/chunking/MinSizeSplitPolicy.d.ts +9 -0
- package/dist/src/chunking/MinSizeSplitPolicy.js +18 -0
- package/dist/src/chunking/RunPacker.d.ts +12 -0
- package/dist/src/chunking/RunPacker.js +84 -0
- package/dist/src/chunking/create-chunker.d.ts +3 -0
- package/dist/src/chunking/create-chunker.js +15 -0
- package/dist/src/chunking/interfaces.d.ts +27 -0
- package/dist/src/chunking/interfaces.js +2 -0
- package/dist/src/commands/ConfigCommand.js +1 -9
- package/dist/src/commands/EvalCommand.d.ts +4 -3
- package/dist/src/commands/EvalCommand.js +85 -52
- package/dist/src/commands/IndexCommand.js +0 -22
- package/dist/src/commands/McpCommand.js +2 -8
- package/dist/src/commands/WebCommand.js +2 -5
- package/dist/src/config-error.d.ts +0 -4
- package/dist/src/config-error.js +0 -4
- package/dist/src/config-store.d.ts +1 -54
- package/dist/src/config-store.js +8 -69
- package/dist/src/config.d.ts +8 -17
- package/dist/src/config.js +35 -34
- package/dist/src/constants.d.ts +2 -28
- package/dist/src/constants.js +6 -28
- package/dist/src/container.d.ts +6 -0
- package/dist/src/container.js +12 -3
- package/dist/src/db/schema.d.ts +17 -0
- package/dist/src/db/schema.js +1 -6
- package/dist/src/declarations/DeclarationTree.d.ts +19 -0
- package/dist/src/declarations/DeclarationTree.js +66 -0
- package/dist/src/declarations/DeclarationTreeBuilder.d.ts +22 -0
- package/dist/src/declarations/DeclarationTreeBuilder.js +179 -0
- package/dist/src/declarations/SourceLines.d.ts +37 -0
- package/dist/src/declarations/SourceLines.js +415 -0
- package/dist/src/dependency-traversal.d.ts +0 -6
- package/dist/src/dependency-traversal.js +0 -6
- package/dist/src/enriched-prompt.d.ts +0 -6
- package/dist/src/enriched-prompt.js +0 -6
- package/dist/src/error-record.d.ts +12 -0
- package/dist/src/error-record.js +248 -0
- package/dist/src/eval-corpus.d.ts +16 -0
- package/dist/src/eval-corpus.js +169 -0
- package/dist/src/eval-results.d.ts +6 -3
- package/dist/src/eval-results.js +309 -26
- package/dist/src/file-utils.d.ts +0 -5
- package/dist/src/file-utils.js +0 -5
- package/dist/src/graph.d.ts +0 -25
- package/dist/src/graph.js +0 -28
- package/dist/src/hnsw-settings.d.ts +3 -0
- package/dist/src/hnsw-settings.js +24 -0
- package/dist/src/interfaces/IAstParser.d.ts +2 -39
- package/dist/src/interfaces/IChunker.d.ts +3 -12
- package/dist/src/interfaces/ICorpusInspector.d.ts +7 -0
- package/dist/src/interfaces/ICorpusInspector.js +2 -0
- package/dist/src/interfaces/IDatabase.d.ts +2 -22
- package/dist/src/interfaces/IEmbedder.d.ts +0 -9
- package/dist/src/interfaces/ILogger.d.ts +1 -11
- package/dist/src/interfaces/IMcpToolHandler.d.ts +0 -5
- package/dist/src/interfaces/IMonorepoResolver.d.ts +0 -22
- package/dist/src/interfaces/IProjectDiscovery.d.ts +0 -17
- package/dist/src/interfaces/IRetrievalEvaluator.d.ts +134 -12
- package/dist/src/interfaces/ISearcher.d.ts +16 -5
- package/dist/src/interrupt.js +0 -2
- package/dist/src/owner-path.d.ts +9 -10
- package/dist/src/owner-path.js +11 -23
- package/dist/src/project-scope.d.ts +0 -12
- package/dist/src/project-scope.js +0 -12
- package/dist/src/project-selector.d.ts +0 -5
- package/dist/src/project-selector.js +0 -11
- package/dist/src/retrieval-eval.d.ts +14 -6
- package/dist/src/retrieval-eval.js +345 -59
- package/dist/src/search/result-shaping.d.ts +4 -0
- package/dist/src/search/result-shaping.js +19 -0
- package/dist/src/services/AstParserService.d.ts +2 -62
- package/dist/src/services/AstParserService.js +25 -137
- package/dist/src/services/DatabaseService.d.ts +2 -7
- package/dist/src/services/DatabaseService.js +42 -29
- package/dist/src/services/EmbedderFactory.d.ts +0 -4
- package/dist/src/services/EmbedderFactory.js +0 -5
- package/dist/src/services/IndexerService.d.ts +0 -16
- package/dist/src/services/IndexerService.js +16 -45
- package/dist/src/services/InfinityEmbedder.d.ts +0 -21
- package/dist/src/services/InfinityEmbedder.js +0 -15
- package/dist/src/services/LoggerService.d.ts +7 -5
- package/dist/src/services/LoggerService.js +37 -41
- package/dist/src/services/McpServer.d.ts +22 -14
- package/dist/src/services/McpServer.js +70 -37
- package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +1 -45
- package/dist/src/services/MonorepoAwareDiscoveryService.js +1 -51
- package/dist/src/services/NxMonorepoResolver.d.ts +0 -29
- package/dist/src/services/NxMonorepoResolver.js +5 -56
- package/dist/src/services/OpenAICompatibleEmbedder.d.ts +0 -6
- package/dist/src/services/OpenAICompatibleEmbedder.js +0 -14
- package/dist/src/services/ProjectDiscoveryService.d.ts +0 -4
- package/dist/src/services/ProjectDiscoveryService.js +0 -4
- package/dist/src/services/SearcherService.d.ts +9 -116
- package/dist/src/services/SearcherService.js +95 -176
- package/dist/src/services/VoyageEmbedder.d.ts +0 -6
- package/dist/src/services/VoyageEmbedder.js +0 -6
- package/dist/src/services/WebServerService.d.ts +1 -53
- package/dist/src/services/WebServerService.js +11 -57
- package/dist/src/services/http-retry.d.ts +0 -7
- package/dist/src/services/http-retry.js +1 -10
- package/dist/src/services/infinity-provisioning.d.ts +0 -74
- package/dist/src/services/infinity-provisioning.js +5 -88
- package/dist/src/services/legacy-ollama-index.d.ts +0 -16
- package/dist/src/services/legacy-ollama-index.js +0 -23
- package/dist/src/services/reranker.d.ts +0 -9
- package/dist/src/services/reranker.js +46 -12
- package/dist/src/setup.d.ts +11 -23
- package/dist/src/setup.js +98 -71
- package/dist/src/symbol-name.d.ts +0 -12
- package/dist/src/symbol-name.js +0 -13
- package/dist/src/symbol-rows.d.ts +0 -14
- package/dist/src/symbol-rows.js +0 -14
- package/dist/src/tool-call-log.d.ts +2 -0
- package/dist/src/tool-call-log.js +24 -0
- package/dist/src/tools/GetDependenciesTool.js +1 -1
- package/dist/src/tools/GetDependentsTool.js +1 -1
- package/dist/src/tools/SearchCodebaseTool.js +6 -3
- package/dist/src/tools/format-helpers.d.ts +0 -6
- package/dist/src/tools/format-helpers.js +87 -14
- package/dist/src/web/dashboard.d.ts +0 -8
- package/dist/src/web/dashboard.js +7 -10
- package/package.json +2 -2
- package/dist/src/services/AstChunker.d.ts +0 -42
- package/dist/src/services/AstChunker.js +0 -158
- package/docs/web-dashboard.md +0 -206
|
@@ -37,6 +37,8 @@ exports.AstParserService = void 0;
|
|
|
37
37
|
const path = __importStar(require("path"));
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const ts_morph_1 = require("ts-morph");
|
|
40
|
+
const DeclarationTreeBuilder_1 = require("../declarations/DeclarationTreeBuilder");
|
|
41
|
+
const DeclarationTree_1 = require("../declarations/DeclarationTree");
|
|
40
42
|
const LoggerService_1 = require("./LoggerService");
|
|
41
43
|
const GRAPHQL_MODULE = '@nestjs/graphql';
|
|
42
44
|
const GRAPHQL_TYPE_DECORATORS = new Set(['ObjectType', 'InputType', 'InterfaceType']);
|
|
@@ -55,11 +57,11 @@ class AstParserService {
|
|
|
55
57
|
logger;
|
|
56
58
|
deps;
|
|
57
59
|
morphProjects = new Map();
|
|
60
|
+
declarationBuilder = new DeclarationTreeBuilder_1.DeclarationTreeBuilder();
|
|
58
61
|
constructor(logger = LoggerService_1.LoggerService.silent(), deps = {}) {
|
|
59
62
|
this.logger = logger;
|
|
60
63
|
this.deps = deps;
|
|
61
64
|
}
|
|
62
|
-
// ── public interface ────────────────────────────────────────────────────────
|
|
63
65
|
async analyzeProject(filePaths, serviceName, projectRoot = process.cwd(), opts = {}) {
|
|
64
66
|
const t0 = Date.now();
|
|
65
67
|
const skipDependencyResolution = opts.skipDependencyResolution ?? false;
|
|
@@ -76,7 +78,7 @@ class AstParserService {
|
|
|
76
78
|
morphProject.addSourceFileAtPath(filePath);
|
|
77
79
|
}
|
|
78
80
|
catch (err) {
|
|
79
|
-
this.logger.warn('AstParser: failed to add source file', { filePath,
|
|
81
|
+
this.logger.warn('AstParser: failed to add source file', { filePath, err });
|
|
80
82
|
analysisMap.set(filePath, { edges: [], symbols: [], calls: [], members: [], graphqlNames: [] });
|
|
81
83
|
}
|
|
82
84
|
}
|
|
@@ -84,7 +86,6 @@ class AstParserService {
|
|
|
84
86
|
const filePath = sourceFile.getFilePath();
|
|
85
87
|
analysisMap.set(filePath, this.analyzeSourceFile(sourceFile, filePath, serviceName));
|
|
86
88
|
}
|
|
87
|
-
// Release source file memory before returning
|
|
88
89
|
for (const sourceFile of morphProject.getSourceFiles()) {
|
|
89
90
|
morphProject.removeSourceFile(sourceFile);
|
|
90
91
|
}
|
|
@@ -120,7 +121,7 @@ class AstParserService {
|
|
|
120
121
|
sourceFile = morphProject.addSourceFileAtPath(filePath);
|
|
121
122
|
}
|
|
122
123
|
catch (err) {
|
|
123
|
-
this.logger.warn('AstParser: analyzeFile failed to add source file', { filePath,
|
|
124
|
+
this.logger.warn('AstParser: analyzeFile failed to add source file', { filePath, err, ms: Date.now() - t0 });
|
|
124
125
|
return { edges: [], symbols: [], calls: [], members: [], graphqlNames: [] };
|
|
125
126
|
}
|
|
126
127
|
const result = this.analyzeSourceFile(sourceFile, filePath, serviceName);
|
|
@@ -130,7 +131,6 @@ class AstParserService {
|
|
|
130
131
|
});
|
|
131
132
|
return result;
|
|
132
133
|
}
|
|
133
|
-
// Kept for test compatibility (extract-import-edges.test.ts).
|
|
134
134
|
async extractImportEdges(filePath, serviceName, projectRoot) {
|
|
135
135
|
const { edges } = await this.analyzeFile(filePath, serviceName, projectRoot);
|
|
136
136
|
return edges;
|
|
@@ -140,54 +140,37 @@ class AstParserService {
|
|
|
140
140
|
const edges = this.extractEdges(sourceFile, filePath, serviceName);
|
|
141
141
|
const symbolCandidates = this.extractSymbols(sourceFile, filePath, serviceName);
|
|
142
142
|
const symbols = symbolCandidates.map(c => c.entry);
|
|
143
|
-
// Member extraction (classes anywhere, class/interface/object-literal
|
|
144
|
-
// members, namespace-level declarations) is isolated in its own try: a
|
|
145
|
-
// bug there must never change a file's top-level symbols, chunks or
|
|
146
|
-
// embedding prompt (see the `symbol-index-members` retrieval-invariance
|
|
147
|
-
// requirement).
|
|
148
143
|
let memberCandidates = [];
|
|
149
144
|
try {
|
|
150
145
|
memberCandidates = this.extractMembers(sourceFile, filePath, serviceName);
|
|
151
146
|
}
|
|
152
147
|
catch (err) {
|
|
153
|
-
this.logger.warn('AstParser: member extraction failed', { filePath,
|
|
148
|
+
this.logger.warn('AstParser: member extraction failed', { filePath, err });
|
|
154
149
|
memberCandidates = [];
|
|
155
150
|
}
|
|
156
151
|
const members = memberCandidates.map(c => c.entry);
|
|
157
152
|
const calls = this.extractCalls(sourceFile, filePath, serviceName);
|
|
158
|
-
const
|
|
159
|
-
// GraphQL-named rows are isolated like members: a bug here must never
|
|
160
|
-
// change a file's symbols, members, chunks, ownerPath or call graph.
|
|
161
|
-
// Runs after member extraction (decision 6 of `graphql-symbols`) — a
|
|
162
|
-
// GraphQL row is written only when the decorated declaration's own row
|
|
163
|
-
// was written to code_symbol.
|
|
153
|
+
const declarations = this.buildDeclarations(sourceFile, filePath, symbolCandidates, memberCandidates);
|
|
164
154
|
let graphqlNames = [];
|
|
165
155
|
try {
|
|
166
156
|
graphqlNames = this.extractGraphqlNames(sourceFile, filePath, serviceName, [...symbolCandidates, ...memberCandidates]);
|
|
167
157
|
}
|
|
168
158
|
catch (err) {
|
|
169
|
-
this.logger.warn('AstParser: GraphQL extraction failed', { filePath,
|
|
159
|
+
this.logger.warn('AstParser: GraphQL extraction failed', { filePath, err });
|
|
170
160
|
graphqlNames = [];
|
|
171
161
|
}
|
|
172
|
-
return { edges, symbols, members, calls,
|
|
162
|
+
return { edges, symbols, members, calls, declarations, graphqlNames };
|
|
173
163
|
}
|
|
174
164
|
catch (err) {
|
|
175
|
-
this.logger.warn('AstParser: failed to analyze file', { filePath,
|
|
165
|
+
this.logger.warn('AstParser: failed to analyze file', { filePath, err });
|
|
176
166
|
return { edges: [], symbols: [], calls: [], members: [], graphqlNames: [] };
|
|
177
167
|
}
|
|
178
168
|
}
|
|
179
|
-
// ── private: MorphProject cache (LRU, max MORPH_CACHE_MAX entries) ──────────
|
|
180
169
|
createMorphProject(projectRoot, skipFileDependencyResolution = false, rootTsConfigPath) {
|
|
181
170
|
const t0 = Date.now();
|
|
182
|
-
// Determine which tsconfig to use:
|
|
183
|
-
// 1. Project-local tsconfig.json
|
|
184
|
-
// 2. Root tsconfig (e.g. tsconfig.base.json for Nx monorepos)
|
|
185
|
-
// 3. Fallback options
|
|
186
171
|
const localTsconfigPath = path.join(projectRoot, 'tsconfig.json');
|
|
187
172
|
const hasLocalTsconfig = fs.existsSync(localTsconfigPath);
|
|
188
173
|
const hasRootTsconfig = rootTsConfigPath ? fs.existsSync(rootTsConfigPath) : false;
|
|
189
|
-
// For monorepos: prefer the root tsconfig.base.json which contains
|
|
190
|
-
// workspace-wide path aliases (e.g. @scope/* → libs/*/src)
|
|
191
174
|
const effectiveTsconfig = hasRootTsconfig ? rootTsConfigPath : (hasLocalTsconfig ? localTsconfigPath : null);
|
|
192
175
|
this.logger.debug('AstParser: tsconfig lookup', {
|
|
193
176
|
projectRoot,
|
|
@@ -219,7 +202,7 @@ class AstParserService {
|
|
|
219
202
|
catch (err) {
|
|
220
203
|
this.logger.warn('AstParser: tsconfig load failed, using fallback', {
|
|
221
204
|
tsconfigPath: effectiveTsconfig ?? localTsconfigPath,
|
|
222
|
-
|
|
205
|
+
err,
|
|
223
206
|
ms: Date.now() - t0,
|
|
224
207
|
});
|
|
225
208
|
project = new ts_morph_1.Project(FALLBACK_OPTS);
|
|
@@ -228,7 +211,6 @@ class AstParserService {
|
|
|
228
211
|
}
|
|
229
212
|
getMorphProject(projectRoot) {
|
|
230
213
|
if (this.morphProjects.has(projectRoot)) {
|
|
231
|
-
// Move to end = most recently used
|
|
232
214
|
const cached = this.morphProjects.get(projectRoot);
|
|
233
215
|
this.morphProjects.delete(projectRoot);
|
|
234
216
|
this.morphProjects.set(projectRoot, cached);
|
|
@@ -236,7 +218,6 @@ class AstParserService {
|
|
|
236
218
|
return cached;
|
|
237
219
|
}
|
|
238
220
|
if (this.morphProjects.size >= MORPH_CACHE_MAX) {
|
|
239
|
-
// Evict least recently used (first entry in insertion-order Map)
|
|
240
221
|
const lruKey = this.morphProjects.keys().next().value;
|
|
241
222
|
this.morphProjects.delete(lruKey);
|
|
242
223
|
this.logger.debug('AstParser: evicted LRU project', { lruKey });
|
|
@@ -246,7 +227,6 @@ class AstParserService {
|
|
|
246
227
|
this.morphProjects.set(projectRoot, project);
|
|
247
228
|
return project;
|
|
248
229
|
}
|
|
249
|
-
// ── private: edge extraction (imports + re-exports) ────────────────────────
|
|
250
230
|
extractEdges(sourceFile, filePath, serviceName) {
|
|
251
231
|
const edges = [];
|
|
252
232
|
const usageMap = this.buildUsageMap(sourceFile);
|
|
@@ -257,9 +237,6 @@ class AstParserService {
|
|
|
257
237
|
const hasClause = Boolean(defaultImport || namespaceImport || namedImports.length > 0);
|
|
258
238
|
const resolved = decl.getModuleSpecifierSourceFile();
|
|
259
239
|
let toFile = resolved ? resolved.getFilePath() : null;
|
|
260
|
-
// A bindingless specifier never resolves through ts-morph's bound symbol
|
|
261
|
-
// (nothing is bound), even for a real project file — fall back to
|
|
262
|
-
// TypeScript's own resolver and map the result back onto the project.
|
|
263
240
|
if (!toFile && !hasClause) {
|
|
264
241
|
toFile = this.resolveSideEffectModuleSpecifier(decl, sourceFile, filePath);
|
|
265
242
|
}
|
|
@@ -328,9 +305,6 @@ class AstParserService {
|
|
|
328
305
|
}
|
|
329
306
|
return edges;
|
|
330
307
|
}
|
|
331
|
-
/** Manual fallback for a bindingless import specifier (design.md decision 2) —
|
|
332
|
-
* ts-morph's `getModuleSpecifierSourceFile()` is `null` for these even when
|
|
333
|
-
* the target is a real project file, since nothing is bound to resolve through. */
|
|
334
308
|
resolveSideEffectModuleSpecifier(decl, sourceFile, filePath) {
|
|
335
309
|
const specifier = decl.getModuleSpecifierValue();
|
|
336
310
|
const compilerOptions = sourceFile.getProject().getCompilerOptions();
|
|
@@ -349,7 +323,6 @@ class AstParserService {
|
|
|
349
323
|
const containers = [
|
|
350
324
|
...sourceFile.getClasses().flatMap(n => n.getName() ? [{ name: n.getName(), node: n }] : []),
|
|
351
325
|
...sourceFile.getFunctions().flatMap(n => n.getName() ? [{ name: n.getName(), node: n }] : []),
|
|
352
|
-
// include top-level variable declarations so arrow function exports are tracked for fromSymbol
|
|
353
326
|
...sourceFile.getVariableStatements()
|
|
354
327
|
.flatMap(stmt => stmt.getDeclarations())
|
|
355
328
|
.flatMap(n => {
|
|
@@ -372,20 +345,15 @@ class AstParserService {
|
|
|
372
345
|
}
|
|
373
346
|
return map;
|
|
374
347
|
}
|
|
375
|
-
// ── private: position / signature helpers ───────────────────────────────────
|
|
376
348
|
getPos(sourceFile, node) {
|
|
377
349
|
const { line, character } = sourceFile.compilerNode
|
|
378
350
|
.getLineAndCharacterOfPosition(node.getStart());
|
|
379
351
|
return { line: line + 1, col: character + 1 };
|
|
380
352
|
}
|
|
381
|
-
/** Trimmed text of the source line containing `node`'s start — the signature
|
|
382
|
-
* rule for every kind (name node for members, the position node for
|
|
383
|
-
* constructors/anonymous default classes). */
|
|
384
353
|
lineTextAt(sourceLines, sourceFile, node) {
|
|
385
354
|
const { line } = this.getPos(sourceFile, node);
|
|
386
355
|
return (sourceLines[line - 1] ?? node.getText().split('\n')[0]).trim();
|
|
387
356
|
}
|
|
388
|
-
// ── private: top-level symbol extraction (feeds the chunker + prompt) ──────
|
|
389
357
|
extractSymbols(sourceFile, filePath, serviceName) {
|
|
390
358
|
const candidates = [];
|
|
391
359
|
const sourceLines = sourceFile.getFullText().split('\n');
|
|
@@ -453,8 +421,6 @@ class AstParserService {
|
|
|
453
421
|
const nameNode = decl.getNameNode();
|
|
454
422
|
const { line, col } = this.getPos(sourceFile, nameNode);
|
|
455
423
|
candidates.push({
|
|
456
|
-
// Owner ranges use the declarator's own span, not the statement's —
|
|
457
|
-
// each declarator of `const a = 1, b = () => {…}` owns only its lines.
|
|
458
424
|
declNode: decl,
|
|
459
425
|
entry: {
|
|
460
426
|
project: serviceName, filePath, name: decl.getName(), kind: 'variable',
|
|
@@ -467,7 +433,6 @@ class AstParserService {
|
|
|
467
433
|
}
|
|
468
434
|
return candidates;
|
|
469
435
|
}
|
|
470
|
-
// ── private: member extraction (classes anywhere, members, namespace body) ─
|
|
471
436
|
extractMembers(sourceFile, filePath, serviceName) {
|
|
472
437
|
const candidates = [];
|
|
473
438
|
const sourceLines = sourceFile.getFullText().split('\n');
|
|
@@ -485,14 +450,10 @@ class AstParserService {
|
|
|
485
450
|
},
|
|
486
451
|
});
|
|
487
452
|
};
|
|
488
|
-
// Classes anywhere in the file — nested, expression, anonymous default.
|
|
489
453
|
for (const cls of [
|
|
490
454
|
...sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.ClassDeclaration),
|
|
491
455
|
...sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.ClassExpression),
|
|
492
456
|
]) {
|
|
493
|
-
// Named top-level classes are already handled by extractSymbols; an
|
|
494
|
-
// anonymous top-level class (`export default class {}`) is not — it
|
|
495
|
-
// must still be collected here.
|
|
496
457
|
if (ts_morph_1.Node.isClassDeclaration(cls) && cls.getName() && ts_morph_1.Node.isSourceFile(cls.getParent()))
|
|
497
458
|
continue;
|
|
498
459
|
const described = this.describeIndexedNode(cls);
|
|
@@ -501,7 +462,6 @@ class AstParserService {
|
|
|
501
462
|
const posNode = (ts_morph_1.Node.isClassDeclaration(cls) ? cls.getNameNode() : undefined) ?? cls;
|
|
502
463
|
push(cls, posNode, described.name, 'class', described.container, false, true);
|
|
503
464
|
}
|
|
504
|
-
// Class / object-literal methods, accessors, constructors, function properties.
|
|
505
465
|
for (const m of sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.MethodDeclaration)) {
|
|
506
466
|
const described = this.describeIndexedNode(m);
|
|
507
467
|
if (!described)
|
|
@@ -539,7 +499,6 @@ class AstParserService {
|
|
|
539
499
|
continue;
|
|
540
500
|
push(p, p.getNameNode(), described.name, 'function_property', described.container, false, true);
|
|
541
501
|
}
|
|
542
|
-
// Interface / type-literal method and function-typed property signatures.
|
|
543
502
|
for (const s of sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.MethodSignature)) {
|
|
544
503
|
const described = this.describeIndexedNode(s);
|
|
545
504
|
if (!described)
|
|
@@ -552,7 +511,6 @@ class AstParserService {
|
|
|
552
511
|
continue;
|
|
553
512
|
push(s, s.getNameNode(), described.name, 'method_signature', described.container, false, false);
|
|
554
513
|
}
|
|
555
|
-
// Declarations directly in a namespace body.
|
|
556
514
|
for (const fn of sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.FunctionDeclaration)) {
|
|
557
515
|
if (!ts_morph_1.Node.isModuleBlock(fn.getParent()))
|
|
558
516
|
continue;
|
|
@@ -592,24 +550,20 @@ class AstParserService {
|
|
|
592
550
|
}
|
|
593
551
|
return candidates;
|
|
594
552
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
553
|
+
buildDeclarations(sourceFile, filePath, symbolCandidates, memberCandidates) {
|
|
554
|
+
try {
|
|
555
|
+
return this.declarationBuilder.build(sourceFile, [...symbolCandidates, ...memberCandidates]);
|
|
556
|
+
}
|
|
557
|
+
catch (err) {
|
|
558
|
+
this.logger.warn('AstParser: declaration tree build failed', { filePath, err });
|
|
559
|
+
try {
|
|
560
|
+
return this.declarationBuilder.flat(symbolCandidates);
|
|
561
|
+
}
|
|
562
|
+
catch {
|
|
563
|
+
return DeclarationTree_1.DeclarationTree.EMPTY;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
608
566
|
}
|
|
609
|
-
// ── private: GraphQL-named rows (graphql-symbols) ───────────────────────────
|
|
610
|
-
/** Local name → the name exported by `@nestjs/graphql`, for every named import
|
|
611
|
-
* of that module specifier (aliases followed). Recognition is import-based,
|
|
612
|
-
* not type-checker-based, so `skipFileDependencyResolution` batches agree. */
|
|
613
567
|
buildGraphqlImportMap(sourceFile) {
|
|
614
568
|
const map = new Map();
|
|
615
569
|
for (const decl of sourceFile.getImportDeclarations()) {
|
|
@@ -623,7 +577,6 @@ class AstParserService {
|
|
|
623
577
|
}
|
|
624
578
|
return map;
|
|
625
579
|
}
|
|
626
|
-
/** The identifier naming a decorator's expression (`@Foo` or the callee of `@Foo(...)`). */
|
|
627
580
|
static decoratorIdentifier(dec) {
|
|
628
581
|
const expr = dec.getExpression();
|
|
629
582
|
if (ts_morph_1.Node.isCallExpression(expr)) {
|
|
@@ -632,7 +585,6 @@ class AstParserService {
|
|
|
632
585
|
}
|
|
633
586
|
return ts_morph_1.Node.isIdentifier(expr) ? expr : undefined;
|
|
634
587
|
}
|
|
635
|
-
/** `dec` recognised against `importMap` — its exported `@nestjs/graphql` name and its call arguments (`[]` for a bare `@Foo`). */
|
|
636
588
|
recognizedDecorator(dec, importMap) {
|
|
637
589
|
const idNode = AstParserService.decoratorIdentifier(dec);
|
|
638
590
|
if (!idNode)
|
|
@@ -652,9 +604,6 @@ class AstParserService {
|
|
|
652
604
|
static checkGraphqlGrammar(name) {
|
|
653
605
|
return GRAPHQL_NAME_RE.test(name) ? { ok: true, name } : { ok: false, reason: 'invalid-name', value: name };
|
|
654
606
|
}
|
|
655
|
-
/** `true` when any decorator argument is an (unwrapped) options object
|
|
656
|
-
* literal with `isAbstract: true` — an abstract base type (`PaginatedType`
|
|
657
|
-
* pattern) that NestJS never registers in the actual GraphQL schema. */
|
|
658
607
|
static isAbstractGraphqlType(args) {
|
|
659
608
|
return args.some((arg) => {
|
|
660
609
|
if (!ts_morph_1.Node.isObjectLiteralExpression(arg))
|
|
@@ -673,7 +622,6 @@ class AstParserService {
|
|
|
673
622
|
return init !== undefined && ts_morph_1.Node.isTrueLiteral(init);
|
|
674
623
|
});
|
|
675
624
|
}
|
|
676
|
-
/** `obj`'s own `name` property (`PropertyAssignment` or `ShorthandPropertyAssignment`), if any. */
|
|
677
625
|
static findNameProperty(obj) {
|
|
678
626
|
return obj.getProperties().find((p) => {
|
|
679
627
|
if (!ts_morph_1.Node.isPropertyAssignment(p) && !ts_morph_1.Node.isShorthandPropertyAssignment(p))
|
|
@@ -686,10 +634,6 @@ class AstParserService {
|
|
|
686
634
|
return false;
|
|
687
635
|
});
|
|
688
636
|
}
|
|
689
|
-
/** Strips parens/`as`/`satisfies`/non-null wrappers (any nesting) to find the
|
|
690
|
-
* underlying node — used only to classify an argument's *shape* (type
|
|
691
|
-
* function vs. options object vs. literal vs. other), never to read a name
|
|
692
|
-
* value through the wrapper (decision 4 of `graphql-symbols` forbids that). */
|
|
693
637
|
static peekThroughWrapper(node) {
|
|
694
638
|
let current = node;
|
|
695
639
|
let wrapped = false;
|
|
@@ -703,22 +647,12 @@ class AstParserService {
|
|
|
703
647
|
}
|
|
704
648
|
return { inner: current, wrapped };
|
|
705
649
|
}
|
|
706
|
-
/**
|
|
707
|
-
* GraphQL name from a decorator's arguments (design decision 4 of
|
|
708
|
-
* `graphql-symbols`) — syntactic, no wrapper looked through at any step.
|
|
709
|
-
* `allowOptionsName` gates step 2 (the `name` option — `Query`/`Mutation`/
|
|
710
|
-
* `Subscription`/`ResolveField` only); `fallbackName` is the TS name used at
|
|
711
|
-
* step 3.
|
|
712
|
-
*/
|
|
713
650
|
deriveGraphqlName(args, allowOptionsName, fallbackName) {
|
|
714
651
|
if (args.length > 0) {
|
|
715
652
|
const first = args[0];
|
|
716
653
|
const { inner, wrapped } = AstParserService.peekThroughWrapper(first);
|
|
717
654
|
const literal = AstParserService.literalStringValue(inner);
|
|
718
655
|
if (literal !== null) {
|
|
719
|
-
// A wrapped literal is still "a wrapped ... argument", an anything-else
|
|
720
|
-
// node (decision 4) — reading its value through the wrapper is exactly
|
|
721
|
-
// what "no wrapper is looked through" forbids.
|
|
722
656
|
if (wrapped)
|
|
723
657
|
return { ok: false, reason: 'non-literal-name' };
|
|
724
658
|
return AstParserService.checkGraphqlGrammar(literal);
|
|
@@ -726,18 +660,12 @@ class AstParserService {
|
|
|
726
660
|
const isTypeFnOrOptions = ts_morph_1.Node.isArrowFunction(inner) || ts_morph_1.Node.isFunctionExpression(inner) || ts_morph_1.Node.isObjectLiteralExpression(inner);
|
|
727
661
|
if (!isTypeFnOrOptions)
|
|
728
662
|
return { ok: false, reason: 'non-literal-name' };
|
|
729
|
-
// Type function / options object, wrapped or not — this is a routing
|
|
730
|
-
// decision only (go to step 2/3); nothing is read from it here, so its
|
|
731
|
-
// wrapper doesn't matter yet.
|
|
732
663
|
}
|
|
733
664
|
if (allowOptionsName) {
|
|
734
665
|
for (const arg of args) {
|
|
735
666
|
const { inner, wrapped } = AstParserService.peekThroughWrapper(arg);
|
|
736
667
|
if (!ts_morph_1.Node.isObjectLiteralExpression(inner))
|
|
737
668
|
continue;
|
|
738
|
-
// A wrapped options object cannot be trusted without looking through
|
|
739
|
-
// the wrapper, which decision 4 forbids — treat it as non-literal
|
|
740
|
-
// rather than silently skipping to the next argument or to step 3.
|
|
741
669
|
if (wrapped)
|
|
742
670
|
return { ok: false, reason: 'non-literal-name' };
|
|
743
671
|
const nameProp = AstParserService.findNameProperty(inner);
|
|
@@ -760,15 +688,10 @@ class AstParserService {
|
|
|
760
688
|
fields.value = value;
|
|
761
689
|
this.logger.debug('AstParser: graphql row skipped', fields);
|
|
762
690
|
}
|
|
763
|
-
/** `declNode`'s qualified TypeScript name for diagnostics, independent of
|
|
764
|
-
* whether its own `code_symbol` row was actually written. */
|
|
765
691
|
declarationNameFor(node, fallback) {
|
|
766
692
|
const described = this.describeIndexedNode(node);
|
|
767
693
|
return described ? this.qualifiedName(described) : fallback;
|
|
768
694
|
}
|
|
769
|
-
/** The identifier named by a `@Resolver` argument: directly, or as an arrow
|
|
770
|
-
* function's expression body; or the sole identifier of a single-element
|
|
771
|
-
* array literal in either position (list-returning resolver). */
|
|
772
695
|
static extractResolverIdentifier(argNode) {
|
|
773
696
|
let target = argNode;
|
|
774
697
|
if (ts_morph_1.Node.isArrowFunction(target)) {
|
|
@@ -794,10 +717,6 @@ class AstParserService {
|
|
|
794
717
|
const resolved = firstDecl && ts_morph_1.Node.isImportSpecifier(firstDecl) ? (sym.getAliasedSymbol() ?? sym) : sym;
|
|
795
718
|
return resolved.getDeclarations()?.[0];
|
|
796
719
|
}
|
|
797
|
-
/** The GraphQL type name of a `@ResolveField`'s resolved parent class
|
|
798
|
-
* (design decision 5, second half): its recognised `@ObjectType` (else
|
|
799
|
-
* `@InterfaceType`) decorator, or its class name when it carries no
|
|
800
|
-
* decorator spelled `ObjectType`/`InterfaceType` at all, recognised or not. */
|
|
801
720
|
classGraphqlTypeName(declClass) {
|
|
802
721
|
const declImportMap = this.buildGraphqlImportMap(declClass.getSourceFile());
|
|
803
722
|
const decorators = declClass.getDecorators();
|
|
@@ -815,9 +734,6 @@ class AstParserService {
|
|
|
815
734
|
const nameResult = this.deriveGraphqlName(recognized.args, false, fallbackName);
|
|
816
735
|
return nameResult.ok ? { ok: true, name: nameResult.name } : { ok: false };
|
|
817
736
|
}
|
|
818
|
-
// Carries a type-spelled decorator that isn't recognised (e.g. imported
|
|
819
|
-
// through a barrel) — its GraphQL name is unreadable; the class-name
|
|
820
|
-
// fallback never applies to it (it may differ from the unreadable name).
|
|
821
737
|
if (hasAnyTypeSpelledDecorator)
|
|
822
738
|
return { ok: false };
|
|
823
739
|
const name = declClass.getName();
|
|
@@ -825,8 +741,6 @@ class AstParserService {
|
|
|
825
741
|
return { ok: false };
|
|
826
742
|
return GRAPHQL_NAME_RE.test(name) ? { ok: true, name } : { ok: false, value: name };
|
|
827
743
|
}
|
|
828
|
-
/** The parent type of a `@ResolveField` — the enclosing class's recognised
|
|
829
|
-
* `@Resolver` argument (design decision 5, first half). */
|
|
830
744
|
resolveResolverParentType(cls, importMap) {
|
|
831
745
|
const resolverDec = cls.getDecorators()
|
|
832
746
|
.map(d => this.recognizedDecorator(d, importMap))
|
|
@@ -914,9 +828,6 @@ class AstParserService {
|
|
|
914
828
|
if (!rec)
|
|
915
829
|
continue;
|
|
916
830
|
if (rec.name === 'ResolveField') {
|
|
917
|
-
// Isolated per field: a throw resolving this field's parent (e.g. the
|
|
918
|
-
// injected test seam) degrades to "no row for that field" and never
|
|
919
|
-
// trips the file-level catch in `analyzeSourceFile`.
|
|
920
831
|
try {
|
|
921
832
|
this.extractResolveFieldRow(method, rec, own, declaration, importMap, filePath, serviceName, entries);
|
|
922
833
|
}
|
|
@@ -927,7 +838,7 @@ class AstParserService {
|
|
|
927
838
|
}
|
|
928
839
|
const kind = GRAPHQL_OPERATION_KIND[rec.name];
|
|
929
840
|
if (!kind)
|
|
930
|
-
continue;
|
|
841
|
+
continue;
|
|
931
842
|
if (!own) {
|
|
932
843
|
this.logGraphqlSkip(filePath, declaration, rec.name, 'no-symbol-row');
|
|
933
844
|
continue;
|
|
@@ -944,10 +855,6 @@ class AstParserService {
|
|
|
944
855
|
});
|
|
945
856
|
}
|
|
946
857
|
}
|
|
947
|
-
/** GraphQL-named rows for every recognised `@nestjs/graphql` decorator in
|
|
948
|
-
* the file (`graphql-symbols`). `ownRows` is `symbols ∪ members` — the
|
|
949
|
-
* declarations' own `code_symbol` candidates, checked so a GraphQL row is
|
|
950
|
-
* never written without its decorated declaration's own row. */
|
|
951
858
|
extractGraphqlNames(sourceFile, filePath, serviceName, ownRows) {
|
|
952
859
|
const importMap = this.buildGraphqlImportMap(sourceFile);
|
|
953
860
|
if (importMap.size === 0)
|
|
@@ -964,7 +871,6 @@ class AstParserService {
|
|
|
964
871
|
}
|
|
965
872
|
return entries;
|
|
966
873
|
}
|
|
967
|
-
// ── private: shared container resolution ────────────────────────────────────
|
|
968
874
|
static isValidSegment(s) {
|
|
969
875
|
return VALID_SEGMENT.test(s);
|
|
970
876
|
}
|
|
@@ -998,7 +904,6 @@ class AstParserService {
|
|
|
998
904
|
return current;
|
|
999
905
|
}
|
|
1000
906
|
isModuleOrNamespaceLevelVariable(varDecl) {
|
|
1001
|
-
// VariableDeclaration -> VariableDeclarationList -> VariableStatement -> (SourceFile | ModuleBlock)
|
|
1002
907
|
const stmt = varDecl.getParent()?.getParent();
|
|
1003
908
|
const stmtParent = stmt?.getParent();
|
|
1004
909
|
return ts_morph_1.Node.isSourceFile(stmtParent) || ts_morph_1.Node.isModuleBlock(stmtParent);
|
|
@@ -1031,9 +936,6 @@ class AstParserService {
|
|
|
1031
936
|
isTopLevelOrNamespaceDirect(node) {
|
|
1032
937
|
return ts_morph_1.Node.isSourceFile(node) || ts_morph_1.Node.isModuleBlock(node);
|
|
1033
938
|
}
|
|
1034
|
-
/** One step of the ancestor walk used by `containerSegments`: does `node`
|
|
1035
|
-
* contribute a named segment, pass through transparently, or disqualify
|
|
1036
|
-
* (exclude) everything nested inside it? */
|
|
1037
939
|
walkStep(node) {
|
|
1038
940
|
if (ts_morph_1.Node.isModuleDeclaration(node)) {
|
|
1039
941
|
if (node.compilerNode.flags & ts_morph_1.NodeFlags.GlobalAugmentation)
|
|
@@ -1086,10 +988,6 @@ class AstParserService {
|
|
|
1086
988
|
}
|
|
1087
989
|
return { kind: 'transparent' };
|
|
1088
990
|
}
|
|
1089
|
-
/** The dotted container path of `node`'s enclosing named scopes, outermost
|
|
1090
|
-
* first, or null when any contributing ancestor is invalid (a computed /
|
|
1091
|
-
* string-literal segment, a string-named module, `declare global`, or an
|
|
1092
|
-
* unrooted class expression / object literal). */
|
|
1093
991
|
containerSegments(node) {
|
|
1094
992
|
const segments = [];
|
|
1095
993
|
let current = node.getParent();
|
|
@@ -1108,10 +1006,6 @@ class AstParserService {
|
|
|
1108
1006
|
}
|
|
1109
1007
|
return segments;
|
|
1110
1008
|
}
|
|
1111
|
-
/** If `node` is a declaration this service indexes (top-level symbol, member,
|
|
1112
|
-
* or collected class), its own name + container. Otherwise null. Shared by
|
|
1113
|
-
* member extraction, ownership ranges and the call graph so all three name a
|
|
1114
|
-
* declaration identically. */
|
|
1115
1009
|
describeIndexedNode(node) {
|
|
1116
1010
|
if (ts_morph_1.Node.isClassDeclaration(node)) {
|
|
1117
1011
|
const name = node.getName() ?? (node.isDefaultExport() ? 'default' : null);
|
|
@@ -1204,9 +1098,6 @@ class AstParserService {
|
|
|
1204
1098
|
qualifiedName(described) {
|
|
1205
1099
|
return described.container.length ? `${described.container.join('.')}.${described.name}` : described.name;
|
|
1206
1100
|
}
|
|
1207
|
-
/** The qualified name of the nearest ancestor of `node` that is itself an
|
|
1208
|
-
* indexed declaration (method, accessor, constructor, function property,
|
|
1209
|
-
* namespace/top-level function, or module-/namespace-level variable). */
|
|
1210
1101
|
findFromSymbol(node) {
|
|
1211
1102
|
let current = node.getParent();
|
|
1212
1103
|
while (current) {
|
|
@@ -1217,7 +1108,6 @@ class AstParserService {
|
|
|
1217
1108
|
}
|
|
1218
1109
|
return null;
|
|
1219
1110
|
}
|
|
1220
|
-
// ── private: call graph extraction ────────────────────────────────────────
|
|
1221
1111
|
extractCalls(sourceFile, filePath, serviceName) {
|
|
1222
1112
|
const calls = [];
|
|
1223
1113
|
for (const callExpr of sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression)) {
|
|
@@ -1240,7 +1130,6 @@ class AstParserService {
|
|
|
1240
1130
|
calleeName = expr.getText();
|
|
1241
1131
|
const sym = expr.getSymbol();
|
|
1242
1132
|
if (sym) {
|
|
1243
|
-
// Imported identifiers resolve to ImportSpecifier (same file); follow the alias.
|
|
1244
1133
|
const firstDecl = sym.getDeclarations()?.[0];
|
|
1245
1134
|
const resolved = firstDecl && ts_morph_1.Node.isImportSpecifier(firstDecl)
|
|
1246
1135
|
? (sym.getAliasedSymbol() ?? sym)
|
|
@@ -1255,7 +1144,6 @@ class AstParserService {
|
|
|
1255
1144
|
}
|
|
1256
1145
|
}
|
|
1257
1146
|
catch {
|
|
1258
|
-
// type resolution failures are normal — skip this call
|
|
1259
1147
|
}
|
|
1260
1148
|
if (!calleeName || !calleeFile)
|
|
1261
1149
|
continue;
|
|
@@ -13,17 +13,13 @@ export declare class DatabaseService implements IDatabase {
|
|
|
13
13
|
constructor(config: Config);
|
|
14
14
|
get orm(): DrizzleDb;
|
|
15
15
|
query<T = any>(sql: string, params?: any[]): Promise<T>;
|
|
16
|
+
queryWithLocalSettings<T = any>(settings: Record<string, string | number>, sql: string, params?: any[]): Promise<T>;
|
|
17
|
+
pgvectorVersion(): Promise<string | null>;
|
|
16
18
|
transaction<T>(fn: (tx: DrizzleTx) => Promise<T>): Promise<T>;
|
|
17
19
|
destroy(): Promise<void>;
|
|
18
20
|
initDb(): Promise<void>;
|
|
19
21
|
initDbWithSchema(): Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
* Create `code_chunk` at `dimensions` if it does not exist, plus its HNSW and
|
|
22
|
-
* (serviceName, filePath) indexes and the additive `contentHash`/`ownerPath`
|
|
23
|
-
* columns for pre-existing tables. Idempotent — safe to call repeatedly.
|
|
24
|
-
*/
|
|
25
22
|
ensureChunkTable(dimensions: number): Promise<void>;
|
|
26
|
-
/** Drop and recreate `code_chunk` at `dimensions` — used on a dimension change. */
|
|
27
23
|
recreateChunkTable(dimensions: number): Promise<void>;
|
|
28
24
|
readIndexMeta(): Promise<IndexMeta | null>;
|
|
29
25
|
writeIndexMeta(meta: IndexMeta): Promise<void>;
|
|
@@ -31,7 +27,6 @@ export declare class DatabaseService implements IDatabase {
|
|
|
31
27
|
deleteFiles(project: string, filePaths: string[]): Promise<void>;
|
|
32
28
|
listIndexedProjects(): Promise<string[]>;
|
|
33
29
|
deleteProject(name: string): Promise<void>;
|
|
34
|
-
/** Guard against SQL injection — the dimension is interpolated into DDL. */
|
|
35
30
|
private assertDim;
|
|
36
31
|
private assertMinPostgresVersion;
|
|
37
32
|
private ensureDatabaseExists;
|