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.
Files changed (141) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.md +46 -26
  3. package/dist/app.js +5 -19
  4. package/dist/src/chunking/AstChunker.d.ts +12 -0
  5. package/dist/src/chunking/AstChunker.js +30 -0
  6. package/dist/src/chunking/CharacterChunkStrategy.d.ts +8 -0
  7. package/dist/src/chunking/CharacterChunkStrategy.js +15 -0
  8. package/dist/src/chunking/DeclarationChunkStrategy.d.ts +15 -0
  9. package/dist/src/chunking/DeclarationChunkStrategy.js +87 -0
  10. package/dist/src/chunking/FragmentMerger.d.ts +11 -0
  11. package/dist/src/chunking/FragmentMerger.js +61 -0
  12. package/dist/src/chunking/ImportOnlyFilter.d.ts +5 -0
  13. package/dist/src/chunking/ImportOnlyFilter.js +14 -0
  14. package/dist/src/chunking/LangchainTextSplitter.d.ts +21 -0
  15. package/dist/src/chunking/LangchainTextSplitter.js +133 -0
  16. package/dist/src/chunking/MinSizeSplitPolicy.d.ts +9 -0
  17. package/dist/src/chunking/MinSizeSplitPolicy.js +18 -0
  18. package/dist/src/chunking/RunPacker.d.ts +12 -0
  19. package/dist/src/chunking/RunPacker.js +84 -0
  20. package/dist/src/chunking/create-chunker.d.ts +3 -0
  21. package/dist/src/chunking/create-chunker.js +15 -0
  22. package/dist/src/chunking/interfaces.d.ts +27 -0
  23. package/dist/src/chunking/interfaces.js +2 -0
  24. package/dist/src/commands/ConfigCommand.js +1 -9
  25. package/dist/src/commands/EvalCommand.d.ts +4 -3
  26. package/dist/src/commands/EvalCommand.js +85 -52
  27. package/dist/src/commands/IndexCommand.js +0 -22
  28. package/dist/src/commands/McpCommand.js +2 -8
  29. package/dist/src/commands/WebCommand.js +2 -5
  30. package/dist/src/config-error.d.ts +0 -4
  31. package/dist/src/config-error.js +0 -4
  32. package/dist/src/config-store.d.ts +1 -54
  33. package/dist/src/config-store.js +8 -69
  34. package/dist/src/config.d.ts +8 -17
  35. package/dist/src/config.js +35 -34
  36. package/dist/src/constants.d.ts +2 -28
  37. package/dist/src/constants.js +6 -28
  38. package/dist/src/container.d.ts +6 -0
  39. package/dist/src/container.js +12 -3
  40. package/dist/src/db/schema.d.ts +17 -0
  41. package/dist/src/db/schema.js +1 -6
  42. package/dist/src/declarations/DeclarationTree.d.ts +19 -0
  43. package/dist/src/declarations/DeclarationTree.js +66 -0
  44. package/dist/src/declarations/DeclarationTreeBuilder.d.ts +22 -0
  45. package/dist/src/declarations/DeclarationTreeBuilder.js +179 -0
  46. package/dist/src/declarations/SourceLines.d.ts +37 -0
  47. package/dist/src/declarations/SourceLines.js +415 -0
  48. package/dist/src/dependency-traversal.d.ts +0 -6
  49. package/dist/src/dependency-traversal.js +0 -6
  50. package/dist/src/enriched-prompt.d.ts +0 -6
  51. package/dist/src/enriched-prompt.js +0 -6
  52. package/dist/src/error-record.d.ts +12 -0
  53. package/dist/src/error-record.js +248 -0
  54. package/dist/src/eval-corpus.d.ts +16 -0
  55. package/dist/src/eval-corpus.js +169 -0
  56. package/dist/src/eval-results.d.ts +6 -3
  57. package/dist/src/eval-results.js +309 -26
  58. package/dist/src/file-utils.d.ts +0 -5
  59. package/dist/src/file-utils.js +0 -5
  60. package/dist/src/graph.d.ts +0 -25
  61. package/dist/src/graph.js +0 -28
  62. package/dist/src/hnsw-settings.d.ts +3 -0
  63. package/dist/src/hnsw-settings.js +24 -0
  64. package/dist/src/interfaces/IAstParser.d.ts +2 -39
  65. package/dist/src/interfaces/IChunker.d.ts +3 -12
  66. package/dist/src/interfaces/ICorpusInspector.d.ts +7 -0
  67. package/dist/src/interfaces/ICorpusInspector.js +2 -0
  68. package/dist/src/interfaces/IDatabase.d.ts +2 -22
  69. package/dist/src/interfaces/IEmbedder.d.ts +0 -9
  70. package/dist/src/interfaces/ILogger.d.ts +1 -11
  71. package/dist/src/interfaces/IMcpToolHandler.d.ts +0 -5
  72. package/dist/src/interfaces/IMonorepoResolver.d.ts +0 -22
  73. package/dist/src/interfaces/IProjectDiscovery.d.ts +0 -17
  74. package/dist/src/interfaces/IRetrievalEvaluator.d.ts +134 -12
  75. package/dist/src/interfaces/ISearcher.d.ts +16 -5
  76. package/dist/src/interrupt.js +0 -2
  77. package/dist/src/owner-path.d.ts +9 -10
  78. package/dist/src/owner-path.js +11 -23
  79. package/dist/src/project-scope.d.ts +0 -12
  80. package/dist/src/project-scope.js +0 -12
  81. package/dist/src/project-selector.d.ts +0 -5
  82. package/dist/src/project-selector.js +0 -11
  83. package/dist/src/retrieval-eval.d.ts +14 -6
  84. package/dist/src/retrieval-eval.js +345 -59
  85. package/dist/src/search/result-shaping.d.ts +4 -0
  86. package/dist/src/search/result-shaping.js +19 -0
  87. package/dist/src/services/AstParserService.d.ts +2 -62
  88. package/dist/src/services/AstParserService.js +25 -137
  89. package/dist/src/services/DatabaseService.d.ts +2 -7
  90. package/dist/src/services/DatabaseService.js +42 -29
  91. package/dist/src/services/EmbedderFactory.d.ts +0 -4
  92. package/dist/src/services/EmbedderFactory.js +0 -5
  93. package/dist/src/services/IndexerService.d.ts +0 -16
  94. package/dist/src/services/IndexerService.js +16 -45
  95. package/dist/src/services/InfinityEmbedder.d.ts +0 -21
  96. package/dist/src/services/InfinityEmbedder.js +0 -15
  97. package/dist/src/services/LoggerService.d.ts +7 -5
  98. package/dist/src/services/LoggerService.js +37 -41
  99. package/dist/src/services/McpServer.d.ts +22 -14
  100. package/dist/src/services/McpServer.js +70 -37
  101. package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +1 -45
  102. package/dist/src/services/MonorepoAwareDiscoveryService.js +1 -51
  103. package/dist/src/services/NxMonorepoResolver.d.ts +0 -29
  104. package/dist/src/services/NxMonorepoResolver.js +5 -56
  105. package/dist/src/services/OpenAICompatibleEmbedder.d.ts +0 -6
  106. package/dist/src/services/OpenAICompatibleEmbedder.js +0 -14
  107. package/dist/src/services/ProjectDiscoveryService.d.ts +0 -4
  108. package/dist/src/services/ProjectDiscoveryService.js +0 -4
  109. package/dist/src/services/SearcherService.d.ts +9 -116
  110. package/dist/src/services/SearcherService.js +95 -176
  111. package/dist/src/services/VoyageEmbedder.d.ts +0 -6
  112. package/dist/src/services/VoyageEmbedder.js +0 -6
  113. package/dist/src/services/WebServerService.d.ts +1 -53
  114. package/dist/src/services/WebServerService.js +11 -57
  115. package/dist/src/services/http-retry.d.ts +0 -7
  116. package/dist/src/services/http-retry.js +1 -10
  117. package/dist/src/services/infinity-provisioning.d.ts +0 -74
  118. package/dist/src/services/infinity-provisioning.js +5 -88
  119. package/dist/src/services/legacy-ollama-index.d.ts +0 -16
  120. package/dist/src/services/legacy-ollama-index.js +0 -23
  121. package/dist/src/services/reranker.d.ts +0 -9
  122. package/dist/src/services/reranker.js +46 -12
  123. package/dist/src/setup.d.ts +11 -23
  124. package/dist/src/setup.js +98 -71
  125. package/dist/src/symbol-name.d.ts +0 -12
  126. package/dist/src/symbol-name.js +0 -13
  127. package/dist/src/symbol-rows.d.ts +0 -14
  128. package/dist/src/symbol-rows.js +0 -14
  129. package/dist/src/tool-call-log.d.ts +2 -0
  130. package/dist/src/tool-call-log.js +24 -0
  131. package/dist/src/tools/GetDependenciesTool.js +1 -1
  132. package/dist/src/tools/GetDependentsTool.js +1 -1
  133. package/dist/src/tools/SearchCodebaseTool.js +6 -3
  134. package/dist/src/tools/format-helpers.d.ts +0 -6
  135. package/dist/src/tools/format-helpers.js +87 -14
  136. package/dist/src/web/dashboard.d.ts +0 -8
  137. package/dist/src/web/dashboard.js +7 -10
  138. package/package.json +2 -2
  139. package/dist/src/services/AstChunker.d.ts +0 -42
  140. package/dist/src/services/AstChunker.js +0 -158
  141. 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, reason: err.message });
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, reason: err.message, ms: Date.now() - t0 });
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, reason: err.message });
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 ownerRanges = this.buildOwnerRanges([...symbolCandidates, ...memberCandidates]);
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, reason: err.message });
159
+ this.logger.warn('AstParser: GraphQL extraction failed', { filePath, err });
170
160
  graphqlNames = [];
171
161
  }
172
- return { edges, symbols, members, calls, ownerRanges, graphqlNames };
162
+ return { edges, symbols, members, calls, declarations, graphqlNames };
173
163
  }
174
164
  catch (err) {
175
- this.logger.warn('AstParser: failed to analyze file', { filePath, reason: err.message });
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
- reason: err.message,
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
- /** Chunk-ownership ranges from every indexed declaration (top-level symbols +
596
- * members) except `method_signature`. Each range spans the declaration node's
597
- * own first-to-last line — decorator-inclusive, comment-exclusive, and (for a
598
- * variable) the declarator alone, never the whole statement. */
599
- buildOwnerRanges(candidates) {
600
- return candidates
601
- .filter(c => c.entry.kind !== 'method_signature')
602
- .map(c => ({
603
- name: c.entry.name,
604
- container: c.entry.container,
605
- line: c.declNode.getStartLineNumber(),
606
- endLine: c.declNode.getEndLineNumber(),
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; // recognised but not an operation decorator (wrong declaration kind) — ignored, no log
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;