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.
- package/README.md +156 -2
- package/dist/constants.js +167 -0
- package/dist/core/config/fairsquare-framework-schema.js +9 -7
- package/dist/core/config/nestjs-framework-schema.js +60 -43
- package/dist/core/config/schema.js +41 -2
- package/dist/core/embeddings/natural-language-to-cypher.service.js +166 -110
- package/dist/core/parsers/typescript-parser.js +1043 -747
- package/dist/core/parsers/workspace-parser.js +177 -194
- package/dist/core/utils/code-normalizer.js +299 -0
- package/dist/core/utils/file-change-detection.js +17 -2
- package/dist/core/utils/file-utils.js +40 -5
- package/dist/core/utils/graph-factory.js +161 -0
- package/dist/core/utils/shared-utils.js +79 -0
- package/dist/core/workspace/workspace-detector.js +59 -5
- package/dist/mcp/constants.js +141 -8
- package/dist/mcp/handlers/graph-generator.handler.js +1 -0
- package/dist/mcp/handlers/incremental-parse.handler.js +3 -6
- package/dist/mcp/handlers/parallel-import.handler.js +136 -0
- package/dist/mcp/handlers/streaming-import.handler.js +14 -59
- package/dist/mcp/mcp.server.js +1 -1
- package/dist/mcp/services/job-manager.js +5 -8
- package/dist/mcp/services/watch-manager.js +7 -18
- package/dist/mcp/tools/detect-dead-code.tool.js +413 -0
- package/dist/mcp/tools/detect-duplicate-code.tool.js +450 -0
- package/dist/mcp/tools/impact-analysis.tool.js +20 -4
- package/dist/mcp/tools/index.js +4 -0
- package/dist/mcp/tools/parse-typescript-project.tool.js +15 -14
- package/dist/mcp/workers/chunk-worker-pool.js +196 -0
- package/dist/mcp/workers/chunk-worker.types.js +4 -0
- package/dist/mcp/workers/chunk.worker.js +89 -0
- package/dist/mcp/workers/parse-coordinator.js +183 -0
- package/dist/mcp/workers/worker.pool.js +54 -0
- package/dist/storage/neo4j/neo4j.service.js +190 -10
- 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.
|
|
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,
|
|
757
|
-
if (
|
|
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,
|
|
774
|
-
if (
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
|
290
|
-
if (!
|
|
310
|
+
const targetName = targetNode.properties?.name;
|
|
311
|
+
if (!targetName)
|
|
291
312
|
return null;
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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: [
|
|
771
|
-
|
|
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',
|