code-graph-context 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +156 -2
  2. package/dist/constants.js +167 -0
  3. package/dist/core/config/fairsquare-framework-schema.js +9 -7
  4. package/dist/core/config/nestjs-framework-schema.js +60 -43
  5. package/dist/core/config/schema.js +41 -2
  6. package/dist/core/embeddings/natural-language-to-cypher.service.js +166 -110
  7. package/dist/core/parsers/typescript-parser.js +1043 -747
  8. package/dist/core/parsers/workspace-parser.js +177 -194
  9. package/dist/core/utils/code-normalizer.js +299 -0
  10. package/dist/core/utils/file-change-detection.js +17 -2
  11. package/dist/core/utils/file-utils.js +40 -5
  12. package/dist/core/utils/graph-factory.js +161 -0
  13. package/dist/core/utils/shared-utils.js +79 -0
  14. package/dist/core/workspace/workspace-detector.js +59 -5
  15. package/dist/mcp/constants.js +141 -8
  16. package/dist/mcp/handlers/graph-generator.handler.js +1 -0
  17. package/dist/mcp/handlers/incremental-parse.handler.js +3 -6
  18. package/dist/mcp/handlers/parallel-import.handler.js +136 -0
  19. package/dist/mcp/handlers/streaming-import.handler.js +14 -59
  20. package/dist/mcp/mcp.server.js +1 -1
  21. package/dist/mcp/services/job-manager.js +5 -8
  22. package/dist/mcp/services/watch-manager.js +7 -18
  23. package/dist/mcp/tools/detect-dead-code.tool.js +413 -0
  24. package/dist/mcp/tools/detect-duplicate-code.tool.js +450 -0
  25. package/dist/mcp/tools/impact-analysis.tool.js +20 -4
  26. package/dist/mcp/tools/index.js +4 -0
  27. package/dist/mcp/tools/parse-typescript-project.tool.js +15 -14
  28. package/dist/mcp/workers/chunk-worker-pool.js +196 -0
  29. package/dist/mcp/workers/chunk-worker.types.js +4 -0
  30. package/dist/mcp/workers/chunk.worker.js +89 -0
  31. package/dist/mcp/workers/parse-coordinator.js +183 -0
  32. package/dist/mcp/workers/worker.pool.js +54 -0
  33. package/dist/storage/neo4j/neo4j.service.js +190 -10
  34. package/package.json +1 -1
package/README.md CHANGED
@@ -20,12 +20,16 @@ A Model Context Protocol (MCP) server that builds rich code graphs to provide de
20
20
  - **Natural Language Querying**: Convert natural language questions into Cypher queries using OpenAI assistants API
21
21
  - **Framework-Aware & Customizable**: Built-in NestJS schema with ability to define custom framework patterns via configuration
22
22
  - **Weighted Graph Traversal**: Intelligent traversal that scores paths based on relationship importance, query relevance, and depth
23
- - **Workspace & Monorepo Support**: Auto-detects Turborepo, pnpm, Yarn, and npm workspaces
23
+ - **Workspace & Monorepo Support**: Auto-detects Nx, Turborepo, pnpm, Yarn, and npm workspaces
24
+ - **Parallel Parsing**: Multi-threaded parsing with configurable worker pool for maximum CPU utilization
24
25
  - **Async Parsing**: Background parsing with Worker threads for large codebases without blocking the MCP server
25
26
  - **Streaming Import**: Chunked processing for projects with 100+ files to prevent memory issues
27
+ - **TypeAlias Support**: Full parsing of TypeScript type aliases into graph nodes
26
28
  - **Incremental Parsing**: Only reparse changed files for faster updates
27
29
  - **File Watching**: Real-time monitoring with automatic incremental graph updates on file changes
28
30
  - **Impact Analysis**: Assess refactoring risk with dependency analysis (LOW/MEDIUM/HIGH/CRITICAL scoring)
31
+ - **Dead Code Detection**: Find unreferenced exports, uncalled private methods, unused interfaces with confidence scoring
32
+ - **Duplicate Code Detection**: Identify structural duplicates (identical AST) and semantic duplicates (similar logic via embeddings)
29
33
  - **High Performance**: Optimized Neo4j storage with vector indexing for fast retrieval
30
34
  - **MCP Integration**: Seamless integration with Claude Code and other MCP-compatible tools
31
35
 
@@ -249,6 +253,8 @@ npm run build
249
253
  | `stop_watch_project` | Stop file watching for a project | **Resource management** - stop monitoring |
250
254
  | `list_watchers` | List all active file watchers | **Monitoring** - see what's being watched |
251
255
  | `natural_language_to_cypher` | Convert natural language to Cypher | **Advanced queries** - complex graph queries |
256
+ | `detect_dead_code` | Find unreferenced exports, uncalled methods, unused interfaces | **Code cleanup** - identify potentially removable code |
257
+ | `detect_duplicate_code` | Find structural and semantic code duplicates | **Refactoring** - identify DRY violations |
252
258
  | `test_neo4j_connection` | Verify database connectivity | **Health check** - troubleshooting |
253
259
 
254
260
  > **Note**: All query tools (`search_codebase`, `traverse_from_node`, `impact_analysis`, `natural_language_to_cypher`) require a `projectId` parameter. Use `list_projects` to discover available projects.
@@ -562,7 +568,155 @@ await mcp.call('test_neo4j_connection');
562
568
  APOC plugin available with 438 functions"
563
569
  ```
564
570
 
565
- #### 5. File Watching Tools
571
+ #### 5. `detect_dead_code` - Code Cleanup Analysis
572
+ **Purpose**: Identify potentially dead code including unreferenced exports, uncalled private methods, and unused interfaces.
573
+
574
+ **Parameters:**
575
+ | Parameter | Type | Default | Description |
576
+ |-----------|------|---------|-------------|
577
+ | `projectId` | string | required | Project ID, name, or path |
578
+ | `excludePatterns` | string[] | [] | Additional file patterns to exclude (e.g., `["*.config.ts"]`) |
579
+ | `excludeSemanticTypes` | string[] | [] | Additional semantic types to exclude (e.g., `["EntityClass"]`) |
580
+ | `minConfidence` | enum | "LOW" | Minimum confidence: "LOW", "MEDIUM", "HIGH" |
581
+ | `summaryOnly` | boolean | false | Return only statistics, not full list |
582
+ | `limit` | number | 100 | Maximum items to return |
583
+ | `offset` | number | 0 | Pagination offset |
584
+
585
+ **Response Structure:**
586
+ ```json
587
+ {
588
+ "summary": "Found 15 potentially dead code items",
589
+ "riskLevel": "MEDIUM",
590
+ "statistics": {
591
+ "total": 15,
592
+ "byConfidence": { "HIGH": 5, "MEDIUM": 7, "LOW": 3 },
593
+ "byCategory": { "internal-unused": 10, "library-export": 3, "ui-component": 2 },
594
+ "byType": { "FunctionDeclaration": 8, "InterfaceDeclaration": 4, "MethodDeclaration": 3 }
595
+ },
596
+ "deadCode": [
597
+ {
598
+ "nodeId": "proj_xxx:FunctionDeclaration:abc123",
599
+ "name": "unusedHelper",
600
+ "type": "FunctionDeclaration",
601
+ "filePath": "/src/utils/helpers.ts",
602
+ "lineNumber": 42,
603
+ "confidence": "HIGH",
604
+ "confidenceReason": "Exported but never imported anywhere",
605
+ "category": "internal-unused",
606
+ "reason": "Exported but never imported or referenced"
607
+ }
608
+ ]
609
+ }
610
+ ```
611
+
612
+ **Framework-Aware Exclusions:**
613
+ - Automatically excludes NestJS entry points (controllers, modules, guards, etc.)
614
+ - Excludes common entry point files (`main.ts`, `*.module.ts`, `*.controller.ts`)
615
+ - Excludes Next.js/React patterns (`page.tsx`, `layout.tsx`, `route.tsx`)
616
+
617
+ ```typescript
618
+ // Basic usage
619
+ await mcp.call('detect_dead_code', {
620
+ projectId: 'my-backend'
621
+ });
622
+
623
+ // High confidence only with custom exclusions
624
+ await mcp.call('detect_dead_code', {
625
+ projectId: 'my-backend',
626
+ minConfidence: 'HIGH',
627
+ excludePatterns: ['*.seed.ts', '*.fixture.ts'],
628
+ excludeSemanticTypes: ['EntityClass', 'DTOClass']
629
+ });
630
+ ```
631
+
632
+ #### 6. `detect_duplicate_code` - DRY Violation Detection
633
+ **Purpose**: Identify duplicate code using structural (identical AST) and semantic (similar embeddings) analysis.
634
+
635
+ **Parameters:**
636
+ | Parameter | Type | Default | Description |
637
+ |-----------|------|---------|-------------|
638
+ | `projectId` | string | required | Project ID, name, or path |
639
+ | `type` | enum | "all" | Detection type: "structural", "semantic", "all" |
640
+ | `scope` | enum | "all" | Scope: "methods", "functions", "classes", "all" |
641
+ | `minSimilarity` | number | 0.8 | Minimum similarity threshold (0.5-1.0) |
642
+ | `maxResults` | number | 50 | Maximum duplicate groups to return |
643
+ | `includeCode` | boolean | true | Include source code snippets |
644
+ | `summaryOnly` | boolean | false | Return only statistics |
645
+
646
+ **Response Structure:**
647
+ ```json
648
+ {
649
+ "summary": "Found 8 duplicate code groups across 12 files",
650
+ "statistics": {
651
+ "totalGroups": 8,
652
+ "totalDuplicates": 24,
653
+ "byType": {
654
+ "structural": { "groups": 3, "items": 9 },
655
+ "semantic": { "groups": 5, "items": 15 }
656
+ },
657
+ "byConfidence": { "HIGH": 4, "MEDIUM": 3, "LOW": 1 }
658
+ },
659
+ "duplicates": [
660
+ {
661
+ "groupId": "dup_1",
662
+ "type": "structural",
663
+ "similarity": 1.0,
664
+ "confidence": "HIGH",
665
+ "category": "cross-file",
666
+ "recommendation": "Extract to shared utility",
667
+ "items": [
668
+ {
669
+ "nodeId": "proj_xxx:MethodDeclaration:abc",
670
+ "name": "formatDate",
671
+ "filePath": "/src/utils/date.ts",
672
+ "lineNumber": 15,
673
+ "sourceCode": "formatDate(date: Date): string { ... }"
674
+ },
675
+ {
676
+ "nodeId": "proj_xxx:MethodDeclaration:def",
677
+ "name": "formatDate",
678
+ "filePath": "/src/helpers/formatting.ts",
679
+ "lineNumber": 42,
680
+ "sourceCode": "formatDate(date: Date): string { ... }"
681
+ }
682
+ ]
683
+ }
684
+ ]
685
+ }
686
+ ```
687
+
688
+ **Detection Types:**
689
+ - **Structural**: Identical normalized code (same AST after removing comments, normalizing names)
690
+ - **Semantic**: Similar logic via vector embeddings (requires embeddings enabled during parse)
691
+
692
+ **Categories:**
693
+ - `ui-component`: Duplicates in UI component directories
694
+ - `cross-app`: Duplicates across monorepo apps
695
+ - `same-file`: Duplicates within the same file
696
+ - `cross-file`: Duplicates across different files
697
+
698
+ ```typescript
699
+ // Find all duplicates
700
+ await mcp.call('detect_duplicate_code', {
701
+ projectId: 'my-backend'
702
+ });
703
+
704
+ // Only structural duplicates in methods
705
+ await mcp.call('detect_duplicate_code', {
706
+ projectId: 'my-backend',
707
+ type: 'structural',
708
+ scope: 'methods'
709
+ });
710
+
711
+ // High similarity semantic duplicates
712
+ await mcp.call('detect_duplicate_code', {
713
+ projectId: 'my-backend',
714
+ type: 'semantic',
715
+ minSimilarity: 0.9
716
+ });
717
+ ```
718
+
719
+ #### 7. File Watching Tools
566
720
  **Purpose**: Monitor file changes and automatically update the graph.
567
721
 
568
722
  ```typescript
package/dist/constants.js CHANGED
@@ -1,4 +1,12 @@
1
1
  export const MAX_TRAVERSAL_DEPTH = 5;
2
+ // Logging Configuration (shared between core and mcp)
3
+ export const LOG_CONFIG = {
4
+ debugLogFile: 'debug-search.log',
5
+ separator: '---',
6
+ jsonIndent: 2,
7
+ // Alias for backwards compatibility with mcp code
8
+ jsonIndentation: 2,
9
+ };
2
10
  // Shared exclude patterns for file parsing and change detection
3
11
  // Regex patterns (escaped dots, anchored to end)
4
12
  export const EXCLUDE_PATTERNS_REGEX = [
@@ -9,6 +17,12 @@ export const EXCLUDE_PATTERNS_REGEX = [
9
17
  '\\.d\\.ts$',
10
18
  '\\.spec\\.ts$',
11
19
  '\\.test\\.ts$',
20
+ // Common config and test infrastructure files
21
+ 'jest\\.config\\.ts$',
22
+ '-e2e/',
23
+ 'test-setup\\.ts$',
24
+ 'global-setup\\.ts$',
25
+ 'global-teardown\\.ts$',
12
26
  ];
13
27
  // Glob patterns for use with glob library
14
28
  export const EXCLUDE_PATTERNS_GLOB = [
@@ -19,4 +33,157 @@ export const EXCLUDE_PATTERNS_GLOB = [
19
33
  '**/*.d.ts',
20
34
  '**/*.spec.ts',
21
35
  '**/*.test.ts',
36
+ // Common config and test infrastructure files
37
+ '**/jest.config.ts',
38
+ '**/*-e2e/**',
39
+ '**/test-setup.ts',
40
+ '**/global-setup.ts',
41
+ '**/global-teardown.ts',
22
42
  ];
43
+ // ============================================
44
+ // CALLS Edge Detection - Built-in Identifiers
45
+ // Skip these when extracting CALLS edges to reduce noise
46
+ // ============================================
47
+ /** Built-in function names to skip when extracting CALLS edges */
48
+ export const BUILT_IN_FUNCTIONS = new Set([
49
+ 'console',
50
+ 'setTimeout',
51
+ 'setInterval',
52
+ 'clearTimeout',
53
+ 'clearInterval',
54
+ 'parseInt',
55
+ 'parseFloat',
56
+ 'isNaN',
57
+ 'isFinite',
58
+ 'encodeURI',
59
+ 'decodeURI',
60
+ 'encodeURIComponent',
61
+ 'decodeURIComponent',
62
+ 'JSON',
63
+ 'Math',
64
+ 'Date',
65
+ 'Object',
66
+ 'Array',
67
+ 'String',
68
+ 'Number',
69
+ 'Boolean',
70
+ 'Symbol',
71
+ 'BigInt',
72
+ 'Promise',
73
+ 'require',
74
+ 'eval',
75
+ ]);
76
+ /** Built-in method names to skip when extracting CALLS edges */
77
+ export const BUILT_IN_METHODS = new Set([
78
+ // Array methods
79
+ 'push',
80
+ 'pop',
81
+ 'shift',
82
+ 'unshift',
83
+ 'slice',
84
+ 'splice',
85
+ 'concat',
86
+ 'join',
87
+ 'reverse',
88
+ 'sort',
89
+ 'indexOf',
90
+ 'lastIndexOf',
91
+ 'includes',
92
+ 'find',
93
+ 'findIndex',
94
+ 'filter',
95
+ 'map',
96
+ 'reduce',
97
+ 'reduceRight',
98
+ 'every',
99
+ 'some',
100
+ 'forEach',
101
+ 'flat',
102
+ 'flatMap',
103
+ 'fill',
104
+ 'entries',
105
+ 'keys',
106
+ 'values',
107
+ // String methods
108
+ 'charAt',
109
+ 'charCodeAt',
110
+ 'substring',
111
+ 'substr',
112
+ 'split',
113
+ 'trim',
114
+ 'trimStart',
115
+ 'trimEnd',
116
+ 'toLowerCase',
117
+ 'toUpperCase',
118
+ 'replace',
119
+ 'replaceAll',
120
+ 'match',
121
+ 'search',
122
+ 'startsWith',
123
+ 'endsWith',
124
+ 'padStart',
125
+ 'padEnd',
126
+ 'repeat',
127
+ // Object methods
128
+ 'hasOwnProperty',
129
+ 'toString',
130
+ 'valueOf',
131
+ 'toJSON',
132
+ // Promise methods
133
+ 'then',
134
+ 'catch',
135
+ 'finally',
136
+ // Console methods
137
+ 'log',
138
+ 'error',
139
+ 'warn',
140
+ 'info',
141
+ 'debug',
142
+ 'trace',
143
+ 'dir',
144
+ 'table',
145
+ // Common utilities
146
+ 'bind',
147
+ 'call',
148
+ 'apply',
149
+ ]);
150
+ /** Built-in class names to skip when extracting constructor calls */
151
+ export const BUILT_IN_CLASSES = new Set([
152
+ 'Array',
153
+ 'Object',
154
+ 'String',
155
+ 'Number',
156
+ 'Boolean',
157
+ 'Date',
158
+ 'RegExp',
159
+ 'Error',
160
+ 'TypeError',
161
+ 'RangeError',
162
+ 'SyntaxError',
163
+ 'ReferenceError',
164
+ 'Map',
165
+ 'Set',
166
+ 'WeakMap',
167
+ 'WeakSet',
168
+ 'Promise',
169
+ 'Proxy',
170
+ 'Reflect',
171
+ 'Symbol',
172
+ 'BigInt',
173
+ 'ArrayBuffer',
174
+ 'DataView',
175
+ 'Int8Array',
176
+ 'Uint8Array',
177
+ 'Int16Array',
178
+ 'Uint16Array',
179
+ 'Int32Array',
180
+ 'Uint32Array',
181
+ 'Float32Array',
182
+ 'Float64Array',
183
+ 'URL',
184
+ 'URLSearchParams',
185
+ 'TextEncoder',
186
+ 'TextDecoder',
187
+ 'Buffer',
188
+ 'EventEmitter',
189
+ ]);
@@ -311,11 +311,12 @@ export const FAIRSQUARE_FRAMEWORK_SCHEMA = {
311
311
  }
312
312
  if (vendorName) {
313
313
  // Initialize map if not exists
314
+ // Store nodeId (string) for serialization support in parallel parsing
314
315
  if (!sharedContext?.has('vendorControllers')) {
315
316
  sharedContext?.set('vendorControllers', new Map());
316
317
  }
317
318
  const vendorControllerMap = sharedContext?.get('vendorControllers');
318
- vendorControllerMap.set(vendorName, parsedNode);
319
+ vendorControllerMap.set(vendorName, parsedNode.id);
319
320
  }
320
321
  }
321
322
  return {};
@@ -747,14 +748,14 @@ export const FAIRSQUARE_FRAMEWORK_SCHEMA = {
747
748
  const isTargetController = parsedTargetNode.semanticType === FairSquareSemanticNodeType.FS_CONTROLLER;
748
749
  if (!isSourceService || !isTargetController)
749
750
  return false;
750
- // Get vendor controller map
751
+ // Get vendor controller map (stores vendorName → nodeId for serialization)
751
752
  const vendorControllerMap = sharedContext?.get('vendorControllers');
752
753
  if (!vendorControllerMap)
753
754
  return false;
754
- // Check if target is a vendor controller
755
+ // Check if target is a vendor controller by matching nodeId
755
756
  let vendorName = '';
756
- for (const [name, controllerNode] of vendorControllerMap) {
757
- if (controllerNode.id === parsedTargetNode.id) {
757
+ for (const [name, nodeId] of vendorControllerMap) {
758
+ if (nodeId === parsedTargetNode.id) {
758
759
  vendorName = name;
759
760
  break;
760
761
  }
@@ -768,10 +769,11 @@ export const FAIRSQUARE_FRAMEWORK_SCHEMA = {
768
769
  return propertyTypes.includes(expectedClientName);
769
770
  },
770
771
  contextExtractor: (parsedSourceNode, parsedTargetNode, allParsedNodes, sharedContext) => {
772
+ // Map stores vendorName → nodeId
771
773
  const vendorControllerMap = sharedContext?.get('vendorControllers');
772
774
  let vendorName = '';
773
- for (const [name, controllerNode] of vendorControllerMap) {
774
- if (controllerNode.id === parsedTargetNode.id) {
775
+ for (const [name, nodeId] of vendorControllerMap) {
776
+ if (nodeId === parsedTargetNode.id) {
775
777
  vendorName = name;
776
778
  break;
777
779
  }
@@ -84,6 +84,38 @@ function extractInjectionToken(node) {
84
84
  }
85
85
  return null;
86
86
  }
87
+ /**
88
+ * Extract constructor parameter types and @Inject tokens for edge detection.
89
+ * This allows INJECTS detection to work without AST access (for cross-chunk detection).
90
+ */
91
+ function extractConstructorParamTypes(node) {
92
+ const types = [];
93
+ const injectTokens = new Map();
94
+ const constructors = node.getConstructors();
95
+ if (constructors.length === 0)
96
+ return { types, injectTokens };
97
+ const constructor = constructors[0];
98
+ const parameters = constructor.getParameters();
99
+ for (const param of parameters) {
100
+ const typeNode = param.getTypeNode();
101
+ if (typeNode) {
102
+ const typeName = typeNode.getText();
103
+ types.push(typeName);
104
+ // Check for @Inject decorator
105
+ const decorators = param.getDecorators();
106
+ for (const decorator of decorators) {
107
+ if (decorator.getName() === 'Inject') {
108
+ const args = decorator.getArguments();
109
+ if (args.length > 0) {
110
+ const token = args[0].getText().replace(/['"]/g, '');
111
+ injectTokens.set(typeName, token);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ }
117
+ return { types, injectTokens };
118
+ }
87
119
  function hasDynamicMethods(node) {
88
120
  const methods = node.getMethods();
89
121
  const dynamicMethods = ['forRoot', 'forFeature', 'forRootAsync', 'forFeatureAsync'];
@@ -262,59 +294,39 @@ function detectDependencyInjection(sourceNode, targetNode) {
262
294
  return false;
263
295
  if (targetNode.properties?.coreType !== CoreNodeType.CLASS_DECLARATION)
264
296
  return false;
265
- const constructors = sourceNode.sourceNode?.getConstructors();
266
- if (!constructors || constructors.length === 0)
267
- return false;
268
- const constructor = constructors[0];
269
- const parameters = constructor.getParameters();
270
297
  const targetName = targetNode.properties?.name;
271
- return parameters.some((param) => {
272
- const paramType = param.getTypeNode()?.getText();
273
- if (paramType === targetName)
274
- return true;
275
- const decorators = param.getDecorators();
276
- return decorators.some((decorator) => {
277
- if (decorator.getName() === 'Inject') {
278
- const args = decorator.getArguments();
279
- if (args.length > 0) {
280
- const token = args[0].getText().replace(/['"]/g, '');
281
- return token === targetName;
282
- }
283
- }
284
- return false;
285
- });
286
- });
298
+ if (!targetName)
299
+ return false;
300
+ // Use pre-extracted constructor params from context (works after AST cleanup)
301
+ const constructorParamTypes = sourceNode.properties?.context?.constructorParamTypes ?? [];
302
+ const injectTokens = sourceNode.properties?.context?.injectTokens ?? {};
303
+ // Check if target is in constructor params by type
304
+ if (constructorParamTypes.includes(targetName))
305
+ return true;
306
+ // Check if target is referenced via @Inject token
307
+ return Object.values(injectTokens).includes(targetName);
287
308
  }
288
309
  function extractInjectionTokenFromRelation(sourceNode, targetNode) {
289
- const constructors = sourceNode.sourceNode?.getConstructors();
290
- if (!constructors || constructors.length === 0)
310
+ const targetName = targetNode.properties?.name;
311
+ if (!targetName)
291
312
  return null;
292
- const constructor = constructors[0];
293
- const parameters = constructor.getParameters();
294
- for (const param of parameters) {
295
- const decorators = param.getDecorators();
296
- for (const decorator of decorators) {
297
- if (decorator.getName() === 'Inject') {
298
- const args = decorator.getArguments();
299
- if (args.length > 0) {
300
- return args[0].getText().replace(/['"]/g, '');
301
- }
302
- }
313
+ // Use pre-extracted inject tokens from context (works after AST cleanup)
314
+ const injectTokens = sourceNode.properties?.context?.injectTokens ?? {};
315
+ // Find the token that maps to the target
316
+ for (const [typeName, token] of Object.entries(injectTokens)) {
317
+ if (token === targetName || typeName === targetName) {
318
+ return token;
303
319
  }
304
320
  }
305
321
  return null;
306
322
  }
307
323
  function findParameterIndex(sourceNode, targetNode) {
308
- const constructors = sourceNode.sourceNode?.getConstructors();
309
- if (!constructors || constructors.length === 0)
310
- return 0;
311
- const constructor = constructors[0];
312
- const parameters = constructor.getParameters();
313
324
  const targetName = targetNode.properties?.name;
314
- return parameters.findIndex((param) => {
315
- const paramType = param.getTypeNode()?.getText();
316
- return paramType === targetName;
317
- });
325
+ if (!targetName)
326
+ return -1;
327
+ // Use pre-extracted constructor params from context (works after AST cleanup)
328
+ const constructorParamTypes = sourceNode.properties?.context?.constructorParamTypes ?? [];
329
+ return constructorParamTypes.indexOf(targetName);
318
330
  }
319
331
  function computeFullPathFromNodes(sourceNode, targetNode) {
320
332
  const basePath = sourceNode.properties?.context?.basePath ?? '';
@@ -791,6 +803,8 @@ export const NESTJS_FRAMEWORK_SCHEMA = {
791
803
  const node = parsedNode.sourceNode;
792
804
  if (!node)
793
805
  return {};
806
+ // Extract constructor param types for INJECTS edge detection
807
+ const { types, injectTokens } = extractConstructorParamTypes(node);
794
808
  return {
795
809
  isAbstract: node.getAbstractKeyword() != null,
796
810
  isDefaultExport: node.isDefaultExport(),
@@ -800,6 +814,9 @@ export const NESTJS_FRAMEWORK_SCHEMA = {
800
814
  methodCount: node.getMethods().length,
801
815
  propertyCount: node.getProperties().length,
802
816
  constructorParameterCount: countConstructorParameters(node),
817
+ // Pre-extracted for cross-chunk edge detection
818
+ constructorParamTypes: types,
819
+ injectTokens: Object.fromEntries(injectTokens),
803
820
  };
804
821
  },
805
822
  priority: 1,
@@ -17,6 +17,7 @@ export var CoreNodeType;
17
17
  CoreNodeType["ENUM_DECLARATION"] = "EnumDeclaration";
18
18
  CoreNodeType["FUNCTION_DECLARATION"] = "FunctionDeclaration";
19
19
  CoreNodeType["VARIABLE_DECLARATION"] = "VariableDeclaration";
20
+ CoreNodeType["TYPE_ALIAS"] = "TypeAlias";
20
21
  // Class Members
21
22
  CoreNodeType["METHOD_DECLARATION"] = "MethodDeclaration";
22
23
  CoreNodeType["PROPERTY_DECLARATION"] = "PropertyDeclaration";
@@ -118,6 +119,7 @@ export const CORE_TYPESCRIPT_SCHEMA = {
118
119
  [CoreNodeType.FUNCTION_DECLARATION]: 'getFunctions',
119
120
  [CoreNodeType.IMPORT_DECLARATION]: 'getImportDeclarations',
120
121
  [CoreNodeType.VARIABLE_DECLARATION]: 'getDeclarations', // Called on VariableStatement
122
+ [CoreNodeType.TYPE_ALIAS]: 'getTypeAliases',
121
123
  [CoreNodeType.ENUM_DECLARATION]: 'getEnums',
122
124
  [CoreNodeType.CONSTRUCTOR_DECLARATION]: 'getConstructors',
123
125
  [CoreNodeType.EXPORT_DECLARATION]: 'getExportDeclarations',
@@ -154,6 +156,8 @@ export const CORE_TYPESCRIPT_SCHEMA = {
154
156
  [CoreNodeType.FUNCTION_DECLARATION]: CoreEdgeType.CONTAINS,
155
157
  [CoreNodeType.IMPORT_DECLARATION]: CoreEdgeType.CONTAINS,
156
158
  [CoreNodeType.ENUM_DECLARATION]: CoreEdgeType.CONTAINS,
159
+ [CoreNodeType.VARIABLE_DECLARATION]: CoreEdgeType.CONTAINS,
160
+ [CoreNodeType.TYPE_ALIAS]: CoreEdgeType.CONTAINS,
157
161
  },
158
162
  neo4j: {
159
163
  labels: ['SourceFile', 'TypeScript'],
@@ -470,6 +474,12 @@ export const CORE_TYPESCRIPT_SCHEMA = {
470
474
  extraction: { method: 'ast', source: 'getName' },
471
475
  neo4j: { indexed: true, unique: false, required: true },
472
476
  },
477
+ {
478
+ name: 'isExported',
479
+ type: 'boolean',
480
+ extraction: { method: 'static', defaultValue: false }, // We'll set this manually
481
+ neo4j: { indexed: true, unique: false, required: true },
482
+ },
473
483
  ],
474
484
  relationships: [],
475
485
  children: {},
@@ -480,6 +490,27 @@ export const CORE_TYPESCRIPT_SCHEMA = {
480
490
  skipEmbedding: true,
481
491
  },
482
492
  },
493
+ [CoreNodeType.TYPE_ALIAS]: {
494
+ coreType: CoreNodeType.TYPE_ALIAS,
495
+ astNodeKind: 265,
496
+ astGetter: 'getTypeAliases',
497
+ properties: [
498
+ {
499
+ name: 'name',
500
+ type: 'string',
501
+ extraction: { method: 'ast', source: 'getName' },
502
+ neo4j: { indexed: true, unique: false, required: true },
503
+ },
504
+ ],
505
+ relationships: [],
506
+ children: {},
507
+ neo4j: {
508
+ labels: ['TypeAlias', 'TypeScript'],
509
+ primaryLabel: 'TypeAlias',
510
+ indexed: ['name'],
511
+ skipEmbedding: true,
512
+ },
513
+ },
483
514
  [CoreNodeType.CONSTRUCTOR_DECLARATION]: {
484
515
  coreType: CoreNodeType.CONSTRUCTOR_DECLARATION,
485
516
  astNodeKind: 175,
@@ -767,8 +798,16 @@ export const CORE_TYPESCRIPT_SCHEMA = {
767
798
  },
768
799
  [CoreEdgeType.CALLS]: {
769
800
  coreType: CoreEdgeType.CALLS,
770
- sourceTypes: [CoreNodeType.METHOD_DECLARATION, CoreNodeType.FUNCTION_DECLARATION],
771
- targetTypes: [CoreNodeType.METHOD_DECLARATION, CoreNodeType.FUNCTION_DECLARATION],
801
+ sourceTypes: [
802
+ CoreNodeType.METHOD_DECLARATION,
803
+ CoreNodeType.FUNCTION_DECLARATION,
804
+ CoreNodeType.CONSTRUCTOR_DECLARATION,
805
+ ],
806
+ targetTypes: [
807
+ CoreNodeType.METHOD_DECLARATION,
808
+ CoreNodeType.FUNCTION_DECLARATION,
809
+ CoreNodeType.CONSTRUCTOR_DECLARATION,
810
+ ],
772
811
  properties: [
773
812
  {
774
813
  name: 'confidence',