apiforge-analyzer 1.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/README.md +279 -0
- package/dist/cjs/cli.js +419 -0
- package/dist/cjs/detect.js +68 -0
- package/dist/cjs/export.js +321 -0
- package/dist/cjs/frameworks/express.js +245 -0
- package/dist/cjs/frameworks/nextjs-app.js +181 -0
- package/dist/cjs/frameworks/nextjs-pages.js +154 -0
- package/dist/cjs/frameworks/react-router.js +115 -0
- package/dist/cjs/frameworks/vite.js +84 -0
- package/dist/cjs/index.js +125 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/uploader.js +34 -0
- package/dist/esm/cli.js +417 -0
- package/dist/esm/detect.js +65 -0
- package/dist/esm/export.js +283 -0
- package/dist/esm/frameworks/express.js +242 -0
- package/dist/esm/frameworks/nextjs-app.js +145 -0
- package/dist/esm/frameworks/nextjs-pages.js +118 -0
- package/dist/esm/frameworks/react-router.js +79 -0
- package/dist/esm/frameworks/vite.js +48 -0
- package/dist/esm/index.js +115 -0
- package/dist/esm/uploader.js +28 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/detect.d.ts +13 -0
- package/dist/types/export.d.ts +38 -0
- package/dist/types/frameworks/express.d.ts +23 -0
- package/dist/types/frameworks/nextjs-app.d.ts +19 -0
- package/dist/types/frameworks/nextjs-pages.d.ts +17 -0
- package/dist/types/frameworks/react-router.d.ts +17 -0
- package/dist/types/frameworks/vite.d.ts +17 -0
- package/dist/types/index.d.ts +44 -0
- package/dist/types/uploader.d.ts +11 -0
- package/package.json +63 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Next.js App Router analyzer
|
|
4
|
+
* Analyzes route handlers in app directory
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.analyzeNextApp = analyzeNextApp;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const export_js_1 = require("../export.js");
|
|
44
|
+
const uploader_js_1 = require("../uploader.js");
|
|
45
|
+
/**
|
|
46
|
+
* Analyze Next.js App Router routes
|
|
47
|
+
*/
|
|
48
|
+
async function analyzeNextApp(options = 'app') {
|
|
49
|
+
// Support both string path and options object
|
|
50
|
+
const appDir = typeof options === 'string' ? options : (options.appDir || 'app');
|
|
51
|
+
const opts = typeof options === 'string' ? { projectName: '' } : options;
|
|
52
|
+
const routes = [];
|
|
53
|
+
try {
|
|
54
|
+
const baseDir = path.join(process.cwd(), appDir);
|
|
55
|
+
if (!fs.existsSync(baseDir)) {
|
|
56
|
+
console.warn('app directory not found');
|
|
57
|
+
return routes;
|
|
58
|
+
}
|
|
59
|
+
// Recursively scan for route.ts/route.js files
|
|
60
|
+
const scanDir = (dir, basePath = '') => {
|
|
61
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
const fullPath = path.join(dir, entry.name);
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
// Handle dynamic routes like [id]
|
|
66
|
+
const dirName = entry.name.startsWith('[') && entry.name.endsWith(']')
|
|
67
|
+
? `:${entry.name.slice(1, -1)}`
|
|
68
|
+
: entry.name;
|
|
69
|
+
// Skip non-route directories
|
|
70
|
+
if (['_components', '_lib', '_utils'].includes(entry.name)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
scanDir(fullPath, `${basePath}/${dirName}`);
|
|
74
|
+
}
|
|
75
|
+
else if (entry.isFile() && /route\.(ts|js)$/.test(entry.name)) {
|
|
76
|
+
// Found a route file
|
|
77
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
78
|
+
const methods = detectAppRouterMethods(content);
|
|
79
|
+
methods.forEach(method => {
|
|
80
|
+
const pathString = basePath || '/';
|
|
81
|
+
const operationId = `${method.toLowerCase()}_${pathString.replace(/[^a-zA-Z0-9]/g, '_').replace(/_+/g, '_').replace(/^_|_$/g, '')}`;
|
|
82
|
+
const tags = pathString.startsWith('/api') ? ['api'] : ['app'];
|
|
83
|
+
const middleware = detectMiddleware(content);
|
|
84
|
+
const authRequired = middleware.includes('auth');
|
|
85
|
+
const pathParams = extractParameters(pathString).map(p => p.name);
|
|
86
|
+
// Basic static extraction
|
|
87
|
+
const queryParams = [];
|
|
88
|
+
const queryHits = [...content.matchAll(/searchParams\.get\(['"]([^'"]+)['"]\)/g)];
|
|
89
|
+
queryHits.forEach(m => queryParams.push(m[1]));
|
|
90
|
+
const requestBodyFields = [];
|
|
91
|
+
const bodyDestruct = content.match(/const\s+\{([^}]+)\}\s*=\s*await\s+req\.json\(\)/);
|
|
92
|
+
if (bodyDestruct) {
|
|
93
|
+
bodyDestruct[1].split(',').map(s => s.trim().split(':')[0]).filter(Boolean).forEach(m => requestBodyFields.push(m));
|
|
94
|
+
}
|
|
95
|
+
routes.push({
|
|
96
|
+
method,
|
|
97
|
+
path: pathString,
|
|
98
|
+
tag: tags[0] || 'General',
|
|
99
|
+
operationId,
|
|
100
|
+
summary: `${method} ${pathString}`,
|
|
101
|
+
middleware,
|
|
102
|
+
authRequired,
|
|
103
|
+
pathParams: Array.from(new Set(pathParams)),
|
|
104
|
+
queryParams: Array.from(new Set(queryParams)),
|
|
105
|
+
requestBodyFields: Array.from(new Set(requestBodyFields)),
|
|
106
|
+
linkedPaths: [],
|
|
107
|
+
hasAuth: authRequired,
|
|
108
|
+
framework: 'nextjs-app'
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
scanDir(baseDir);
|
|
115
|
+
console.log(`✅ Found ${routes.length} routes`);
|
|
116
|
+
// Export to JSON if requested
|
|
117
|
+
if (opts.saveJson && opts.projectName) {
|
|
118
|
+
const filePath = opts.jsonPath || 'api-export.json';
|
|
119
|
+
await (0, export_js_1.exportToJSON)(routes, opts.projectName, filePath, opts.baseUrl);
|
|
120
|
+
}
|
|
121
|
+
// Upload to APIForge if requested
|
|
122
|
+
if (opts.upload && opts.apiKey && opts.projectName) {
|
|
123
|
+
await (0, uploader_js_1.uploadRoutes)(routes, {
|
|
124
|
+
projectName: opts.projectName,
|
|
125
|
+
apiKey: opts.apiKey,
|
|
126
|
+
baseUrl: opts.baseUrl,
|
|
127
|
+
apiForgeUrl: opts.apiForgeUrl
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
console.error('Error analyzing Next.js App routes:', error.message);
|
|
133
|
+
}
|
|
134
|
+
return routes;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Detect exported HTTP method handlers
|
|
138
|
+
*/
|
|
139
|
+
function detectAppRouterMethods(content) {
|
|
140
|
+
const methods = [];
|
|
141
|
+
const httpMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
|
|
142
|
+
httpMethods.forEach(method => {
|
|
143
|
+
// Check for export async function METHOD or export function METHOD
|
|
144
|
+
const regex = new RegExp(`export\\s+(async\\s+)?function\\s+${method}\\s*\\(`, 'g');
|
|
145
|
+
if (regex.test(content)) {
|
|
146
|
+
methods.push(method);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return methods;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Detect middleware usage
|
|
153
|
+
*/
|
|
154
|
+
function detectMiddleware(content) {
|
|
155
|
+
const middleware = [];
|
|
156
|
+
if (content.includes('auth') || content.includes('authenticate')) {
|
|
157
|
+
middleware.push('auth');
|
|
158
|
+
}
|
|
159
|
+
if (content.includes('cors')) {
|
|
160
|
+
middleware.push('cors');
|
|
161
|
+
}
|
|
162
|
+
return middleware;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Extract parameters from route path
|
|
166
|
+
*/
|
|
167
|
+
function extractParameters(routePath) {
|
|
168
|
+
const params = [];
|
|
169
|
+
const paramMatches = routePath.match(/:(\w+)/g) || [];
|
|
170
|
+
paramMatches.forEach(param => {
|
|
171
|
+
const name = param.slice(1);
|
|
172
|
+
params.push({
|
|
173
|
+
name,
|
|
174
|
+
in: 'path',
|
|
175
|
+
required: true,
|
|
176
|
+
type: 'string',
|
|
177
|
+
description: `Path parameter: ${name}`
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
return params;
|
|
181
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Next.js Pages Router analyzer
|
|
4
|
+
* Analyzes API routes in pages/api directory
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.analyzeNextPages = analyzeNextPages;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* Analyze Next.js Pages Router API routes
|
|
45
|
+
*/
|
|
46
|
+
async function analyzeNextPages(pagesDir = 'pages/api') {
|
|
47
|
+
const routes = [];
|
|
48
|
+
try {
|
|
49
|
+
const apiDir = path.join(process.cwd(), pagesDir);
|
|
50
|
+
if (!fs.existsSync(apiDir)) {
|
|
51
|
+
console.warn('pages/api directory not found');
|
|
52
|
+
return routes;
|
|
53
|
+
}
|
|
54
|
+
// Recursively scan API directory
|
|
55
|
+
const scanDir = (dir, basePath = '') => {
|
|
56
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
const fullPath = path.join(dir, entry.name);
|
|
59
|
+
if (entry.isDirectory()) {
|
|
60
|
+
// Handle dynamic routes like [id]
|
|
61
|
+
const dirName = entry.name.startsWith('[') && entry.name.endsWith(']')
|
|
62
|
+
? `:${entry.name.slice(1, -1)}`
|
|
63
|
+
: entry.name;
|
|
64
|
+
scanDir(fullPath, `${basePath}/${dirName}`);
|
|
65
|
+
}
|
|
66
|
+
else if (entry.isFile() && /\.(ts|js)$/.test(entry.name)) {
|
|
67
|
+
// Convert filename to route path
|
|
68
|
+
const fileName = entry.name.replace(/\.(ts|js)$/, '');
|
|
69
|
+
let routePath = basePath;
|
|
70
|
+
if (fileName !== 'index') {
|
|
71
|
+
// Handle dynamic params like [id].ts
|
|
72
|
+
const paramName = fileName.startsWith('[') && fileName.endsWith(']')
|
|
73
|
+
? `:${fileName.slice(1, -1)}`
|
|
74
|
+
: fileName;
|
|
75
|
+
routePath += `/${paramName}`;
|
|
76
|
+
}
|
|
77
|
+
// Read file to detect HTTP methods
|
|
78
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
79
|
+
const methods = detectMethods(content);
|
|
80
|
+
// Create route for each detected method
|
|
81
|
+
methods.forEach(method => {
|
|
82
|
+
routes.push({
|
|
83
|
+
method,
|
|
84
|
+
path: `/api${routePath}`,
|
|
85
|
+
description: `${method} /api${routePath}`,
|
|
86
|
+
tags: ['api'],
|
|
87
|
+
middleware: detectMiddleware(content),
|
|
88
|
+
parameters: extractParameters(routePath),
|
|
89
|
+
responses: {
|
|
90
|
+
'200': { description: 'Successful response' }
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
scanDir(apiDir);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
console.error('Error analyzing Next.js Pages routes:', error.message);
|
|
101
|
+
}
|
|
102
|
+
return routes;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Detect HTTP methods in Next.js API route
|
|
106
|
+
*/
|
|
107
|
+
function detectMethods(content) {
|
|
108
|
+
const methods = new Set();
|
|
109
|
+
// Check for req.method checks
|
|
110
|
+
const methodChecks = content.match(/req\.method\s*===?\s*['"`](\w+)['"`]/g) || [];
|
|
111
|
+
methodChecks.forEach(check => {
|
|
112
|
+
const match = check.match(/['"`](\w+)['"`]/);
|
|
113
|
+
if (match)
|
|
114
|
+
methods.add(match[1]);
|
|
115
|
+
});
|
|
116
|
+
// If no explicit methods found, assume it handles all methods
|
|
117
|
+
if (methods.size === 0) {
|
|
118
|
+
methods.add('GET');
|
|
119
|
+
methods.add('POST');
|
|
120
|
+
}
|
|
121
|
+
return Array.from(methods);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Detect middleware usage
|
|
125
|
+
*/
|
|
126
|
+
function detectMiddleware(content) {
|
|
127
|
+
const middleware = [];
|
|
128
|
+
// Check for common middleware patterns
|
|
129
|
+
if (content.includes('auth') || content.includes('authenticate')) {
|
|
130
|
+
middleware.push('auth');
|
|
131
|
+
}
|
|
132
|
+
if (content.includes('cors')) {
|
|
133
|
+
middleware.push('cors');
|
|
134
|
+
}
|
|
135
|
+
return middleware;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Extract parameters from route path
|
|
139
|
+
*/
|
|
140
|
+
function extractParameters(routePath) {
|
|
141
|
+
const params = [];
|
|
142
|
+
const paramMatches = routePath.match(/:(\w+)/g) || [];
|
|
143
|
+
paramMatches.forEach(param => {
|
|
144
|
+
const name = param.slice(1);
|
|
145
|
+
params.push({
|
|
146
|
+
name,
|
|
147
|
+
in: 'path',
|
|
148
|
+
required: true,
|
|
149
|
+
type: 'string',
|
|
150
|
+
description: `Path parameter: ${name}`
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
return params;
|
|
154
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* React Router analyzer
|
|
4
|
+
* Analyzes React Router route definitions
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.analyzeReactRouter = analyzeReactRouter;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* Analyze React Router routes
|
|
45
|
+
*/
|
|
46
|
+
async function analyzeReactRouter(routesFile = 'src/routes.tsx') {
|
|
47
|
+
const routes = [];
|
|
48
|
+
try {
|
|
49
|
+
const routesPath = path.join(process.cwd(), routesFile);
|
|
50
|
+
if (!fs.existsSync(routesPath)) {
|
|
51
|
+
console.warn(`Routes file not found: ${routesFile}`);
|
|
52
|
+
return routes;
|
|
53
|
+
}
|
|
54
|
+
const content = fs.readFileSync(routesPath, 'utf-8');
|
|
55
|
+
// Extract routes from React Router
|
|
56
|
+
// Look for <Route path="/..." />
|
|
57
|
+
const routeRegex = /<Route\s+path=["']([^"']+)["']/g;
|
|
58
|
+
let match;
|
|
59
|
+
while ((match = routeRegex.exec(content)) !== null) {
|
|
60
|
+
const routePath = match[1];
|
|
61
|
+
routes.push({
|
|
62
|
+
method: 'GET',
|
|
63
|
+
path: routePath,
|
|
64
|
+
description: `React Router: ${routePath}`,
|
|
65
|
+
tags: ['frontend'],
|
|
66
|
+
middleware: [],
|
|
67
|
+
parameters: extractParameters(routePath),
|
|
68
|
+
responses: {
|
|
69
|
+
'200': { description: 'Page rendered' }
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// Also check for createBrowserRouter routes
|
|
74
|
+
const browserRouterRegex = /path:\s*["']([^"']+)["']/g;
|
|
75
|
+
while ((match = browserRouterRegex.exec(content)) !== null) {
|
|
76
|
+
const routePath = match[1];
|
|
77
|
+
if (!routes.find(r => r.path === routePath)) {
|
|
78
|
+
routes.push({
|
|
79
|
+
method: 'GET',
|
|
80
|
+
path: routePath,
|
|
81
|
+
description: `React Router: ${routePath}`,
|
|
82
|
+
tags: ['frontend'],
|
|
83
|
+
middleware: [],
|
|
84
|
+
parameters: extractParameters(routePath),
|
|
85
|
+
responses: {
|
|
86
|
+
'200': { description: 'Page rendered' }
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
console.log(`✅ Found ${routes.length} React Router routes`);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.error('Error analyzing React Router:', error.message);
|
|
95
|
+
}
|
|
96
|
+
return routes;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Extract parameters from route path
|
|
100
|
+
*/
|
|
101
|
+
function extractParameters(routePath) {
|
|
102
|
+
const params = [];
|
|
103
|
+
const paramMatches = routePath.match(/:(\w+)/g) || [];
|
|
104
|
+
paramMatches.forEach(param => {
|
|
105
|
+
const name = param.slice(1);
|
|
106
|
+
params.push({
|
|
107
|
+
name,
|
|
108
|
+
in: 'path',
|
|
109
|
+
required: true,
|
|
110
|
+
type: 'string',
|
|
111
|
+
description: `Path parameter: ${name}`
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
return params;
|
|
115
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Vite analyzer
|
|
4
|
+
* Analyzes Vite projects with API proxy configurations
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.analyzeVite = analyzeVite;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* Analyze Vite API proxy routes
|
|
45
|
+
*/
|
|
46
|
+
async function analyzeVite(viteConfigPath = 'vite.config.js') {
|
|
47
|
+
const routes = [];
|
|
48
|
+
try {
|
|
49
|
+
const configPath = path.join(process.cwd(), viteConfigPath);
|
|
50
|
+
if (!fs.existsSync(configPath)) {
|
|
51
|
+
console.warn('vite.config.js not found');
|
|
52
|
+
return routes;
|
|
53
|
+
}
|
|
54
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
55
|
+
// Extract proxy configurations
|
|
56
|
+
const proxyRegex = /proxy:\s*{([^}]+)}/gs;
|
|
57
|
+
const proxyMatch = content.match(proxyRegex);
|
|
58
|
+
if (proxyMatch) {
|
|
59
|
+
const proxyContent = proxyMatch[0];
|
|
60
|
+
// Extract proxy routes like '/api': { target: 'http://localhost:3000' }
|
|
61
|
+
const routeRegex = /['"`]([^'"`]+)['"`]\s*:\s*{/g;
|
|
62
|
+
let match;
|
|
63
|
+
while ((match = routeRegex.exec(proxyContent)) !== null) {
|
|
64
|
+
const proxyPath = match[1];
|
|
65
|
+
// Create route entry
|
|
66
|
+
routes.push({
|
|
67
|
+
method: 'ALL',
|
|
68
|
+
path: proxyPath,
|
|
69
|
+
description: `Vite proxy: ${proxyPath}`,
|
|
70
|
+
tags: ['proxy'],
|
|
71
|
+
middleware: [],
|
|
72
|
+
responses: {
|
|
73
|
+
'200': { description: 'Proxied response' }
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
console.log(`✅ Found ${routes.length} Vite proxy routes`);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error('Error analyzing Vite config:', error.message);
|
|
82
|
+
}
|
|
83
|
+
return routes;
|
|
84
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Main analyzer entry point with dual module support
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.detectFramework = exports.uploadRoutes = exports.exportToJSON = exports.analyzeReactRouter = exports.analyzeVite = exports.analyzeNextApp = exports.analyzeNextPages = exports.analyzeExpress = void 0;
|
|
7
|
+
exports.analyze = analyze;
|
|
8
|
+
const express_js_1 = require("./frameworks/express.js");
|
|
9
|
+
Object.defineProperty(exports, "analyzeExpress", { enumerable: true, get: function () { return express_js_1.analyzeExpress; } });
|
|
10
|
+
const nextjs_pages_js_1 = require("./frameworks/nextjs-pages.js");
|
|
11
|
+
Object.defineProperty(exports, "analyzeNextPages", { enumerable: true, get: function () { return nextjs_pages_js_1.analyzeNextPages; } });
|
|
12
|
+
const nextjs_app_js_1 = require("./frameworks/nextjs-app.js");
|
|
13
|
+
Object.defineProperty(exports, "analyzeNextApp", { enumerable: true, get: function () { return nextjs_app_js_1.analyzeNextApp; } });
|
|
14
|
+
const vite_js_1 = require("./frameworks/vite.js");
|
|
15
|
+
Object.defineProperty(exports, "analyzeVite", { enumerable: true, get: function () { return vite_js_1.analyzeVite; } });
|
|
16
|
+
const react_router_js_1 = require("./frameworks/react-router.js");
|
|
17
|
+
Object.defineProperty(exports, "analyzeReactRouter", { enumerable: true, get: function () { return react_router_js_1.analyzeReactRouter; } });
|
|
18
|
+
const detect_js_1 = require("./detect.js");
|
|
19
|
+
const export_js_1 = require("./export.js");
|
|
20
|
+
const uploader_js_1 = require("./uploader.js");
|
|
21
|
+
// Global state for debouncing
|
|
22
|
+
const analysisState = new Map();
|
|
23
|
+
const DEBOUNCE_TIME = 5000;
|
|
24
|
+
/**
|
|
25
|
+
* Main analyze function with auto-detection
|
|
26
|
+
*/
|
|
27
|
+
async function analyze(app, options) {
|
|
28
|
+
// Skip in production
|
|
29
|
+
if (options.skipInProduction !== false && process.env.NODE_ENV === 'production') {
|
|
30
|
+
console.log('⏭️ Skipping analysis in production');
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
// Debounce check
|
|
34
|
+
if (options.runOnce !== false) {
|
|
35
|
+
const cached = analysisState.get(options.projectName);
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
if (cached && (now - cached.timestamp) < DEBOUNCE_TIME) {
|
|
38
|
+
console.log(`⚡ Analysis skipped - already ran ${Math.round((now - cached.timestamp) / 1000)}s ago`);
|
|
39
|
+
return cached.routes;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
console.log(`🔍 Analyzing ${options.projectName}...`);
|
|
43
|
+
// CRITICAL: Prevent restart loops - force saveJson=false when uploading
|
|
44
|
+
if (options.upload !== false && options.apiKey) {
|
|
45
|
+
options.saveJson = false;
|
|
46
|
+
}
|
|
47
|
+
let routes = [];
|
|
48
|
+
let framework = options.framework;
|
|
49
|
+
// Auto-detect framework if not specified
|
|
50
|
+
if (!framework) {
|
|
51
|
+
const detected = (0, detect_js_1.detectFramework)(app);
|
|
52
|
+
framework = detected.name;
|
|
53
|
+
console.log(`📦 Detected framework: ${framework}`);
|
|
54
|
+
}
|
|
55
|
+
// Analyze based on framework
|
|
56
|
+
switch (framework) {
|
|
57
|
+
case 'express':
|
|
58
|
+
if (!app) {
|
|
59
|
+
console.error('❌ Express app instance required');
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
routes = await (0, express_js_1.analyzeExpress)(app);
|
|
63
|
+
break;
|
|
64
|
+
case 'nextjs-pages':
|
|
65
|
+
routes = await (0, nextjs_pages_js_1.analyzeNextPages)(options.nextDir || 'pages/api');
|
|
66
|
+
break;
|
|
67
|
+
case 'nextjs-app':
|
|
68
|
+
routes = await (0, nextjs_app_js_1.analyzeNextApp)(options.nextDir || 'app');
|
|
69
|
+
break;
|
|
70
|
+
case 'vite':
|
|
71
|
+
routes = await (0, vite_js_1.analyzeVite)(options.nextDir || 'vite.config.js');
|
|
72
|
+
break;
|
|
73
|
+
case 'react-router':
|
|
74
|
+
routes = await (0, react_router_js_1.analyzeReactRouter)(options.nextDir || 'src/routes.tsx');
|
|
75
|
+
break;
|
|
76
|
+
case 'node-http':
|
|
77
|
+
case 'unknown':
|
|
78
|
+
default:
|
|
79
|
+
console.warn(`⚠️ Framework "${framework}" not supported yet`);
|
|
80
|
+
console.log('💡 Supported: express, nextjs-pages, nextjs-app, vite, react-router');
|
|
81
|
+
console.log('💡 Tip: Specify framework manually: { framework: "express" }');
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
console.log(`✅ Found ${routes.length} routes`);
|
|
85
|
+
// Cache results
|
|
86
|
+
if (options.runOnce !== false) {
|
|
87
|
+
analysisState.set(options.projectName, {
|
|
88
|
+
timestamp: Date.now(),
|
|
89
|
+
routes
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// Save JSON (only if not uploading)
|
|
93
|
+
if (options.saveJson) {
|
|
94
|
+
const filePath = options.jsonPath || 'api-export.json';
|
|
95
|
+
await (0, export_js_1.exportToJSON)(routes, options.projectName, filePath, options.baseUrl);
|
|
96
|
+
}
|
|
97
|
+
// Upload to APIForge
|
|
98
|
+
if (options.upload !== false && options.apiKey) {
|
|
99
|
+
await (0, uploader_js_1.uploadRoutes)(routes, {
|
|
100
|
+
projectName: options.projectName,
|
|
101
|
+
apiKey: options.apiKey,
|
|
102
|
+
baseUrl: options.baseUrl,
|
|
103
|
+
apiForgeUrl: options.apiForgeUrl,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return routes;
|
|
107
|
+
}
|
|
108
|
+
var export_js_2 = require("./export.js");
|
|
109
|
+
Object.defineProperty(exports, "exportToJSON", { enumerable: true, get: function () { return export_js_2.exportToJSON; } });
|
|
110
|
+
var uploader_js_2 = require("./uploader.js");
|
|
111
|
+
Object.defineProperty(exports, "uploadRoutes", { enumerable: true, get: function () { return uploader_js_2.uploadRoutes; } });
|
|
112
|
+
var detect_js_2 = require("./detect.js");
|
|
113
|
+
Object.defineProperty(exports, "detectFramework", { enumerable: true, get: function () { return detect_js_2.detectFramework; } });
|
|
114
|
+
// Default export for CommonJS
|
|
115
|
+
exports.default = {
|
|
116
|
+
analyze,
|
|
117
|
+
analyzeExpress: express_js_1.analyzeExpress,
|
|
118
|
+
analyzeNextPages: nextjs_pages_js_1.analyzeNextPages,
|
|
119
|
+
analyzeNextApp: nextjs_app_js_1.analyzeNextApp,
|
|
120
|
+
analyzeVite: vite_js_1.analyzeVite,
|
|
121
|
+
analyzeReactRouter: react_router_js_1.analyzeReactRouter,
|
|
122
|
+
exportToJSON: export_js_1.exportToJSON,
|
|
123
|
+
uploadRoutes: uploader_js_1.uploadRoutes,
|
|
124
|
+
detectFramework: detect_js_1.detectFramework
|
|
125
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|