@ace-sdk/core 2.0.1 → 2.1.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/dist/cache/project-index.d.ts +152 -0
- package/dist/cache/project-index.d.ts.map +1 -0
- package/dist/cache/project-index.js +290 -0
- package/dist/cache/project-index.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/services/bootstrap-stream.d.ts +113 -0
- package/dist/services/bootstrap-stream.d.ts.map +1 -0
- package/dist/services/bootstrap-stream.js +261 -0
- package/dist/services/bootstrap-stream.js.map +1 -0
- package/dist/services/import-graph.d.ts +111 -0
- package/dist/services/import-graph.d.ts.map +1 -0
- package/dist/services/import-graph.js +292 -0
- package/dist/services/import-graph.js.map +1 -0
- package/dist/services/language-detector.d.ts +120 -0
- package/dist/services/language-detector.d.ts.map +1 -0
- package/dist/services/language-detector.js +210 -0
- package/dist/services/language-detector.js.map +1 -0
- package/dist/types/bootstrap-events.d.ts +251 -0
- package/dist/types/bootstrap-events.d.ts.map +1 -0
- package/dist/types/bootstrap-events.js +103 -0
- package/dist/types/bootstrap-events.js.map +1 -0
- package/dist/types/project-dna.d.ts +151 -0
- package/dist/types/project-dna.d.ts.map +1 -0
- package/dist/types/project-dna.js +42 -0
- package/dist/types/project-dna.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap Streaming Client
|
|
3
|
+
*
|
|
4
|
+
* Connects to server SSE endpoint for streaming bootstrap progress.
|
|
5
|
+
* Handles event parsing, timeout, and error recovery.
|
|
6
|
+
*
|
|
7
|
+
* Updated to match ACE Server v3.13.0 response format.
|
|
8
|
+
*
|
|
9
|
+
* @package @ace-sdk/core
|
|
10
|
+
*/
|
|
11
|
+
import { isTerminalEvent, isDoneEvent, isErrorEvent } from '../types/bootstrap-events.js';
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Bootstrap Stream Function
|
|
14
|
+
// =============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Stream bootstrap progress from ACE Server
|
|
17
|
+
*
|
|
18
|
+
* Connects to the server's SSE endpoint and receives progress updates
|
|
19
|
+
* as the bootstrap processes code blocks.
|
|
20
|
+
*
|
|
21
|
+
* @param options - Bootstrap streaming options
|
|
22
|
+
* @returns Promise that resolves when bootstrap completes or fails
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const result = await bootstrapWithStreaming({
|
|
27
|
+
* serverUrl: 'https://ace-api.code-engine.app',
|
|
28
|
+
* orgId: 'myorg',
|
|
29
|
+
* projectId: 'myproject',
|
|
30
|
+
* mode: 'hybrid',
|
|
31
|
+
* codeBlocks: ['function foo() { ... }', '// STRATEGY: Use DI'],
|
|
32
|
+
* onEvent: (event) => {
|
|
33
|
+
* console.log(`[${event.stage}] ${event.message}`);
|
|
34
|
+
* if (event.stage === 'done') {
|
|
35
|
+
* const stats = event.data as DoneStageData;
|
|
36
|
+
* console.log(`Patterns: ${stats.patterns_extracted}`);
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* if (result.success) {
|
|
42
|
+
* console.log(`Bootstrap complete: ${result.playbook?.totalPatterns} patterns`);
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export async function bootstrapWithStreaming(options) {
|
|
47
|
+
const { serverUrl, orgId, projectId, mode = 'hybrid', codeBlocks, metadata, projectDNA, apiToken, onEvent, onError, timeout = 120000, verbosity = 'compact' } = options;
|
|
48
|
+
const controller = new AbortController();
|
|
49
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
50
|
+
let result = { success: false };
|
|
51
|
+
try {
|
|
52
|
+
// Build request headers
|
|
53
|
+
const headers = {
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'Accept': 'text/event-stream',
|
|
56
|
+
'X-ACE-Org': orgId,
|
|
57
|
+
'X-ACE-Project': projectId
|
|
58
|
+
};
|
|
59
|
+
if (apiToken) {
|
|
60
|
+
headers['Authorization'] = `Bearer ${apiToken}`;
|
|
61
|
+
}
|
|
62
|
+
// Build request body (matches server v3.13.0 BootstrapStreamRequest)
|
|
63
|
+
const body = JSON.stringify({
|
|
64
|
+
mode,
|
|
65
|
+
code_blocks: codeBlocks,
|
|
66
|
+
metadata: metadata || {
|
|
67
|
+
project_dna: projectDNA
|
|
68
|
+
},
|
|
69
|
+
verbosity
|
|
70
|
+
});
|
|
71
|
+
// Make streaming request
|
|
72
|
+
const response = await fetch(`${serverUrl}/bootstrap/stream`, {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers,
|
|
75
|
+
body,
|
|
76
|
+
signal: controller.signal
|
|
77
|
+
});
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw new Error(`Bootstrap request failed: ${response.status} ${response.statusText}`);
|
|
80
|
+
}
|
|
81
|
+
// Get response body reader
|
|
82
|
+
const reader = response.body?.getReader();
|
|
83
|
+
const decoder = new TextDecoder();
|
|
84
|
+
if (!reader) {
|
|
85
|
+
throw new Error('No response body available');
|
|
86
|
+
}
|
|
87
|
+
let buffer = '';
|
|
88
|
+
// Read SSE stream
|
|
89
|
+
while (true) {
|
|
90
|
+
const { done, value } = await reader.read();
|
|
91
|
+
if (done)
|
|
92
|
+
break;
|
|
93
|
+
// Append chunk to buffer
|
|
94
|
+
buffer += decoder.decode(value, { stream: true });
|
|
95
|
+
// Parse complete lines from buffer
|
|
96
|
+
const lines = buffer.split('\n');
|
|
97
|
+
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
// Skip empty lines and comments
|
|
100
|
+
if (!line || line.startsWith(':'))
|
|
101
|
+
continue;
|
|
102
|
+
// Parse SSE data line
|
|
103
|
+
if (line.startsWith('data: ')) {
|
|
104
|
+
const json = line.slice(6);
|
|
105
|
+
try {
|
|
106
|
+
const event = JSON.parse(json);
|
|
107
|
+
// Call user's event handler
|
|
108
|
+
onEvent(event);
|
|
109
|
+
// Check for terminal events
|
|
110
|
+
if (isTerminalEvent(event)) {
|
|
111
|
+
if (isDoneEvent(event)) {
|
|
112
|
+
const data = event.data;
|
|
113
|
+
result = {
|
|
114
|
+
success: data.success,
|
|
115
|
+
statistics: data.bootstrap_statistics,
|
|
116
|
+
playbook: {
|
|
117
|
+
totalPatterns: data.patterns_extracted,
|
|
118
|
+
bySection: data.bootstrap_statistics.by_section
|
|
119
|
+
},
|
|
120
|
+
processingTime: data.analysis_time_seconds
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
else if (isErrorEvent(event)) {
|
|
124
|
+
const data = event.data;
|
|
125
|
+
result = {
|
|
126
|
+
success: false,
|
|
127
|
+
error: {
|
|
128
|
+
code: data.error_code,
|
|
129
|
+
message: data.message,
|
|
130
|
+
retryable: data.retryable
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (parseError) {
|
|
138
|
+
console.warn('Failed to parse SSE event:', json, parseError);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Stream ended without terminal event
|
|
144
|
+
if (!result.success && !result.error) {
|
|
145
|
+
result = {
|
|
146
|
+
success: false,
|
|
147
|
+
error: {
|
|
148
|
+
code: 'STREAM_ENDED',
|
|
149
|
+
message: 'Stream ended without completion or error event',
|
|
150
|
+
retryable: true
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const err = error;
|
|
158
|
+
// Handle abort (timeout)
|
|
159
|
+
if (err.name === 'AbortError') {
|
|
160
|
+
result = {
|
|
161
|
+
success: false,
|
|
162
|
+
error: {
|
|
163
|
+
code: 'TIMEOUT',
|
|
164
|
+
message: `Bootstrap timed out after ${timeout}ms`,
|
|
165
|
+
retryable: true
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
result = {
|
|
171
|
+
success: false,
|
|
172
|
+
error: {
|
|
173
|
+
code: 'NETWORK_ERROR',
|
|
174
|
+
message: err.message,
|
|
175
|
+
retryable: true
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
// Call error handler if provided
|
|
180
|
+
if (onError) {
|
|
181
|
+
onError(err);
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
clearTimeout(timeoutId);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// =============================================================================
|
|
190
|
+
// Fallback: Non-Streaming Bootstrap
|
|
191
|
+
// =============================================================================
|
|
192
|
+
/**
|
|
193
|
+
* Non-streaming bootstrap fallback
|
|
194
|
+
*
|
|
195
|
+
* For servers that don't support SSE streaming, this function
|
|
196
|
+
* makes a regular POST request and waits for the response.
|
|
197
|
+
*
|
|
198
|
+
* @param options - Bootstrap options (same as streaming but without onEvent)
|
|
199
|
+
* @returns Promise that resolves with bootstrap result
|
|
200
|
+
*/
|
|
201
|
+
export async function bootstrapWithoutStreaming(options) {
|
|
202
|
+
const { serverUrl, orgId, projectId, mode = 'hybrid', codeBlocks, metadata, projectDNA, apiToken, onError, timeout = 120000 } = options;
|
|
203
|
+
const controller = new AbortController();
|
|
204
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
205
|
+
try {
|
|
206
|
+
const headers = {
|
|
207
|
+
'Content-Type': 'application/json',
|
|
208
|
+
'X-ACE-Org': orgId,
|
|
209
|
+
'X-ACE-Project': projectId
|
|
210
|
+
};
|
|
211
|
+
if (apiToken) {
|
|
212
|
+
headers['Authorization'] = `Bearer ${apiToken}`;
|
|
213
|
+
}
|
|
214
|
+
const body = JSON.stringify({
|
|
215
|
+
mode,
|
|
216
|
+
code_blocks: codeBlocks,
|
|
217
|
+
metadata: metadata || {
|
|
218
|
+
project_dna: projectDNA
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
const response = await fetch(`${serverUrl}/bootstrap`, {
|
|
222
|
+
method: 'POST',
|
|
223
|
+
headers,
|
|
224
|
+
body,
|
|
225
|
+
signal: controller.signal
|
|
226
|
+
});
|
|
227
|
+
if (!response.ok) {
|
|
228
|
+
throw new Error(`Bootstrap request failed: ${response.status} ${response.statusText}`);
|
|
229
|
+
}
|
|
230
|
+
const data = await response.json();
|
|
231
|
+
// Handle both old and new response formats
|
|
232
|
+
const totalPatterns = data.patterns_extracted || data.total_patterns || 0;
|
|
233
|
+
const bySection = data.bootstrap_statistics?.by_section || data.by_section || {};
|
|
234
|
+
return {
|
|
235
|
+
success: true,
|
|
236
|
+
statistics: data.bootstrap_statistics,
|
|
237
|
+
playbook: {
|
|
238
|
+
totalPatterns,
|
|
239
|
+
bySection
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
const err = error;
|
|
245
|
+
if (onError) {
|
|
246
|
+
onError(err);
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
success: false,
|
|
250
|
+
error: {
|
|
251
|
+
code: err.name === 'AbortError' ? 'TIMEOUT' : 'NETWORK_ERROR',
|
|
252
|
+
message: err.message,
|
|
253
|
+
retryable: true
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
clearTimeout(timeoutId);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=bootstrap-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap-stream.js","sourceRoot":"","sources":["../../src/services/bootstrap-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import Graph Analysis Service
|
|
3
|
+
*
|
|
4
|
+
* Uses Skott for JS/TS dependency graph analysis with dead code detection.
|
|
5
|
+
* Routes to appropriate analyzer based on primary language.
|
|
6
|
+
*
|
|
7
|
+
* @package @ace-sdk/core
|
|
8
|
+
*/
|
|
9
|
+
import type { GraphMetrics, CodeHealthMetrics } from '../types/project-dna.js';
|
|
10
|
+
/**
|
|
11
|
+
* Represents a node in the import graph
|
|
12
|
+
*/
|
|
13
|
+
export interface FileNode {
|
|
14
|
+
/** File path relative to repo root */
|
|
15
|
+
path: string;
|
|
16
|
+
/** Files this file imports */
|
|
17
|
+
imports: string[];
|
|
18
|
+
/** Files that import this file */
|
|
19
|
+
importedBy: string[];
|
|
20
|
+
/** Has no dependents (entry point) */
|
|
21
|
+
isEntryPoint: boolean;
|
|
22
|
+
/** Has 5+ importers */
|
|
23
|
+
isHub: boolean;
|
|
24
|
+
/** Has no dependencies */
|
|
25
|
+
isLeaf: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Complete import graph analysis result
|
|
29
|
+
*/
|
|
30
|
+
export interface ImportGraph {
|
|
31
|
+
/** All file nodes in the graph */
|
|
32
|
+
nodes: Map<string, FileNode>;
|
|
33
|
+
/** Files with no dependents */
|
|
34
|
+
entryPoints: string[];
|
|
35
|
+
/** Files with 5+ importers */
|
|
36
|
+
hubFiles: string[];
|
|
37
|
+
/** Files with no dependencies */
|
|
38
|
+
leafFiles: string[];
|
|
39
|
+
/** Circular dependency chains */
|
|
40
|
+
circularDeps: string[][];
|
|
41
|
+
/** Dead code files (never imported) */
|
|
42
|
+
deadCode: string[];
|
|
43
|
+
/** Unused dependencies from package.json */
|
|
44
|
+
unusedDeps: string[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Options for building import graph
|
|
48
|
+
*/
|
|
49
|
+
export interface ImportGraphOptions {
|
|
50
|
+
/** Root path to analyze */
|
|
51
|
+
repoPath: string;
|
|
52
|
+
/** Entry points to start from (optional) */
|
|
53
|
+
entryPoints?: string[];
|
|
54
|
+
/** Paths to ignore */
|
|
55
|
+
ignorePaths?: string[];
|
|
56
|
+
/** Maximum files to analyze */
|
|
57
|
+
maxFiles?: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Default paths to ignore during analysis
|
|
61
|
+
*/
|
|
62
|
+
export declare const DEFAULT_IGNORE_PATHS: string[];
|
|
63
|
+
/**
|
|
64
|
+
* Minimum number of importers to be considered a hub
|
|
65
|
+
*/
|
|
66
|
+
export declare const HUB_THRESHOLD = 5;
|
|
67
|
+
/**
|
|
68
|
+
* Build import graph with language-based routing
|
|
69
|
+
*
|
|
70
|
+
* Routes to the best analyzer based on primary language:
|
|
71
|
+
* - TypeScript/JavaScript: Skott (fast, dead code detection)
|
|
72
|
+
* - Other languages: Git co-occurrence fallback
|
|
73
|
+
*
|
|
74
|
+
* @param options - Import graph options
|
|
75
|
+
* @returns Complete import graph analysis
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const graph = await buildImportGraph({ repoPath: '/path/to/repo' });
|
|
80
|
+
*
|
|
81
|
+
* console.log(`Entry points: ${graph.entryPoints.length}`);
|
|
82
|
+
* console.log(`Hub files: ${graph.hubFiles.length}`);
|
|
83
|
+
* console.log(`Dead code: ${graph.deadCode.length}`);
|
|
84
|
+
* console.log(`Circular deps: ${graph.circularDeps.length}`);
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare function buildImportGraph(options: ImportGraphOptions): Promise<ImportGraph>;
|
|
88
|
+
/**
|
|
89
|
+
* Select priority files from import graph for bootstrap
|
|
90
|
+
*
|
|
91
|
+
* Priority order:
|
|
92
|
+
* 1. Entry points (always include)
|
|
93
|
+
* 2. Hub files (most imported - high value)
|
|
94
|
+
* 3. Files in circular deps (need attention)
|
|
95
|
+
* 4. Leaf files with many exports (API surface)
|
|
96
|
+
* 5. Fill remaining with diverse sampling
|
|
97
|
+
*
|
|
98
|
+
* @param graph - Import graph to select from
|
|
99
|
+
* @param maxFiles - Maximum files to select
|
|
100
|
+
* @returns Array of priority file paths
|
|
101
|
+
*/
|
|
102
|
+
export declare function selectPriorityFiles(graph: ImportGraph, maxFiles: number): string[];
|
|
103
|
+
/**
|
|
104
|
+
* Convert import graph to GraphMetrics for ProjectDNA
|
|
105
|
+
*/
|
|
106
|
+
export declare function graphToMetrics(graph: ImportGraph): GraphMetrics;
|
|
107
|
+
/**
|
|
108
|
+
* Calculate code health metrics from import graph
|
|
109
|
+
*/
|
|
110
|
+
export declare function calculateHealthMetrics(graph: ImportGraph, totalFiles: number, avgFileSize: number, maxFileSize: number): CodeHealthMetrics;
|
|
111
|
+
//# sourceMappingURL=import-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-graph.d.ts","sourceRoot":"","sources":["../../src/services/import-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAM/E;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kCAAkC;IAClC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,sCAAsC;IACtC,YAAY,EAAE,OAAO,CAAC;IACtB,uBAAuB;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,0BAA0B;IAC1B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC7B,+BAA+B;IAC/B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,iCAAiC;IACjC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iCAAiC;IACjC,YAAY,EAAE,MAAM,EAAE,EAAE,CAAC;IACzB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAMD;;GAEG;AACH,eAAO,MAAM,oBAAoB,UAWhC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,IAAI,CAAC;AA+J/B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAsBxF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CA6ClF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAQ/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,iBAAiB,CAYnB"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import Graph Analysis Service
|
|
3
|
+
*
|
|
4
|
+
* Uses Skott for JS/TS dependency graph analysis with dead code detection.
|
|
5
|
+
* Routes to appropriate analyzer based on primary language.
|
|
6
|
+
*
|
|
7
|
+
* @package @ace-sdk/core
|
|
8
|
+
*/
|
|
9
|
+
import skott from 'skott';
|
|
10
|
+
import { LanguageDetector } from './language-detector.js';
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Constants
|
|
13
|
+
// =============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Default paths to ignore during analysis
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_IGNORE_PATHS = [
|
|
18
|
+
'node_modules',
|
|
19
|
+
'dist',
|
|
20
|
+
'build',
|
|
21
|
+
'.git',
|
|
22
|
+
'.next',
|
|
23
|
+
'coverage',
|
|
24
|
+
'__tests__',
|
|
25
|
+
'__mocks__',
|
|
26
|
+
'*.test.*',
|
|
27
|
+
'*.spec.*'
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Minimum number of importers to be considered a hub
|
|
31
|
+
*/
|
|
32
|
+
export const HUB_THRESHOLD = 5;
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// Import Graph Builder
|
|
35
|
+
// =============================================================================
|
|
36
|
+
/**
|
|
37
|
+
* Build import graph using Skott for JS/TS projects
|
|
38
|
+
*/
|
|
39
|
+
async function buildSkottGraph(options) {
|
|
40
|
+
const { repoPath, ignorePaths = DEFAULT_IGNORE_PATHS } = options;
|
|
41
|
+
const graph = {
|
|
42
|
+
nodes: new Map(),
|
|
43
|
+
entryPoints: [],
|
|
44
|
+
hubFiles: [],
|
|
45
|
+
leafFiles: [],
|
|
46
|
+
circularDeps: [],
|
|
47
|
+
deadCode: [],
|
|
48
|
+
unusedDeps: []
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
// Run Skott analysis with config
|
|
52
|
+
const skottInstance = await skott({
|
|
53
|
+
cwd: repoPath,
|
|
54
|
+
entrypoint: options.entryPoints?.[0],
|
|
55
|
+
includeBaseDir: false,
|
|
56
|
+
ignorePatterns: ignorePaths,
|
|
57
|
+
dependencyTracking: {
|
|
58
|
+
thirdParty: true,
|
|
59
|
+
builtin: false,
|
|
60
|
+
typeOnly: true
|
|
61
|
+
},
|
|
62
|
+
fileExtensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'],
|
|
63
|
+
tsConfigPath: 'tsconfig.json',
|
|
64
|
+
manifestPath: 'package.json',
|
|
65
|
+
incremental: false,
|
|
66
|
+
circularMaxDepth: 10,
|
|
67
|
+
verbose: false
|
|
68
|
+
});
|
|
69
|
+
// Get structure from the instance
|
|
70
|
+
const structure = skottInstance.getStructure();
|
|
71
|
+
const { files, graph: skottGraph } = structure;
|
|
72
|
+
// Get circular dependencies and unused deps from graph API
|
|
73
|
+
const graphApi = skottInstance.useGraph();
|
|
74
|
+
const circularDependencies = graphApi.findCircularDependencies();
|
|
75
|
+
const unusedDependencies = await skottInstance.findUnusedDependencies();
|
|
76
|
+
// Build our graph from Skott's output
|
|
77
|
+
// SkottNode has: { id, adjacentTo: string[], body: { size, thirdPartyDependencies, builtinDependencies } }
|
|
78
|
+
for (const file of files) {
|
|
79
|
+
const node = skottGraph[file];
|
|
80
|
+
const deps = node?.adjacentTo || [];
|
|
81
|
+
graph.nodes.set(file, {
|
|
82
|
+
path: file,
|
|
83
|
+
imports: deps,
|
|
84
|
+
importedBy: [],
|
|
85
|
+
isEntryPoint: false,
|
|
86
|
+
isHub: false,
|
|
87
|
+
isLeaf: deps.length === 0
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Build reverse mapping (who imports this file)
|
|
91
|
+
for (const [filePath, node] of graph.nodes) {
|
|
92
|
+
for (const dep of node.imports) {
|
|
93
|
+
const depNode = graph.nodes.get(dep);
|
|
94
|
+
if (depNode) {
|
|
95
|
+
depNode.importedBy.push(filePath);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Identify entry points, hubs, and dead code
|
|
100
|
+
for (const [filePath, node] of graph.nodes) {
|
|
101
|
+
// Entry point: no one imports this file
|
|
102
|
+
if (node.importedBy.length === 0) {
|
|
103
|
+
node.isEntryPoint = true;
|
|
104
|
+
graph.entryPoints.push(filePath);
|
|
105
|
+
}
|
|
106
|
+
// Hub: many files import this
|
|
107
|
+
if (node.importedBy.length >= HUB_THRESHOLD) {
|
|
108
|
+
node.isHub = true;
|
|
109
|
+
graph.hubFiles.push(filePath);
|
|
110
|
+
}
|
|
111
|
+
// Leaf: no dependencies
|
|
112
|
+
if (node.isLeaf) {
|
|
113
|
+
graph.leafFiles.push(filePath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Dead code: files that are never imported (but not entry points)
|
|
117
|
+
// Use Skott's circular dependencies detection
|
|
118
|
+
graph.circularDeps = circularDependencies || [];
|
|
119
|
+
// Unused npm dependencies (thirdParty is the property containing unused deps)
|
|
120
|
+
graph.unusedDeps = unusedDependencies?.thirdParty || [];
|
|
121
|
+
// Find orphan files (not entry points but never imported)
|
|
122
|
+
for (const [filePath, node] of graph.nodes) {
|
|
123
|
+
if (node.importedBy.length === 0 && !isLikelyEntryPoint(filePath)) {
|
|
124
|
+
graph.deadCode.push(filePath);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return graph;
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.error('Skott analysis failed:', error);
|
|
131
|
+
return graph;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if a file is likely an entry point
|
|
136
|
+
*/
|
|
137
|
+
function isLikelyEntryPoint(filePath) {
|
|
138
|
+
const entryPatterns = [
|
|
139
|
+
/^index\.[jt]sx?$/,
|
|
140
|
+
/^main\.[jt]sx?$/,
|
|
141
|
+
/^app\.[jt]sx?$/,
|
|
142
|
+
/^server\.[jt]sx?$/,
|
|
143
|
+
/^cli\.[jt]sx?$/,
|
|
144
|
+
/\/index\.[jt]sx?$/,
|
|
145
|
+
/\/main\.[jt]sx?$/,
|
|
146
|
+
/src\/index\.[jt]sx?$/,
|
|
147
|
+
/src\/main\.[jt]sx?$/
|
|
148
|
+
];
|
|
149
|
+
return entryPatterns.some(pattern => pattern.test(filePath));
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Build import graph using git co-occurrence for non-JS/TS projects
|
|
153
|
+
* (Fallback when no static analyzer is available)
|
|
154
|
+
*/
|
|
155
|
+
async function buildGitCooccurrenceGraph(_options) {
|
|
156
|
+
// For non-JS/TS projects, we can't do static analysis
|
|
157
|
+
// Return empty graph - future: implement git co-occurrence analysis
|
|
158
|
+
return {
|
|
159
|
+
nodes: new Map(),
|
|
160
|
+
entryPoints: [],
|
|
161
|
+
hubFiles: [],
|
|
162
|
+
leafFiles: [],
|
|
163
|
+
circularDeps: [],
|
|
164
|
+
deadCode: [],
|
|
165
|
+
unusedDeps: []
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// =============================================================================
|
|
169
|
+
// Main Export
|
|
170
|
+
// =============================================================================
|
|
171
|
+
/**
|
|
172
|
+
* Build import graph with language-based routing
|
|
173
|
+
*
|
|
174
|
+
* Routes to the best analyzer based on primary language:
|
|
175
|
+
* - TypeScript/JavaScript: Skott (fast, dead code detection)
|
|
176
|
+
* - Other languages: Git co-occurrence fallback
|
|
177
|
+
*
|
|
178
|
+
* @param options - Import graph options
|
|
179
|
+
* @returns Complete import graph analysis
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* const graph = await buildImportGraph({ repoPath: '/path/to/repo' });
|
|
184
|
+
*
|
|
185
|
+
* console.log(`Entry points: ${graph.entryPoints.length}`);
|
|
186
|
+
* console.log(`Hub files: ${graph.hubFiles.length}`);
|
|
187
|
+
* console.log(`Dead code: ${graph.deadCode.length}`);
|
|
188
|
+
* console.log(`Circular deps: ${graph.circularDeps.length}`);
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export async function buildImportGraph(options) {
|
|
192
|
+
const detector = new LanguageDetector();
|
|
193
|
+
const primaryLang = await detector.getPrimaryLanguage(options.repoPath);
|
|
194
|
+
// Route to best analyzer for this language
|
|
195
|
+
switch (primaryLang) {
|
|
196
|
+
case 'TypeScript':
|
|
197
|
+
case 'JavaScript':
|
|
198
|
+
case 'TSX':
|
|
199
|
+
case 'JSX':
|
|
200
|
+
return buildSkottGraph(options);
|
|
201
|
+
// Future: Add support for other languages
|
|
202
|
+
// case 'Java':
|
|
203
|
+
// case 'Python':
|
|
204
|
+
// case 'Go':
|
|
205
|
+
// return buildDependsGraph(options);
|
|
206
|
+
default:
|
|
207
|
+
// Fallback to git co-occurrence
|
|
208
|
+
return buildGitCooccurrenceGraph(options);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Select priority files from import graph for bootstrap
|
|
213
|
+
*
|
|
214
|
+
* Priority order:
|
|
215
|
+
* 1. Entry points (always include)
|
|
216
|
+
* 2. Hub files (most imported - high value)
|
|
217
|
+
* 3. Files in circular deps (need attention)
|
|
218
|
+
* 4. Leaf files with many exports (API surface)
|
|
219
|
+
* 5. Fill remaining with diverse sampling
|
|
220
|
+
*
|
|
221
|
+
* @param graph - Import graph to select from
|
|
222
|
+
* @param maxFiles - Maximum files to select
|
|
223
|
+
* @returns Array of priority file paths
|
|
224
|
+
*/
|
|
225
|
+
export function selectPriorityFiles(graph, maxFiles) {
|
|
226
|
+
const priority = [];
|
|
227
|
+
const seen = new Set();
|
|
228
|
+
const addUnique = (files, limit) => {
|
|
229
|
+
let added = 0;
|
|
230
|
+
for (const file of files) {
|
|
231
|
+
if (!seen.has(file)) {
|
|
232
|
+
seen.add(file);
|
|
233
|
+
priority.push(file);
|
|
234
|
+
added++;
|
|
235
|
+
if (limit && added >= limit)
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
// 1. Entry points (always include)
|
|
241
|
+
addUnique(graph.entryPoints, 10);
|
|
242
|
+
// 2. Hub files (most imported - high value)
|
|
243
|
+
const sortedHubs = [...graph.hubFiles].sort((a, b) => {
|
|
244
|
+
const aNode = graph.nodes.get(a);
|
|
245
|
+
const bNode = graph.nodes.get(b);
|
|
246
|
+
return (bNode?.importedBy.length || 0) - (aNode?.importedBy.length || 0);
|
|
247
|
+
});
|
|
248
|
+
addUnique(sortedHubs, 20);
|
|
249
|
+
// 3. Files in circular deps (need attention)
|
|
250
|
+
const circularFiles = graph.circularDeps.flat();
|
|
251
|
+
addUnique(circularFiles, 10);
|
|
252
|
+
// 4. Leaf files with many exports (API surface)
|
|
253
|
+
const apiSurface = [...graph.nodes.entries()]
|
|
254
|
+
.filter(([_, n]) => n.isLeaf && n.importedBy.length > 0)
|
|
255
|
+
.sort((a, b) => b[1].importedBy.length - a[1].importedBy.length)
|
|
256
|
+
.map(([p]) => p);
|
|
257
|
+
addUnique(apiSurface, 20);
|
|
258
|
+
// 5. Fill remaining with diverse sampling
|
|
259
|
+
if (priority.length < maxFiles) {
|
|
260
|
+
const remaining = [...graph.nodes.keys()].filter(p => !seen.has(p));
|
|
261
|
+
addUnique(remaining, maxFiles - priority.length);
|
|
262
|
+
}
|
|
263
|
+
return priority.slice(0, maxFiles);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Convert import graph to GraphMetrics for ProjectDNA
|
|
267
|
+
*/
|
|
268
|
+
export function graphToMetrics(graph) {
|
|
269
|
+
return {
|
|
270
|
+
totalFiles: graph.nodes.size,
|
|
271
|
+
hubFiles: graph.hubFiles.slice(0, 20),
|
|
272
|
+
entryPoints: graph.entryPoints.slice(0, 10),
|
|
273
|
+
leafNodes: graph.leafFiles.slice(0, 20),
|
|
274
|
+
circularDeps: graph.circularDeps.slice(0, 10)
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Calculate code health metrics from import graph
|
|
279
|
+
*/
|
|
280
|
+
export function calculateHealthMetrics(graph, totalFiles, avgFileSize, maxFileSize) {
|
|
281
|
+
const deadCodePercentage = totalFiles > 0
|
|
282
|
+
? (graph.deadCode.length / totalFiles) * 100
|
|
283
|
+
: 0;
|
|
284
|
+
return {
|
|
285
|
+
deadCodePercentage,
|
|
286
|
+
circularDepsCount: graph.circularDeps.length,
|
|
287
|
+
unusedDepsCount: graph.unusedDeps.length,
|
|
288
|
+
avgFileSize,
|
|
289
|
+
maxFileSize
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=import-graph.js.map
|