@sun-asterisk/impact-analyzer 1.0.4 → 1.0.6

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 (29) hide show
  1. package/.github/copilot-instructions.md +116 -0
  2. package/.github/prompts/README.md +91 -0
  3. package/.github/prompts/task-001-refactor.prompt.md +241 -0
  4. package/.specify/bugs/bug-001-database-detector.md +222 -0
  5. package/.specify/bugs/bug-002-database-detector.md +478 -0
  6. package/.specify/bugs/bug-003-multiline-detection.md +527 -0
  7. package/.specify/plans/architecture.md +186 -0
  8. package/.specify/specs/features/api-impact-detection.md +317 -0
  9. package/.specify/specs/features/component-impact-detection.md +263 -0
  10. package/.specify/specs/features/database-impact-detection.md +247 -0
  11. package/.specify/tasks/task-001-refactor-api-detector.md +284 -0
  12. package/.specify/tasks/task-002-database-detector.md +593 -0
  13. package/.specify/tasks/task-003-component-detector.md +0 -0
  14. package/.specify/tasks/task-004-report.md +484 -0
  15. package/README.md +13 -19
  16. package/core/detectors/database-detector.js +912 -0
  17. package/{modules → core}/detectors/endpoint-detector.js +11 -8
  18. package/{modules → core}/report-generator.js +102 -20
  19. package/core/utils/logger.js +12 -0
  20. package/index.js +6 -5
  21. package/package.json +1 -1
  22. package/modules/detectors/database-detector.js +0 -182
  23. /package/{modules → core}/change-detector.js +0 -0
  24. /package/{modules → core}/impact-analyzer.js +0 -0
  25. /package/{modules → core}/utils/ast-parser.js +0 -0
  26. /package/{modules → core}/utils/dependency-graph.js +0 -0
  27. /package/{modules → core}/utils/file-utils.js +0 -0
  28. /package/{modules → core}/utils/git-utils.js +0 -0
  29. /package/{modules → core}/utils/method-call-graph.js +0 -0
@@ -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
@@ -0,0 +1,263 @@
1
+ # Feature Spec: Component Impact Detection
2
+
3
+ ## 1. Overview
4
+
5
+ Identify UI components affected by code changes. This feature traces component dependencies to find parent components that may be impacted by changes to child components.
6
+
7
+ ## 2. User Story
8
+
9
+ > As a developer reviewing a pull request,
10
+ > I want to see which UI components are affected by the code changes,
11
+ > So that I can plan visual testing and assess user-facing impact.
12
+
13
+ ## 3. Functional Requirements
14
+
15
+ ### FR-001: Detect Component Definitions
16
+ - **Description**: Identify React/Vue/Angular component definitions
17
+ - **Patterns to Support**:
18
+ - React functional components
19
+ - React class components
20
+ - React. FC type annotation
21
+ - Vue Single File Components (SFC)
22
+ - Vue Composition API (script setup)
23
+ - Angular @Component decorator
24
+ - **Acceptance Criteria**:
25
+ - Detects function components (named and arrow)
26
+ - Detects class components extending React.Component
27
+ - Detects Vue SFC script blocks
28
+ - Detects Angular component classes
29
+
30
+ ### FR-002: Build Component Dependency Tree
31
+ - **Description**: Map component import/usage relationships
32
+ - **Input**: Changed component names
33
+ - **Output**: Tree of parent components
34
+ - **Acceptance Criteria**:
35
+ - Traces JSX/TSX component usage
36
+ - Resolves import statements
37
+ - Handles re-exports
38
+ - Handles dynamic imports
39
+
40
+ ### FR-003: Detect Props/Interface Changes
41
+ - **Description**: Identify changes to component props
42
+ - **Patterns to Support**:
43
+ - TypeScript interface: `interface Props { }`
44
+ - TypeScript type: `type Props = { }`
45
+ - PropTypes definition
46
+ - Vue defineProps
47
+ - Angular @Input() decorator
48
+ - **Acceptance Criteria**:
49
+ - Detects added props
50
+ - Detects removed props
51
+ - Detects prop type changes
52
+ - Flags breaking prop changes
53
+
54
+ ### FR-004: Find Parent Components
55
+ - **Description**: Locate all components that use the changed component
56
+ - **Input**: Changed component name and file
57
+ - **Output**: List of parent components with usage count
58
+ - **Acceptance Criteria**:
59
+ - Searches all component files in project
60
+ - Finds JSX usage: `<ComponentName />`
61
+ - Handles aliased imports
62
+ - Counts usage instances
63
+
64
+ ### FR-005: Classify Component Type
65
+ - **Description**: Categorize the component framework and style
66
+ - **Types**:
67
+ - `react-functional`: React function components
68
+ - `react-class`: React class components
69
+ - `vue-sfc`: Vue Single File Components
70
+ - `vue-composition`: Vue Composition API
71
+ - `angular`: Angular components
72
+ - **Acceptance Criteria**:
73
+ - Correctly identifies React components
74
+ - Correctly identifies Vue components
75
+ - Correctly identifies Angular components
76
+
77
+ ### FR-006: Generate Component Report
78
+ - **Description**: Compile component impacts into structured report
79
+ - **Output**: `ComponentImpact[]` array
80
+ - **Acceptance Criteria**:
81
+ - Includes component name and type
82
+ - Includes parent component list
83
+ - Indicates props changes
84
+ - Includes severity based on usage
85
+
86
+ ## 4. Non-Functional Requirements
87
+
88
+ ### NFR-001: Performance
89
+ - Analysis should add <10 seconds to total analysis time
90
+ - Should handle projects with 500+ components
91
+
92
+ ### NFR-002: Accuracy
93
+ - Should detect >90% of component changes
94
+ - Should find >85% of parent component relationships
95
+
96
+ ## 5. Output Schema
97
+
98
+ ```javascript
99
+ /**
100
+ * @typedef {Object} ComponentImpact
101
+ * @property {string} componentName - Component name
102
+ * @property {string} filePath - Path to component file
103
+ * @property {'react-functional'|'react-class'|'vue-sfc'|'vue-composition'|'angular'} componentType
104
+ * @property {ParentComponent[]} parentComponents
105
+ * @property {boolean} propsChanged - Whether props interface was modified
106
+ * @property {FileChange} changeSource
107
+ * @property {'low'|'medium'|'high'|'critical'} severity
108
+ */
109
+
110
+ /**
111
+ * @typedef {Object} ParentComponent
112
+ * @property {string} name - Parent component name
113
+ * @property {string} filePath - Path to parent component
114
+ * @property {number} usageCount - How many times child is used
115
+ */
116
+ ```
117
+
118
+ ## 6. Severity Classification
119
+
120
+ | Severity | Criteria |
121
+ |----------|----------|
122
+ | Critical | Props changed + >10 parent components |
123
+ | High | Props changed OR >10 parent components |
124
+ | Medium | 3-10 parent components |
125
+ | Low | <3 parent components |
126
+
127
+ ## 7. Component Detection Patterns
128
+
129
+ ### React Functional Components
130
+ ```javascript
131
+ // Named function
132
+ function Button({ label }) {
133
+ return <button>{label}</button>;
134
+ }
135
+
136
+ // Arrow function
137
+ const Button = ({ label }) => <button>{label}</button>;
138
+
139
+ // React. FC type
140
+ const Button: React.FC<ButtonProps> = ({ label }) => { };
141
+
142
+ // forwardRef
143
+ const Button = forwardRef((props, ref) => { });
144
+
145
+ // memo
146
+ const Button = memo(({ label }) => { });
147
+ ```
148
+
149
+ ### React Class Components
150
+ ```javascript
151
+ class Button extends React.Component { }
152
+ class Button extends Component { }
153
+ class Button extends React.PureComponent { }
154
+ ```
155
+
156
+ ### Vue Components
157
+ ```vue
158
+ <!-- Options API -->
159
+ <script>
160
+ export default {
161
+ name: 'Button',
162
+ props: { label: String }
163
+ }
164
+ </script>
165
+
166
+ <!-- Composition API -->
167
+ <script setup>
168
+ defineProps({ label: String })
169
+ </script>
170
+ ```
171
+
172
+ ### Angular Components
173
+ ```typescript
174
+ @Component({
175
+ selector: 'app-button',
176
+ template: '<button>{{label}}</button>'
177
+ })
178
+ export class ButtonComponent {
179
+ @Input() label: string;
180
+ }
181
+ ```
182
+
183
+ ## 8. Test Scenarios
184
+
185
+ ### Scenario 1: Direct Component Change
186
+ - **Given**: A component's JSX is modified
187
+ - **When**: Analysis runs
188
+ - **Then**: Component is detected with its parent components
189
+
190
+ ### Scenario 2: Props Interface Change
191
+ - **Given**: A component's Props interface is modified
192
+ - **When**: Analysis runs
193
+ - **Then**: `propsChanged` is true and severity is elevated
194
+
195
+ ### Scenario 3: Shared Component Change
196
+ - **Given**: A shared component (used by 15+ parents) is modified
197
+ - **When**: Analysis runs
198
+ - **Then**: All parent components are listed, severity is high
199
+
200
+ ### Scenario 4: Isolated Component Change
201
+ - **Given**: A component with no parents is modified
202
+ - **When**: Analysis runs
203
+ - **Then**: Component is detected with empty parent list
204
+
205
+ ### Scenario 5: Style-Only Change
206
+ - **Given**: Only CSS/styles in a component file change
207
+ - **When**: Analysis runs
208
+ - **Then**: Component is detected with low severity
209
+
210
+ ## 9. Edge Cases
211
+
212
+ | Case | Expected Behavior |
213
+ |------|-------------------|
214
+ | Higher-order components | Detect both wrapper and wrapped |
215
+ | Render props | Detect component, not render function |
216
+ | Context providers | Detect as component |
217
+ | CSS-only files | Skip, not a component |
218
+ | Test files | Skip (excluded by pattern) |
219
+ | Storybook files | Skip (excluded by pattern) |
220
+
221
+ ## 10. Example
222
+
223
+ ### Input
224
+ ```javascript
225
+ // Changed file: src/components/Button/Button.jsx
226
+ // Modified lines: 5-8
227
+
228
+ /**
229
+ * @typedef {Object} ButtonProps
230
+ * @property {string} label
231
+ * @property {string} variant // Line 5 - ADDED
232
+ * @property {() => void} onClick
233
+ */
234
+
235
+ function Button({ label, variant, onClick }) { // Line 8 - CHANGED
236
+ return (
237
+ <button className={variant} onClick={onClick}>
238
+ {label}
239
+ </button>
240
+ );
241
+ }
242
+ ```
243
+
244
+ ### Expected Output
245
+ ```javascript
246
+ {
247
+ component: [
248
+ {
249
+ componentName: "Button",
250
+ filePath: "src/components/Button/Button.jsx",
251
+ componentType: "react-functional",
252
+ parentComponents: [
253
+ { name: "Header", filePath: "src/components/Header. jsx", usageCount: 2 },
254
+ { name: "Modal", filePath: "src/components/Modal.jsx", usageCount: 1 },
255
+ { name: "Form", filePath: "src/components/Form.jsx", usageCount: 3 }
256
+ ],
257
+ propsChanged: true,
258
+ changeSource: { path: "src/components/Button/Button.jsx", ... },
259
+ severity: "medium"
260
+ }
261
+ ]
262
+ }
263
+ ```