@sun-asterisk/impact-analyzer 1.0.4 → 1.0.5
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/.github/copilot-instructions.md +116 -0
- package/.github/prompts/README.md +91 -0
- package/.github/prompts/task-001-refactor.prompt.md +241 -0
- package/.specify/bugs/bug-001-database-detector.md +222 -0
- package/.specify/plans/architecture.md +186 -0
- package/.specify/specs/features/api-impact-detection.md +317 -0
- package/.specify/specs/features/component-impact-detection.md +263 -0
- package/.specify/specs/features/database-impact-detection.md +247 -0
- package/.specify/tasks/task-001-refactor-api-detector.md +284 -0
- package/.specify/tasks/task-002-database-detector.md +593 -0
- package/.specify/tasks/task-003-component-detector.md +0 -0
- package/.specify/tasks/task-004-report.md +484 -0
- package/README.md +13 -19
- package/core/detectors/database-detector.js +702 -0
- package/{modules → core}/detectors/endpoint-detector.js +11 -8
- package/{modules → core}/report-generator.js +102 -20
- package/core/utils/logger.js +12 -0
- package/index.js +6 -5
- package/package.json +1 -1
- package/modules/detectors/database-detector.js +0 -182
- /package/{modules → core}/change-detector.js +0 -0
- /package/{modules → core}/impact-analyzer.js +0 -0
- /package/{modules → core}/utils/ast-parser.js +0 -0
- /package/{modules → core}/utils/dependency-graph.js +0 -0
- /package/{modules → core}/utils/file-utils.js +0 -0
- /package/{modules → core}/utils/git-utils.js +0 -0
- /package/{modules → core}/utils/method-call-graph.js +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Impact Analyzer - Architecture
|
|
2
|
+
|
|
3
|
+
## What This Tool Does
|
|
4
|
+
|
|
5
|
+
Analyzes code changes in TypeScript/JavaScript projects to identify impacts on APIs, databases, and logic dependencies. Generates severity-based reports (LOW/MEDIUM/HIGH/CRITICAL).
|
|
6
|
+
|
|
7
|
+
## System Architecture
|
|
8
|
+
|
|
9
|
+
High-level flow through 4 stages:
|
|
10
|
+
|
|
11
|
+
**Stage 1: Change Detection** (`change-detector.js`)
|
|
12
|
+
- Parse git diff to find changed files
|
|
13
|
+
- Extract modified symbols using AST parser
|
|
14
|
+
- Output: List of changed methods/functions/classes
|
|
15
|
+
|
|
16
|
+
**Stage 2: Call Graph Building** (`method-call-graph.js`, `dependency-graph.js`)
|
|
17
|
+
- Parse all source files with ts-morph
|
|
18
|
+
- Build method-to-caller relationships
|
|
19
|
+
- Build method-to-callee relationships
|
|
20
|
+
- Map HTTP decorators to methods
|
|
21
|
+
- Output: Complete call graph
|
|
22
|
+
|
|
23
|
+
**Stage 3: Impact Detection** (`detectors/`)
|
|
24
|
+
- Endpoint Detector: Traverse call graph upward to find affected API endpoints
|
|
25
|
+
- Database Detector: Detect DB operations and extract table/field information
|
|
26
|
+
- Output: Affected endpoints + DB impacts
|
|
27
|
+
|
|
28
|
+
**Stage 4: Report Generation** (`report-generator.js`)
|
|
29
|
+
- Calculate impact score based on endpoints, DB ops, and callers
|
|
30
|
+
- Apply severity multipliers
|
|
31
|
+
- Generate console/markdown/JSON reports
|
|
32
|
+
- Output: Final impact report with severity level
|
|
33
|
+
|
|
34
|
+
## File Structure
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
core/
|
|
38
|
+
├── change-detector.js # Git diff + AST extraction
|
|
39
|
+
├── impact-analyzer.js # Main orchestrator
|
|
40
|
+
├── report-generator.js # Score calculation + output
|
|
41
|
+
├── detectors/
|
|
42
|
+
│ ├── endpoint-detector.js # API impact via call graph traversal
|
|
43
|
+
│ └── database-detector.js # DB impact via pattern detection
|
|
44
|
+
└── utils/
|
|
45
|
+
├── ast-parser.js # Parse TS/JS with ts-morph
|
|
46
|
+
├── method-call-graph.js # Build method-level call graphs
|
|
47
|
+
├── dependency-graph.js # Track file/module dependencies
|
|
48
|
+
├── file-utils.js # File system operations
|
|
49
|
+
└── git-utils.js # Git operations (diff, show)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Key Algorithms
|
|
53
|
+
|
|
54
|
+
### 1. Call Graph Traversal (Endpoint Detection)
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
// Find endpoints affected by changed method
|
|
58
|
+
function findAffectedEndpoints(changedMethod, callGraph) {
|
|
59
|
+
const visited = new Set();
|
|
60
|
+
const endpoints = [];
|
|
61
|
+
|
|
62
|
+
// Traverse upward through callers
|
|
63
|
+
function traverseUp(method) {
|
|
64
|
+
if (visited.has(method)) return;
|
|
65
|
+
visited.add(method);
|
|
66
|
+
|
|
67
|
+
const callers = callGraph.getCallers(method);
|
|
68
|
+
for (const caller of callers) {
|
|
69
|
+
// Check if this caller is an endpoint
|
|
70
|
+
if (hasHttpDecorator(caller)) {
|
|
71
|
+
endpoints.push(extractEndpointInfo(caller));
|
|
72
|
+
} else {
|
|
73
|
+
// Continue traversing up
|
|
74
|
+
traverseUp(caller);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
traverseUp(changedMethod);
|
|
80
|
+
return endpoints;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 2. Database Impact Detection
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// Detect DB operations in repository methods
|
|
88
|
+
function detectDatabaseImpact(changedFile, ast) {
|
|
89
|
+
const impacts = [];
|
|
90
|
+
|
|
91
|
+
// Find repository layer methods
|
|
92
|
+
const repoMethods = findRepositoryMethods(ast);
|
|
93
|
+
|
|
94
|
+
for (const method of repoMethods) {
|
|
95
|
+
// Look for ORM calls: find, save, update, delete
|
|
96
|
+
const dbCalls = findDatabaseCalls(method);
|
|
97
|
+
|
|
98
|
+
for (const call of dbCalls) {
|
|
99
|
+
impacts.push({
|
|
100
|
+
table: extractTableName(call),
|
|
101
|
+
fields: extractFields(call),
|
|
102
|
+
operation: getOperationType(call), // READ/WRITE
|
|
103
|
+
orm: detectORM(call) // TypeORM/Prisma/etc
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return impacts;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 3. Impact Score Calculation
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
function calculateImpactScore(impacts) {
|
|
116
|
+
let score = 0;
|
|
117
|
+
|
|
118
|
+
// Base scoring
|
|
119
|
+
score += impacts.endpoints.length * 10;
|
|
120
|
+
score += impacts.dbTables.length * 5;
|
|
121
|
+
score += impacts.directCallers.length * 3;
|
|
122
|
+
score += impacts.indirectCallers.length * 1;
|
|
123
|
+
|
|
124
|
+
// Apply multipliers
|
|
125
|
+
if (impacts.hasHighRiskLogic) {
|
|
126
|
+
score *= 1.5;
|
|
127
|
+
}
|
|
128
|
+
if (impacts.hasDatabaseMigration) {
|
|
129
|
+
score += 20;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Determine severity
|
|
133
|
+
const severity =
|
|
134
|
+
score < 21 ? 'LOW' :
|
|
135
|
+
score < 51 ? 'MEDIUM' :
|
|
136
|
+
score < 101 ? 'HIGH' : 'CRITICAL';
|
|
137
|
+
|
|
138
|
+
return { score, severity };
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Framework Support
|
|
143
|
+
|
|
144
|
+
### Supported Patterns
|
|
145
|
+
|
|
146
|
+
**Backend Frameworks:**
|
|
147
|
+
- NestJS: `@Controller`, `@Get`, `@Post`, `@Put`, `@Delete`
|
|
148
|
+
- Express: `router.get()`, `router.post()`, etc.
|
|
149
|
+
- Fastify: `fastify.get()`, `fastify.post()`, etc.
|
|
150
|
+
|
|
151
|
+
**ORM Frameworks:**
|
|
152
|
+
- TypeORM: `@Entity`, `@Table`, `find()`, `save()`, `update()`
|
|
153
|
+
- Prisma: `prisma.model.findMany()`, `prisma.model.create()`
|
|
154
|
+
- Sequelize: `Model.findAll()`, `Model.create()`
|
|
155
|
+
|
|
156
|
+
**Detection Strategy:**
|
|
157
|
+
1. Parse decorators (`@Controller`, `@Entity`)
|
|
158
|
+
2. Match method call patterns (`find`, `save`)
|
|
159
|
+
3. Extract metadata (route paths, table names)
|
|
160
|
+
|
|
161
|
+
## Design Principles
|
|
162
|
+
|
|
163
|
+
**1. Single Responsibility**
|
|
164
|
+
- Each module has one clear purpose
|
|
165
|
+
- Detectors are independent and composable
|
|
166
|
+
|
|
167
|
+
**2. Layer-Aware Analysis**
|
|
168
|
+
```
|
|
169
|
+
Database Layer (Repository)
|
|
170
|
+
↓
|
|
171
|
+
Business Layer (Service)
|
|
172
|
+
↓
|
|
173
|
+
Presentation Layer (Controller)
|
|
174
|
+
↓
|
|
175
|
+
API Endpoints
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**3. Optimized Parsing**
|
|
179
|
+
- Only parse source files (skip node_modules)
|
|
180
|
+
- Build call graph once, reuse for all detectors
|
|
181
|
+
- Stream processing for large codebases
|
|
182
|
+
|
|
183
|
+
**4. Extensible Detection**
|
|
184
|
+
- Add new detectors without modifying core
|
|
185
|
+
- Framework patterns defined in config
|
|
186
|
+
- Custom rules per project
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# Feature Spec: API Impact Detection
|
|
2
|
+
|
|
3
|
+
## What It Does
|
|
4
|
+
|
|
5
|
+
Finds API endpoints affected by code changes using call graph traversal.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
**Input:** Changed methods from git diff
|
|
10
|
+
**Output:** List of affected API endpoints with HTTP method and path
|
|
11
|
+
|
|
12
|
+
### Algorithm
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
function findAffectedEndpoints(changedMethod, callGraph) {
|
|
16
|
+
// 1. Start from changed method
|
|
17
|
+
// 2. Traverse upward through callers
|
|
18
|
+
// 3. Stop when reaching method with HTTP decorator
|
|
19
|
+
// 4. Extract endpoint info (method, path)
|
|
20
|
+
|
|
21
|
+
const endpoints = [];
|
|
22
|
+
const visited = new Set();
|
|
23
|
+
|
|
24
|
+
function traverse(method) {
|
|
25
|
+
if (visited.has(method)) return;
|
|
26
|
+
visited.add(method);
|
|
27
|
+
|
|
28
|
+
// Check if this is an endpoint
|
|
29
|
+
if (hasHttpDecorator(method)) {
|
|
30
|
+
endpoints.push({
|
|
31
|
+
httpMethod: extractHttpMethod(method),
|
|
32
|
+
path: extractPath(method),
|
|
33
|
+
controller: method.name
|
|
34
|
+
});
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Continue traversing up
|
|
39
|
+
const callers = callGraph.getCallers(method);
|
|
40
|
+
callers.forEach(traverse);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
traverse(changedMethod);
|
|
44
|
+
return endpoints;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Flow Example
|
|
49
|
+
|
|
50
|
+
**Synchronous (Direct Call):**
|
|
51
|
+
```
|
|
52
|
+
Changed: UserService.updateEmail()
|
|
53
|
+
↓
|
|
54
|
+
Caller: UserController.updateProfile()
|
|
55
|
+
↓
|
|
56
|
+
Check: Has @Put(':id') decorator
|
|
57
|
+
↓
|
|
58
|
+
Output: PUT /users/:id
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Asynchronous (Command/Queue):**
|
|
62
|
+
```
|
|
63
|
+
Changed: OrderService.processOrder()
|
|
64
|
+
↓
|
|
65
|
+
Caller: ProcessOrderHandler.handle()
|
|
66
|
+
↓
|
|
67
|
+
Check: Has @Process('processOrder') decorator
|
|
68
|
+
↓
|
|
69
|
+
Search: Find where 'processOrder' command is published
|
|
70
|
+
↓
|
|
71
|
+
Found: OrderController.create() with queue.add('processOrder')
|
|
72
|
+
↓
|
|
73
|
+
Check: Has @Post('orders') decorator
|
|
74
|
+
↓
|
|
75
|
+
Output: POST /orders (via command: processOrder)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Supported Frameworks
|
|
79
|
+
|
|
80
|
+
### NestJS (Primary)
|
|
81
|
+
|
|
82
|
+
**HTTP Decorators:**
|
|
83
|
+
- `@Controller('path')` - Base path
|
|
84
|
+
- `@Get('subpath')` - GET method
|
|
85
|
+
- `@Post('subpath')` - POST method
|
|
86
|
+
- `@Put('subpath')` - PUT method
|
|
87
|
+
- `@Delete('subpath')` - DELETE method
|
|
88
|
+
- `@Patch('subpath')` - PATCH method
|
|
89
|
+
|
|
90
|
+
**Queue/Command Decorators:**
|
|
91
|
+
- `@Process('commandName')` - Queue processor
|
|
92
|
+
- `@OnQueueCompleted()` - Queue event handler
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
// Detection logic for HTTP endpoints
|
|
96
|
+
function hasHttpDecorator(method) {
|
|
97
|
+
const decorators = ['Get', 'Post', 'Put', 'Delete', 'Patch'];
|
|
98
|
+
return method.decorators.some(d => decorators.includes(d.name));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Detection logic for queue handlers
|
|
102
|
+
function isQueueHandler(method) {
|
|
103
|
+
return method.decorators.some(d => d.name === 'Process');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function extractCommandName(method) {
|
|
107
|
+
const processDecorator = method.decorators.find(d => d.name === 'Process');
|
|
108
|
+
return processDecorator?.arguments[0]; // Returns 'processOrder'
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function extractEndpointInfo(method) {
|
|
112
|
+
const httpDecorator = method.decorators.find(d =>
|
|
113
|
+
['Get', 'Post', 'Put', 'Delete', 'Patch'].includes(d.name)
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const controllerDecorator = method.class.decorators.find(d =>
|
|
117
|
+
d.name === 'Controller'
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
method: httpDecorator.name.toUpperCase(),
|
|
122
|
+
path: joinPath(
|
|
123
|
+
controllerDecorator.arguments[0],
|
|
124
|
+
httpDecorator.arguments[0]
|
|
125
|
+
)
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Express
|
|
131
|
+
|
|
132
|
+
Detect route definitions:
|
|
133
|
+
- `router.get(path, handler)`
|
|
134
|
+
- `router.post(path, handler)`
|
|
135
|
+
- `app.get(path, handler)`
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
// Detection logic
|
|
139
|
+
function isExpressRoute(callExpression) {
|
|
140
|
+
const methods = ['get', 'post', 'put', 'delete', 'patch'];
|
|
141
|
+
return callExpression.expression.object.name === 'router' &&
|
|
142
|
+
methods.includes(callExpression.expression.property.name);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Async Patterns (Command/Queue)
|
|
147
|
+
|
|
148
|
+
### Queue Publishers
|
|
149
|
+
|
|
150
|
+
Detect queue/command publishing patterns:
|
|
151
|
+
- BullMQ: `queue.add('commandName', data)`
|
|
152
|
+
- NestJS Queue: `await this.queue.add('commandName', data)`
|
|
153
|
+
- AWS SQS: `await sqs.send({ command: 'commandName' })`
|
|
154
|
+
- AWS SNS: `await sns.send({ message: { command: 'commandName' } })`
|
|
155
|
+
- Command Bus: `await commandBus.execute('commandName', data)`
|
|
156
|
+
|
|
157
|
+
### Detection Algorithm for Async Flow
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
function findAsyncEndpoints(changedMethod, callGraph) {
|
|
161
|
+
const endpoints = [];
|
|
162
|
+
|
|
163
|
+
// 1. Traverse up to find queue handler
|
|
164
|
+
const handler = findQueueHandler(changedMethod, callGraph);
|
|
165
|
+
if (!handler) return endpoints;
|
|
166
|
+
|
|
167
|
+
// 2. Extract command name from handler decorator
|
|
168
|
+
const commandName = extractCommandName(handler);
|
|
169
|
+
if (!commandName) return endpoints;
|
|
170
|
+
|
|
171
|
+
// 3. Search for publishers of this command
|
|
172
|
+
const publishers = findCommandPublishers(commandName);
|
|
173
|
+
|
|
174
|
+
// 4. For each publisher, find the endpoint
|
|
175
|
+
for (const publisher of publishers) {
|
|
176
|
+
const endpoint = findEndpointForMethod(publisher, callGraph);
|
|
177
|
+
if (endpoint) {
|
|
178
|
+
endpoints.push({
|
|
179
|
+
...endpoint,
|
|
180
|
+
flow: 'async',
|
|
181
|
+
command: commandName,
|
|
182
|
+
handler: handler.name
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return endpoints;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function findQueueHandler(method, callGraph) {
|
|
191
|
+
const callers = callGraph.getCallers(method);
|
|
192
|
+
for (const caller of callers) {
|
|
193
|
+
if (isQueueHandler(caller)) {
|
|
194
|
+
return caller;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function findCommandPublishers(commandName) {
|
|
201
|
+
// Search all files for queue.add('commandName')
|
|
202
|
+
const publishers = [];
|
|
203
|
+
const pattern = new RegExp(`\\.add\\(['"\`]${commandName}['"\`]`);
|
|
204
|
+
|
|
205
|
+
// Use AST to find call expressions matching pattern
|
|
206
|
+
// Returns array of methods that publish this command
|
|
207
|
+
|
|
208
|
+
return publishers;
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Example: Async Flow
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// Controller publishes command
|
|
216
|
+
@Controller('orders')
|
|
217
|
+
class OrderController {
|
|
218
|
+
@Post()
|
|
219
|
+
async create(@Body() dto: CreateOrderDto) {
|
|
220
|
+
const command = 'processOrder';
|
|
221
|
+
await this.sns.publishMessage({
|
|
222
|
+
message: { command: command, payload: dto }
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
return { success: true };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Case 1. Handler processes command
|
|
230
|
+
@Processor('orderQueue')
|
|
231
|
+
class OrderProcessor {
|
|
232
|
+
@Process('processOrder')
|
|
233
|
+
async handle(job: Job) {
|
|
234
|
+
await this.orderService.processOrder(job.data); // Changed method
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Case 2. Handler command by nest
|
|
239
|
+
@command({name: 'processOrder'})
|
|
240
|
+
export class OrderCommand extends CommonComand {
|
|
241
|
+
async run() {
|
|
242
|
+
await this.orderService.processOrder(job.data); // Changed method
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Service method (changed)
|
|
246
|
+
class OrderService {
|
|
247
|
+
async processOrder(data) {
|
|
248
|
+
// Implementation changed here
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Detection result:
|
|
253
|
+
// POST /orders (via command: processOrder)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Output Structure
|
|
257
|
+
|
|
258
|
+
**Synchronous Endpoint:**
|
|
259
|
+
```javascript
|
|
260
|
+
{
|
|
261
|
+
type: 'endpoint',
|
|
262
|
+
flow: 'sync',
|
|
263
|
+
method: 'PUT', // HTTP method
|
|
264
|
+
path: '/users/:id', // Route path
|
|
265
|
+
controller: 'UserController.updateProfile',
|
|
266
|
+
changedMethod: 'UserService.updateEmail',
|
|
267
|
+
file: 'src/services/user.service.js'
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Asynchronous Endpoint:**
|
|
272
|
+
```javascript
|
|
273
|
+
{
|
|
274
|
+
type: 'endpoint',
|
|
275
|
+
flow: 'async',
|
|
276
|
+
method: 'POST',
|
|
277
|
+
path: '/orders',
|
|
278
|
+
controller: 'OrderController.create',
|
|
279
|
+
command: 'processOrder', // Command name
|
|
280
|
+
handler: 'OrderProcessor.handle', // Handler method
|
|
281
|
+
changedMethod: 'OrderService.processOrder',
|
|
282
|
+
file: 'src/services/order.service.js'
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Implementation Notes
|
|
287
|
+
|
|
288
|
+
**Call Graph Building:**
|
|
289
|
+
- Use `ts-morph` to parse TypeScript/JavaScript files
|
|
290
|
+
- Build reverse map: method → callers
|
|
291
|
+
- Store decorator information on methods
|
|
292
|
+
- Store command names from queue operations
|
|
293
|
+
|
|
294
|
+
**Traversal Strategy:**
|
|
295
|
+
- BFS (breadth-first search) to find closest endpoints first
|
|
296
|
+
- Stop at HTTP decorators (sync) or queue handlers (async)
|
|
297
|
+
- Track visited nodes to avoid cycles
|
|
298
|
+
- Max depth: 10 levels
|
|
299
|
+
|
|
300
|
+
**Async Detection Strategy:**
|
|
301
|
+
1. Traverse up from changed method to find queue handler
|
|
302
|
+
2. Extract command name from `@Process('commandName')`
|
|
303
|
+
3. Search codebase for `queue.add('commandName')` patterns
|
|
304
|
+
4. Find endpoint that publishes the command
|
|
305
|
+
5. Combine handler info with endpoint info
|
|
306
|
+
|
|
307
|
+
**Path Construction:**
|
|
308
|
+
- Combine `@Controller` path with HTTP decorator path
|
|
309
|
+
- Handle dynamic segments (`:id`, `:slug`)
|
|
310
|
+
- Normalize paths (remove trailing slashes)
|
|
311
|
+
|
|
312
|
+
**Edge Cases:**
|
|
313
|
+
- Skip if circular dependency detected
|
|
314
|
+
- Skip if max depth reached
|
|
315
|
+
- Report multiple endpoints if method has multiple callers
|
|
316
|
+
- For async: Skip if command name is dynamic/variable
|
|
317
|
+
- For async: Mark as external if command publisher not found
|