@wtdlee/repomap 0.3.0 → 0.3.2
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/analyzers/index.d.ts +69 -5
- package/dist/analyzers/index.js +1 -5
- package/dist/{server/doc-server.js → chunk-4K4MGTPV.js} +41 -329
- package/dist/chunk-6F4PWJZI.js +0 -0
- package/dist/chunk-J2CM7T7U.js +1 -0
- package/dist/{generators/page-map-generator.js → chunk-MOEA75XK.js} +278 -503
- package/dist/{generators/rails-map-generator.js → chunk-SL2GMDBN.js} +48 -129
- package/dist/chunk-UJT5KTVK.js +36 -0
- package/dist/chunk-VV3A3UE3.js +1 -0
- package/dist/chunk-XWZH2RDG.js +19 -0
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +29 -499
- package/dist/dataflow-analyzer-BfAiqVUp.d.ts +180 -0
- package/dist/env-detector-BIWJ7OYF.js +1 -0
- package/dist/generators/assets/common.css +564 -23
- package/dist/generators/index.d.ts +431 -3
- package/dist/generators/index.js +1 -3
- package/dist/index.d.ts +53 -10
- package/dist/index.js +1 -11
- package/dist/page-map-generator-XNZ4TDJT.js +1 -0
- package/dist/rails-TJCDGBBF.js +1 -0
- package/dist/rails-map-generator-JL5PKHYP.js +1 -0
- package/dist/server/index.d.ts +33 -1
- package/dist/server/index.js +1 -1
- package/dist/types.d.ts +39 -37
- package/dist/types.js +1 -5
- package/package.json +4 -2
- package/dist/analyzers/base-analyzer.d.ts +0 -45
- package/dist/analyzers/base-analyzer.js +0 -47
- package/dist/analyzers/dataflow-analyzer.d.ts +0 -29
- package/dist/analyzers/dataflow-analyzer.js +0 -425
- package/dist/analyzers/graphql-analyzer.d.ts +0 -22
- package/dist/analyzers/graphql-analyzer.js +0 -386
- package/dist/analyzers/pages-analyzer.d.ts +0 -84
- package/dist/analyzers/pages-analyzer.js +0 -1695
- package/dist/analyzers/rails/index.d.ts +0 -46
- package/dist/analyzers/rails/index.js +0 -145
- package/dist/analyzers/rails/rails-controller-analyzer.d.ts +0 -82
- package/dist/analyzers/rails/rails-controller-analyzer.js +0 -478
- package/dist/analyzers/rails/rails-grpc-analyzer.d.ts +0 -44
- package/dist/analyzers/rails/rails-grpc-analyzer.js +0 -262
- package/dist/analyzers/rails/rails-model-analyzer.d.ts +0 -88
- package/dist/analyzers/rails/rails-model-analyzer.js +0 -493
- package/dist/analyzers/rails/rails-react-analyzer.d.ts +0 -41
- package/dist/analyzers/rails/rails-react-analyzer.js +0 -529
- package/dist/analyzers/rails/rails-routes-analyzer.d.ts +0 -62
- package/dist/analyzers/rails/rails-routes-analyzer.js +0 -540
- package/dist/analyzers/rails/rails-view-analyzer.d.ts +0 -49
- package/dist/analyzers/rails/rails-view-analyzer.js +0 -386
- package/dist/analyzers/rails/ruby-parser.d.ts +0 -63
- package/dist/analyzers/rails/ruby-parser.js +0 -212
- package/dist/analyzers/rest-api-analyzer.d.ts +0 -65
- package/dist/analyzers/rest-api-analyzer.js +0 -479
- package/dist/core/cache.d.ts +0 -47
- package/dist/core/cache.js +0 -151
- package/dist/core/engine.d.ts +0 -46
- package/dist/core/engine.js +0 -319
- package/dist/core/index.d.ts +0 -2
- package/dist/core/index.js +0 -2
- package/dist/generators/markdown-generator.d.ts +0 -25
- package/dist/generators/markdown-generator.js +0 -782
- package/dist/generators/mermaid-generator.d.ts +0 -35
- package/dist/generators/mermaid-generator.js +0 -364
- package/dist/generators/page-map-generator.d.ts +0 -22
- package/dist/generators/rails-map-generator.d.ts +0 -21
- package/dist/server/doc-server.d.ts +0 -30
- package/dist/utils/env-detector.d.ts +0 -31
- package/dist/utils/env-detector.js +0 -188
- package/dist/utils/parallel.d.ts +0 -23
- package/dist/utils/parallel.js +0 -70
- package/dist/utils/port.d.ts +0 -15
- package/dist/utils/port.js +0 -41
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { BaseAnalyzer } from './base-analyzer.js';
|
|
2
|
-
import type { AnalysisResult, RepositoryConfig } from '../types.js';
|
|
3
|
-
/**
|
|
4
|
-
* Analyzer for REST API calls (fetch, axios, useSWR, etc.)
|
|
5
|
-
* REST API呼び出しの分析器
|
|
6
|
-
*/
|
|
7
|
-
export declare class RestApiAnalyzer extends BaseAnalyzer {
|
|
8
|
-
private project;
|
|
9
|
-
private apiCallCounter;
|
|
10
|
-
constructor(config: RepositoryConfig);
|
|
11
|
-
getName(): string;
|
|
12
|
-
analyze(): Promise<Partial<AnalysisResult>>;
|
|
13
|
-
/**
|
|
14
|
-
* Find fetch() calls
|
|
15
|
-
*/
|
|
16
|
-
private findFetchCalls;
|
|
17
|
-
/**
|
|
18
|
-
* Find axios calls (axios.get, axios.post, etc.)
|
|
19
|
-
*/
|
|
20
|
-
private findAxiosCalls;
|
|
21
|
-
/**
|
|
22
|
-
* Find useSWR calls
|
|
23
|
-
*/
|
|
24
|
-
private findSwrCalls;
|
|
25
|
-
/**
|
|
26
|
-
* Extract API call info from fetch()
|
|
27
|
-
*/
|
|
28
|
-
private extractFetchCall;
|
|
29
|
-
/**
|
|
30
|
-
* Extract API call info from axios.method()
|
|
31
|
-
*/
|
|
32
|
-
private extractAxiosCall;
|
|
33
|
-
/**
|
|
34
|
-
* Extract API call info from axios() direct call
|
|
35
|
-
*/
|
|
36
|
-
private extractAxiosDirectCall;
|
|
37
|
-
/**
|
|
38
|
-
* Extract API call info from useSWR()
|
|
39
|
-
*/
|
|
40
|
-
private extractSwrCall;
|
|
41
|
-
/**
|
|
42
|
-
* Get the name of the containing function/component
|
|
43
|
-
*/
|
|
44
|
-
private getContainingFunctionName;
|
|
45
|
-
/**
|
|
46
|
-
* Extract URL from argument (handles literals, variables, function calls)
|
|
47
|
-
*/
|
|
48
|
-
private extractUrlFromArg;
|
|
49
|
-
/**
|
|
50
|
-
* Clean string literal (remove quotes)
|
|
51
|
-
*/
|
|
52
|
-
private cleanStringLiteral;
|
|
53
|
-
/**
|
|
54
|
-
* Check if URL looks like an API endpoint
|
|
55
|
-
*/
|
|
56
|
-
private isApiUrl;
|
|
57
|
-
/**
|
|
58
|
-
* Categorize API by URL pattern
|
|
59
|
-
*/
|
|
60
|
-
private categorizeApi;
|
|
61
|
-
/**
|
|
62
|
-
* Normalize HTTP method
|
|
63
|
-
*/
|
|
64
|
-
private normalizeMethod;
|
|
65
|
-
}
|
|
@@ -1,479 +0,0 @@
|
|
|
1
|
-
import { Project, SyntaxKind, Node } from 'ts-morph';
|
|
2
|
-
import fg from 'fast-glob';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { BaseAnalyzer } from './base-analyzer.js';
|
|
5
|
-
import { parallelMapSafe } from '../utils/parallel.js';
|
|
6
|
-
/**
|
|
7
|
-
* Analyzer for REST API calls (fetch, axios, useSWR, etc.)
|
|
8
|
-
* REST API呼び出しの分析器
|
|
9
|
-
*/
|
|
10
|
-
export class RestApiAnalyzer extends BaseAnalyzer {
|
|
11
|
-
project;
|
|
12
|
-
apiCallCounter = 0;
|
|
13
|
-
constructor(config) {
|
|
14
|
-
super(config);
|
|
15
|
-
this.project = new Project({
|
|
16
|
-
tsConfigFilePath: this.resolvePath('tsconfig.json'),
|
|
17
|
-
skipAddingFilesFromTsConfig: true,
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
getName() {
|
|
21
|
-
return 'RestApiAnalyzer';
|
|
22
|
-
}
|
|
23
|
-
async analyze() {
|
|
24
|
-
this.log('Starting REST API analysis...');
|
|
25
|
-
const tsFiles = await fg(['**/*.ts', '**/*.tsx'], {
|
|
26
|
-
cwd: this.basePath,
|
|
27
|
-
ignore: [
|
|
28
|
-
'**/node_modules/**',
|
|
29
|
-
'**/.next/**',
|
|
30
|
-
'**/__tests__/**',
|
|
31
|
-
'**/*.test.*',
|
|
32
|
-
'**/*.spec.*',
|
|
33
|
-
'**/*.stories.*',
|
|
34
|
-
'**/__generated__/**',
|
|
35
|
-
'**/dist/**',
|
|
36
|
-
'**/build/**',
|
|
37
|
-
],
|
|
38
|
-
absolute: true,
|
|
39
|
-
});
|
|
40
|
-
// Add all files to project first
|
|
41
|
-
const sourceFiles = new Map();
|
|
42
|
-
for (const filePath of tsFiles) {
|
|
43
|
-
try {
|
|
44
|
-
sourceFiles.set(filePath, this.project.addSourceFileAtPath(filePath));
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
// Skip files that can't be added
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
// Analyze files in parallel
|
|
51
|
-
const results = await parallelMapSafe(Array.from(sourceFiles.entries()), async ([filePath, sourceFile]) => {
|
|
52
|
-
const relativePath = path.relative(this.basePath, filePath);
|
|
53
|
-
const calls = [];
|
|
54
|
-
// Find fetch() calls
|
|
55
|
-
calls.push(...this.findFetchCalls(sourceFile, relativePath));
|
|
56
|
-
// Find axios calls
|
|
57
|
-
calls.push(...this.findAxiosCalls(sourceFile, relativePath));
|
|
58
|
-
// Find useSWR calls
|
|
59
|
-
calls.push(...this.findSwrCalls(sourceFile, relativePath));
|
|
60
|
-
return calls;
|
|
61
|
-
}, 8);
|
|
62
|
-
const apiCalls = results.flat();
|
|
63
|
-
this.log(`Found ${apiCalls.length} REST API calls`);
|
|
64
|
-
return { apiCalls };
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Find fetch() calls
|
|
68
|
-
*/
|
|
69
|
-
findFetchCalls(sourceFile, filePath) {
|
|
70
|
-
const calls = [];
|
|
71
|
-
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
72
|
-
for (const call of callExpressions) {
|
|
73
|
-
try {
|
|
74
|
-
const expression = call.getExpression();
|
|
75
|
-
const expressionText = expression.getText();
|
|
76
|
-
// Match fetch() or window.fetch()
|
|
77
|
-
if (expressionText === 'fetch' || expressionText === 'window.fetch') {
|
|
78
|
-
const apiCall = this.extractFetchCall(call, filePath);
|
|
79
|
-
if (apiCall) {
|
|
80
|
-
calls.push(apiCall);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
// Skip parsing errors
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return calls;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Find axios calls (axios.get, axios.post, etc.)
|
|
92
|
-
*/
|
|
93
|
-
findAxiosCalls(sourceFile, filePath) {
|
|
94
|
-
const calls = [];
|
|
95
|
-
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
96
|
-
for (const call of callExpressions) {
|
|
97
|
-
try {
|
|
98
|
-
const expression = call.getExpression();
|
|
99
|
-
const expressionText = expression.getText();
|
|
100
|
-
// Match axios.get, axios.post, axios.put, axios.delete, axios.patch
|
|
101
|
-
const axiosMatch = expressionText.match(/^axios\.(get|post|put|delete|patch)$/i);
|
|
102
|
-
if (axiosMatch) {
|
|
103
|
-
const apiCall = this.extractAxiosCall(call, filePath, axiosMatch[1].toUpperCase());
|
|
104
|
-
if (apiCall) {
|
|
105
|
-
calls.push(apiCall);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// Match axios() direct call
|
|
109
|
-
if (expressionText === 'axios') {
|
|
110
|
-
const apiCall = this.extractAxiosDirectCall(call, filePath);
|
|
111
|
-
if (apiCall) {
|
|
112
|
-
calls.push(apiCall);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
// Skip parsing errors
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return calls;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Find useSWR calls
|
|
124
|
-
*/
|
|
125
|
-
findSwrCalls(sourceFile, filePath) {
|
|
126
|
-
const calls = [];
|
|
127
|
-
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
128
|
-
for (const call of callExpressions) {
|
|
129
|
-
try {
|
|
130
|
-
const expression = call.getExpression();
|
|
131
|
-
const expressionText = expression.getText();
|
|
132
|
-
// Match useSWR
|
|
133
|
-
if (expressionText === 'useSWR' || expressionText === 'useSWRImmutable') {
|
|
134
|
-
const apiCall = this.extractSwrCall(call, filePath);
|
|
135
|
-
if (apiCall) {
|
|
136
|
-
calls.push(apiCall);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
// Skip parsing errors
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return calls;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Extract API call info from fetch()
|
|
148
|
-
*/
|
|
149
|
-
extractFetchCall(call, filePath) {
|
|
150
|
-
const args = call.getArguments();
|
|
151
|
-
if (args.length === 0)
|
|
152
|
-
return null;
|
|
153
|
-
const urlArg = args[0].getText();
|
|
154
|
-
const urlInfo = this.extractUrlFromArg(urlArg);
|
|
155
|
-
if (!urlInfo.url)
|
|
156
|
-
return null;
|
|
157
|
-
// Skip non-API URLs only if we have a concrete URL (not a placeholder)
|
|
158
|
-
if (!urlInfo.isPlaceholder && !this.isApiUrl(urlInfo.url))
|
|
159
|
-
return null;
|
|
160
|
-
let method = 'GET';
|
|
161
|
-
let requiresAuth = false;
|
|
162
|
-
// Check options argument for method
|
|
163
|
-
if (args.length > 1) {
|
|
164
|
-
const optionsText = args[1].getText();
|
|
165
|
-
const methodMatch = optionsText.match(/method:\s*["'](\w+)["']/i);
|
|
166
|
-
if (methodMatch) {
|
|
167
|
-
method = this.normalizeMethod(methodMatch[1]);
|
|
168
|
-
}
|
|
169
|
-
requiresAuth =
|
|
170
|
-
optionsText.includes('credentials') ||
|
|
171
|
-
optionsText.includes('Authorization') ||
|
|
172
|
-
optionsText.includes('withCredentials');
|
|
173
|
-
}
|
|
174
|
-
const containingFunction = this.getContainingFunctionName(call);
|
|
175
|
-
const line = call.getStartLineNumber();
|
|
176
|
-
return {
|
|
177
|
-
id: `api-${++this.apiCallCounter}`,
|
|
178
|
-
method,
|
|
179
|
-
url: urlInfo.url,
|
|
180
|
-
callType: 'fetch',
|
|
181
|
-
filePath,
|
|
182
|
-
line,
|
|
183
|
-
containingFunction,
|
|
184
|
-
usedIn: [],
|
|
185
|
-
requiresAuth,
|
|
186
|
-
category: this.categorizeApi(urlInfo.url),
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Extract API call info from axios.method()
|
|
191
|
-
*/
|
|
192
|
-
extractAxiosCall(call, filePath, method) {
|
|
193
|
-
const args = call.getArguments();
|
|
194
|
-
if (args.length === 0)
|
|
195
|
-
return null;
|
|
196
|
-
const urlArg = args[0].getText();
|
|
197
|
-
const urlInfo = this.extractUrlFromArg(urlArg);
|
|
198
|
-
if (!urlInfo.url)
|
|
199
|
-
return null;
|
|
200
|
-
let requiresAuth = false;
|
|
201
|
-
if (args.length > 1) {
|
|
202
|
-
const optionsText = args[args.length - 1].getText();
|
|
203
|
-
requiresAuth =
|
|
204
|
-
optionsText.includes('withCredentials') || optionsText.includes('Authorization');
|
|
205
|
-
}
|
|
206
|
-
const containingFunction = this.getContainingFunctionName(call);
|
|
207
|
-
const line = call.getStartLineNumber();
|
|
208
|
-
return {
|
|
209
|
-
id: `api-${++this.apiCallCounter}`,
|
|
210
|
-
method: this.normalizeMethod(method),
|
|
211
|
-
url: urlInfo.url,
|
|
212
|
-
callType: 'axios',
|
|
213
|
-
filePath,
|
|
214
|
-
line,
|
|
215
|
-
containingFunction,
|
|
216
|
-
usedIn: [],
|
|
217
|
-
requiresAuth,
|
|
218
|
-
category: this.categorizeApi(urlInfo.url),
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Extract API call info from axios() direct call
|
|
223
|
-
*/
|
|
224
|
-
extractAxiosDirectCall(call, filePath) {
|
|
225
|
-
const args = call.getArguments();
|
|
226
|
-
if (args.length === 0)
|
|
227
|
-
return null;
|
|
228
|
-
const configText = args[0].getText();
|
|
229
|
-
const urlMatch = configText.match(/url:\s*["'`]([^"'`]+)["'`]/);
|
|
230
|
-
const methodMatch = configText.match(/method:\s*["'](\w+)["']/i);
|
|
231
|
-
if (!urlMatch)
|
|
232
|
-
return null;
|
|
233
|
-
const url = urlMatch[1];
|
|
234
|
-
const method = methodMatch ? this.normalizeMethod(methodMatch[1]) : 'GET';
|
|
235
|
-
const requiresAuth = configText.includes('withCredentials') || configText.includes('Authorization');
|
|
236
|
-
const containingFunction = this.getContainingFunctionName(call);
|
|
237
|
-
const line = call.getStartLineNumber();
|
|
238
|
-
return {
|
|
239
|
-
id: `api-${++this.apiCallCounter}`,
|
|
240
|
-
method,
|
|
241
|
-
url,
|
|
242
|
-
callType: 'axios',
|
|
243
|
-
filePath,
|
|
244
|
-
line,
|
|
245
|
-
containingFunction,
|
|
246
|
-
usedIn: [],
|
|
247
|
-
requiresAuth,
|
|
248
|
-
category: this.categorizeApi(url),
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Extract API call info from useSWR()
|
|
253
|
-
*/
|
|
254
|
-
extractSwrCall(call, filePath) {
|
|
255
|
-
const args = call.getArguments();
|
|
256
|
-
if (args.length === 0)
|
|
257
|
-
return null;
|
|
258
|
-
const keyArg = args[0].getText();
|
|
259
|
-
let url = null;
|
|
260
|
-
// String literal: useSWR("/api/users", fetcher)
|
|
261
|
-
if (keyArg.startsWith('"') || keyArg.startsWith("'") || keyArg.startsWith('`')) {
|
|
262
|
-
url = this.cleanStringLiteral(keyArg);
|
|
263
|
-
}
|
|
264
|
-
// Conditional: useSWR(condition ? "/api/users" : null, fetcher)
|
|
265
|
-
else if (keyArg.includes('?') && keyArg.includes(':')) {
|
|
266
|
-
// Try to find URL in true branch
|
|
267
|
-
let match = keyArg.match(/\?\s*["'`]([^"'`]+)["'`]/);
|
|
268
|
-
if (match) {
|
|
269
|
-
url = match[1];
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
// Try to find URL in false branch
|
|
273
|
-
match = keyArg.match(/:\s*["'`]([^"'`]+)["'`]/);
|
|
274
|
-
if (match) {
|
|
275
|
-
url = match[1];
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
// If still no URL, check for template literals in the condition
|
|
279
|
-
if (!url) {
|
|
280
|
-
match = keyArg.match(/\?\s*`([^`]+)`/);
|
|
281
|
-
if (match) {
|
|
282
|
-
url = match[1].replace(/\$\{[^}]+\}/g, ':param');
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
// Function call pattern
|
|
287
|
-
else {
|
|
288
|
-
const urlInfo = this.extractUrlFromArg(keyArg);
|
|
289
|
-
if (urlInfo.url && !keyArg.includes('null') && !keyArg.includes('undefined')) {
|
|
290
|
-
url = urlInfo.url;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
if (!url)
|
|
294
|
-
return null;
|
|
295
|
-
const containingFunction = this.getContainingFunctionName(call);
|
|
296
|
-
const line = call.getStartLineNumber();
|
|
297
|
-
return {
|
|
298
|
-
id: `api-${++this.apiCallCounter}`,
|
|
299
|
-
method: 'GET', // SWR is typically for GET requests
|
|
300
|
-
url,
|
|
301
|
-
callType: 'useSWR',
|
|
302
|
-
filePath,
|
|
303
|
-
line,
|
|
304
|
-
containingFunction,
|
|
305
|
-
usedIn: [],
|
|
306
|
-
requiresAuth: false,
|
|
307
|
-
category: this.categorizeApi(url),
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* Get the name of the containing function/component
|
|
312
|
-
*/
|
|
313
|
-
getContainingFunctionName(node) {
|
|
314
|
-
let current = node;
|
|
315
|
-
while (current) {
|
|
316
|
-
// Function declaration
|
|
317
|
-
if (Node.isFunctionDeclaration(current)) {
|
|
318
|
-
return current.getName() || 'anonymous';
|
|
319
|
-
}
|
|
320
|
-
// Variable declaration with arrow function
|
|
321
|
-
if (Node.isVariableDeclaration(current)) {
|
|
322
|
-
return current.getName();
|
|
323
|
-
}
|
|
324
|
-
// Method declaration
|
|
325
|
-
if (Node.isMethodDeclaration(current)) {
|
|
326
|
-
return current.getName();
|
|
327
|
-
}
|
|
328
|
-
// Arrow function in variable
|
|
329
|
-
if (Node.isArrowFunction(current)) {
|
|
330
|
-
const parent = current.getParent();
|
|
331
|
-
if (parent && Node.isVariableDeclaration(parent)) {
|
|
332
|
-
return parent.getName();
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
current = current.getParent();
|
|
336
|
-
}
|
|
337
|
-
return 'unknown';
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Extract URL from argument (handles literals, variables, function calls)
|
|
341
|
-
*/
|
|
342
|
-
extractUrlFromArg(argText) {
|
|
343
|
-
// String literal: "url" or 'url' or `url`
|
|
344
|
-
if (/^["'`]/.test(argText)) {
|
|
345
|
-
const url = this.cleanStringLiteral(argText);
|
|
346
|
-
return { url, isPlaceholder: false };
|
|
347
|
-
}
|
|
348
|
-
// Function call: someUrlBuilder("/path") or buildUrl(`/path`)
|
|
349
|
-
const funcCallMatch = argText.match(/^(\w+)\s*\(\s*["'`]([^"'`]+)["'`]/);
|
|
350
|
-
if (funcCallMatch) {
|
|
351
|
-
return { url: `[${funcCallMatch[1]}] ${funcCallMatch[2]}`, isPlaceholder: true };
|
|
352
|
-
}
|
|
353
|
-
// Function call with template literal: someFunc(`/path/${var}`)
|
|
354
|
-
const funcTemplateMatch = argText.match(/^(\w+)\s*\(\s*`([^`]+)`/);
|
|
355
|
-
if (funcTemplateMatch) {
|
|
356
|
-
const path = funcTemplateMatch[2].replace(/\$\{[^}]+\}/g, ':param');
|
|
357
|
-
return { url: `[${funcTemplateMatch[1]}] ${path}`, isPlaceholder: true };
|
|
358
|
-
}
|
|
359
|
-
// Variable reference: use as placeholder
|
|
360
|
-
if (/^\w+(\.\w+)*$/.test(argText)) {
|
|
361
|
-
return { url: `[${argText}]`, isPlaceholder: true };
|
|
362
|
-
}
|
|
363
|
-
// Property access: obj.url
|
|
364
|
-
if (argText.includes('.')) {
|
|
365
|
-
return { url: `[${argText}]`, isPlaceholder: true };
|
|
366
|
-
}
|
|
367
|
-
return { url: null, isPlaceholder: false };
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* Clean string literal (remove quotes)
|
|
371
|
-
*/
|
|
372
|
-
cleanStringLiteral(value) {
|
|
373
|
-
// Remove quotes and template literal markers
|
|
374
|
-
const cleaned = value.replace(/^["'`]|["'`]$/g, '').trim();
|
|
375
|
-
// Skip template literals with expressions
|
|
376
|
-
if (cleaned.includes('${')) {
|
|
377
|
-
// Try to extract the base path and convert expressions to :param
|
|
378
|
-
const parameterized = cleaned.replace(/\$\{[^}]+\}/g, ':param');
|
|
379
|
-
return parameterized;
|
|
380
|
-
}
|
|
381
|
-
return cleaned || null;
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Check if URL looks like an API endpoint
|
|
385
|
-
*/
|
|
386
|
-
isApiUrl(url) {
|
|
387
|
-
// Skip data URLs, blob URLs, etc.
|
|
388
|
-
if (url.startsWith('data:') || url.startsWith('blob:')) {
|
|
389
|
-
return false;
|
|
390
|
-
}
|
|
391
|
-
// Skip file extensions that are clearly not APIs
|
|
392
|
-
if (/\.(css|js|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|html)$/i.test(url)) {
|
|
393
|
-
return false;
|
|
394
|
-
}
|
|
395
|
-
// Accept paths that look like API endpoints
|
|
396
|
-
return (url.startsWith('/') ||
|
|
397
|
-
url.startsWith('http') ||
|
|
398
|
-
url.includes('/api/') ||
|
|
399
|
-
url.includes('.json') ||
|
|
400
|
-
// Common external API patterns
|
|
401
|
-
url.includes('api.') ||
|
|
402
|
-
url.includes('github.io') || // GitHub Pages hosted APIs
|
|
403
|
-
url.includes('hsforms.com') || // HubSpot
|
|
404
|
-
url.includes('hubspot') || // HubSpot
|
|
405
|
-
url.includes('amazonaws.com') || // AWS S3
|
|
406
|
-
url.includes('s3.') || // AWS S3 alternative
|
|
407
|
-
url.includes('googleapis.com') || // Google APIs
|
|
408
|
-
url.includes('stripe.com') || // Stripe
|
|
409
|
-
url.includes('graph.facebook.com') || // Facebook
|
|
410
|
-
url.includes('api.twitter.com') || // Twitter
|
|
411
|
-
url.includes('slack.com') || // Slack
|
|
412
|
-
url.includes('discord.com') || // Discord
|
|
413
|
-
url.includes('sendgrid.com') || // SendGrid
|
|
414
|
-
url.includes('twilio.com') || // Twilio
|
|
415
|
-
url.includes('firebase') || // Firebase
|
|
416
|
-
url.includes('supabase') || // Supabase
|
|
417
|
-
url.includes('auth0.com') || // Auth0
|
|
418
|
-
url.includes('okta.com') || // Okta
|
|
419
|
-
url.includes('cloudflare.com') || // Cloudflare
|
|
420
|
-
url.includes('vercel.com') || // Vercel
|
|
421
|
-
url.includes('netlify.com') // Netlify
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Categorize API by URL pattern
|
|
426
|
-
*/
|
|
427
|
-
categorizeApi(url) {
|
|
428
|
-
// External services
|
|
429
|
-
if (url.includes('hsforms.com') || url.includes('hubspot'))
|
|
430
|
-
return 'HubSpot';
|
|
431
|
-
if (url.includes('amazonaws.com') || url.includes('s3.'))
|
|
432
|
-
return 'AWS S3';
|
|
433
|
-
if (url.includes('googleapis.com'))
|
|
434
|
-
return 'Google API';
|
|
435
|
-
if (url.includes('stripe.com'))
|
|
436
|
-
return 'Stripe';
|
|
437
|
-
if (url.includes('graph.facebook.com'))
|
|
438
|
-
return 'Facebook';
|
|
439
|
-
if (url.includes('api.twitter.com'))
|
|
440
|
-
return 'Twitter';
|
|
441
|
-
if (url.includes('slack.com'))
|
|
442
|
-
return 'Slack';
|
|
443
|
-
if (url.includes('discord.com'))
|
|
444
|
-
return 'Discord';
|
|
445
|
-
if (url.includes('sendgrid.com'))
|
|
446
|
-
return 'SendGrid';
|
|
447
|
-
if (url.includes('twilio.com'))
|
|
448
|
-
return 'Twilio';
|
|
449
|
-
if (url.includes('firebase'))
|
|
450
|
-
return 'Firebase';
|
|
451
|
-
if (url.includes('supabase'))
|
|
452
|
-
return 'Supabase';
|
|
453
|
-
if (url.includes('auth0.com'))
|
|
454
|
-
return 'Auth0';
|
|
455
|
-
if (url.includes('okta.com'))
|
|
456
|
-
return 'Okta';
|
|
457
|
-
if (url.includes('github.io'))
|
|
458
|
-
return 'GitHub Pages API';
|
|
459
|
-
// Internal patterns
|
|
460
|
-
if (url.startsWith('/api/'))
|
|
461
|
-
return 'Internal API';
|
|
462
|
-
if (url.startsWith('/'))
|
|
463
|
-
return 'Internal Route';
|
|
464
|
-
// Dynamic/placeholder URLs
|
|
465
|
-
if (url.startsWith('['))
|
|
466
|
-
return 'Dynamic URL';
|
|
467
|
-
return undefined;
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Normalize HTTP method
|
|
471
|
-
*/
|
|
472
|
-
normalizeMethod(method) {
|
|
473
|
-
const upper = method.toUpperCase();
|
|
474
|
-
if (['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(upper)) {
|
|
475
|
-
return upper;
|
|
476
|
-
}
|
|
477
|
-
return 'unknown';
|
|
478
|
-
}
|
|
479
|
-
}
|
package/dist/core/cache.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File-based cache for analysis results
|
|
3
|
-
* ファイルベースのキャッシュシステム
|
|
4
|
-
*/
|
|
5
|
-
export declare class AnalysisCache {
|
|
6
|
-
private cacheDir;
|
|
7
|
-
private manifest;
|
|
8
|
-
private manifestPath;
|
|
9
|
-
private dirty;
|
|
10
|
-
constructor(repoPath: string);
|
|
11
|
-
/**
|
|
12
|
-
* Initialize cache directory and load manifest
|
|
13
|
-
*/
|
|
14
|
-
init(): Promise<void>;
|
|
15
|
-
/**
|
|
16
|
-
* Compute hash for a file
|
|
17
|
-
*/
|
|
18
|
-
computeFileHash(filePath: string): Promise<string>;
|
|
19
|
-
/**
|
|
20
|
-
* Compute hash for multiple files
|
|
21
|
-
* Sort files first for consistent ordering, then use a sample of files
|
|
22
|
-
*/
|
|
23
|
-
computeFilesHash(filePaths: string[]): Promise<string>;
|
|
24
|
-
/**
|
|
25
|
-
* Get cached data if valid
|
|
26
|
-
*/
|
|
27
|
-
get<T>(key: string, currentHash: string): T | null;
|
|
28
|
-
/**
|
|
29
|
-
* Store data in cache
|
|
30
|
-
*/
|
|
31
|
-
set(key: string, hash: string, data: unknown): void;
|
|
32
|
-
/**
|
|
33
|
-
* Save manifest to disk
|
|
34
|
-
*/
|
|
35
|
-
save(): Promise<void>;
|
|
36
|
-
/**
|
|
37
|
-
* Clear all cache
|
|
38
|
-
*/
|
|
39
|
-
clear(): Promise<void>;
|
|
40
|
-
/**
|
|
41
|
-
* Get cache statistics
|
|
42
|
-
*/
|
|
43
|
-
getStats(): {
|
|
44
|
-
entries: number;
|
|
45
|
-
size: string;
|
|
46
|
-
};
|
|
47
|
-
}
|