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,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js Pages Router analyzer
|
|
3
|
+
* Analyzes API routes in pages/api directory
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
/**
|
|
8
|
+
* Analyze Next.js Pages Router API routes
|
|
9
|
+
*/
|
|
10
|
+
export async function analyzeNextPages(pagesDir = 'pages/api') {
|
|
11
|
+
const routes = [];
|
|
12
|
+
try {
|
|
13
|
+
const apiDir = path.join(process.cwd(), pagesDir);
|
|
14
|
+
if (!fs.existsSync(apiDir)) {
|
|
15
|
+
console.warn('pages/api directory not found');
|
|
16
|
+
return routes;
|
|
17
|
+
}
|
|
18
|
+
// Recursively scan API directory
|
|
19
|
+
const scanDir = (dir, basePath = '') => {
|
|
20
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
const fullPath = path.join(dir, entry.name);
|
|
23
|
+
if (entry.isDirectory()) {
|
|
24
|
+
// Handle dynamic routes like [id]
|
|
25
|
+
const dirName = entry.name.startsWith('[') && entry.name.endsWith(']')
|
|
26
|
+
? `:${entry.name.slice(1, -1)}`
|
|
27
|
+
: entry.name;
|
|
28
|
+
scanDir(fullPath, `${basePath}/${dirName}`);
|
|
29
|
+
}
|
|
30
|
+
else if (entry.isFile() && /\.(ts|js)$/.test(entry.name)) {
|
|
31
|
+
// Convert filename to route path
|
|
32
|
+
const fileName = entry.name.replace(/\.(ts|js)$/, '');
|
|
33
|
+
let routePath = basePath;
|
|
34
|
+
if (fileName !== 'index') {
|
|
35
|
+
// Handle dynamic params like [id].ts
|
|
36
|
+
const paramName = fileName.startsWith('[') && fileName.endsWith(']')
|
|
37
|
+
? `:${fileName.slice(1, -1)}`
|
|
38
|
+
: fileName;
|
|
39
|
+
routePath += `/${paramName}`;
|
|
40
|
+
}
|
|
41
|
+
// Read file to detect HTTP methods
|
|
42
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
43
|
+
const methods = detectMethods(content);
|
|
44
|
+
// Create route for each detected method
|
|
45
|
+
methods.forEach(method => {
|
|
46
|
+
routes.push({
|
|
47
|
+
method,
|
|
48
|
+
path: `/api${routePath}`,
|
|
49
|
+
description: `${method} /api${routePath}`,
|
|
50
|
+
tags: ['api'],
|
|
51
|
+
middleware: detectMiddleware(content),
|
|
52
|
+
parameters: extractParameters(routePath),
|
|
53
|
+
responses: {
|
|
54
|
+
'200': { description: 'Successful response' }
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
scanDir(apiDir);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error('Error analyzing Next.js Pages routes:', error.message);
|
|
65
|
+
}
|
|
66
|
+
return routes;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Detect HTTP methods in Next.js API route
|
|
70
|
+
*/
|
|
71
|
+
function detectMethods(content) {
|
|
72
|
+
const methods = new Set();
|
|
73
|
+
// Check for req.method checks
|
|
74
|
+
const methodChecks = content.match(/req\.method\s*===?\s*['"`](\w+)['"`]/g) || [];
|
|
75
|
+
methodChecks.forEach(check => {
|
|
76
|
+
const match = check.match(/['"`](\w+)['"`]/);
|
|
77
|
+
if (match)
|
|
78
|
+
methods.add(match[1]);
|
|
79
|
+
});
|
|
80
|
+
// If no explicit methods found, assume it handles all methods
|
|
81
|
+
if (methods.size === 0) {
|
|
82
|
+
methods.add('GET');
|
|
83
|
+
methods.add('POST');
|
|
84
|
+
}
|
|
85
|
+
return Array.from(methods);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Detect middleware usage
|
|
89
|
+
*/
|
|
90
|
+
function detectMiddleware(content) {
|
|
91
|
+
const middleware = [];
|
|
92
|
+
// Check for common middleware patterns
|
|
93
|
+
if (content.includes('auth') || content.includes('authenticate')) {
|
|
94
|
+
middleware.push('auth');
|
|
95
|
+
}
|
|
96
|
+
if (content.includes('cors')) {
|
|
97
|
+
middleware.push('cors');
|
|
98
|
+
}
|
|
99
|
+
return middleware;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Extract parameters from route path
|
|
103
|
+
*/
|
|
104
|
+
function extractParameters(routePath) {
|
|
105
|
+
const params = [];
|
|
106
|
+
const paramMatches = routePath.match(/:(\w+)/g) || [];
|
|
107
|
+
paramMatches.forEach(param => {
|
|
108
|
+
const name = param.slice(1);
|
|
109
|
+
params.push({
|
|
110
|
+
name,
|
|
111
|
+
in: 'path',
|
|
112
|
+
required: true,
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: `Path parameter: ${name}`
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
return params;
|
|
118
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Router analyzer
|
|
3
|
+
* Analyzes React Router route definitions
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
/**
|
|
8
|
+
* Analyze React Router routes
|
|
9
|
+
*/
|
|
10
|
+
export async function analyzeReactRouter(routesFile = 'src/routes.tsx') {
|
|
11
|
+
const routes = [];
|
|
12
|
+
try {
|
|
13
|
+
const routesPath = path.join(process.cwd(), routesFile);
|
|
14
|
+
if (!fs.existsSync(routesPath)) {
|
|
15
|
+
console.warn(`Routes file not found: ${routesFile}`);
|
|
16
|
+
return routes;
|
|
17
|
+
}
|
|
18
|
+
const content = fs.readFileSync(routesPath, 'utf-8');
|
|
19
|
+
// Extract routes from React Router
|
|
20
|
+
// Look for <Route path="/..." />
|
|
21
|
+
const routeRegex = /<Route\s+path=["']([^"']+)["']/g;
|
|
22
|
+
let match;
|
|
23
|
+
while ((match = routeRegex.exec(content)) !== null) {
|
|
24
|
+
const routePath = match[1];
|
|
25
|
+
routes.push({
|
|
26
|
+
method: 'GET',
|
|
27
|
+
path: routePath,
|
|
28
|
+
description: `React Router: ${routePath}`,
|
|
29
|
+
tags: ['frontend'],
|
|
30
|
+
middleware: [],
|
|
31
|
+
parameters: extractParameters(routePath),
|
|
32
|
+
responses: {
|
|
33
|
+
'200': { description: 'Page rendered' }
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
// Also check for createBrowserRouter routes
|
|
38
|
+
const browserRouterRegex = /path:\s*["']([^"']+)["']/g;
|
|
39
|
+
while ((match = browserRouterRegex.exec(content)) !== null) {
|
|
40
|
+
const routePath = match[1];
|
|
41
|
+
if (!routes.find(r => r.path === routePath)) {
|
|
42
|
+
routes.push({
|
|
43
|
+
method: 'GET',
|
|
44
|
+
path: routePath,
|
|
45
|
+
description: `React Router: ${routePath}`,
|
|
46
|
+
tags: ['frontend'],
|
|
47
|
+
middleware: [],
|
|
48
|
+
parameters: extractParameters(routePath),
|
|
49
|
+
responses: {
|
|
50
|
+
'200': { description: 'Page rendered' }
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
console.log(`✅ Found ${routes.length} React Router routes`);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error('Error analyzing React Router:', error.message);
|
|
59
|
+
}
|
|
60
|
+
return routes;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Extract parameters from route path
|
|
64
|
+
*/
|
|
65
|
+
function extractParameters(routePath) {
|
|
66
|
+
const params = [];
|
|
67
|
+
const paramMatches = routePath.match(/:(\w+)/g) || [];
|
|
68
|
+
paramMatches.forEach(param => {
|
|
69
|
+
const name = param.slice(1);
|
|
70
|
+
params.push({
|
|
71
|
+
name,
|
|
72
|
+
in: 'path',
|
|
73
|
+
required: true,
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: `Path parameter: ${name}`
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
return params;
|
|
79
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite analyzer
|
|
3
|
+
* Analyzes Vite projects with API proxy configurations
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
/**
|
|
8
|
+
* Analyze Vite API proxy routes
|
|
9
|
+
*/
|
|
10
|
+
export async function analyzeVite(viteConfigPath = 'vite.config.js') {
|
|
11
|
+
const routes = [];
|
|
12
|
+
try {
|
|
13
|
+
const configPath = path.join(process.cwd(), viteConfigPath);
|
|
14
|
+
if (!fs.existsSync(configPath)) {
|
|
15
|
+
console.warn('vite.config.js not found');
|
|
16
|
+
return routes;
|
|
17
|
+
}
|
|
18
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
19
|
+
// Extract proxy configurations
|
|
20
|
+
const proxyRegex = /proxy:\s*{([^}]+)}/gs;
|
|
21
|
+
const proxyMatch = content.match(proxyRegex);
|
|
22
|
+
if (proxyMatch) {
|
|
23
|
+
const proxyContent = proxyMatch[0];
|
|
24
|
+
// Extract proxy routes like '/api': { target: 'http://localhost:3000' }
|
|
25
|
+
const routeRegex = /['"`]([^'"`]+)['"`]\s*:\s*{/g;
|
|
26
|
+
let match;
|
|
27
|
+
while ((match = routeRegex.exec(proxyContent)) !== null) {
|
|
28
|
+
const proxyPath = match[1];
|
|
29
|
+
// Create route entry
|
|
30
|
+
routes.push({
|
|
31
|
+
method: 'ALL',
|
|
32
|
+
path: proxyPath,
|
|
33
|
+
description: `Vite proxy: ${proxyPath}`,
|
|
34
|
+
tags: ['proxy'],
|
|
35
|
+
middleware: [],
|
|
36
|
+
responses: {
|
|
37
|
+
'200': { description: 'Proxied response' }
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
console.log(`✅ Found ${routes.length} Vite proxy routes`);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error('Error analyzing Vite config:', error.message);
|
|
46
|
+
}
|
|
47
|
+
return routes;
|
|
48
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main analyzer entry point with dual module support
|
|
3
|
+
*/
|
|
4
|
+
import { analyzeExpress } from './frameworks/express.js';
|
|
5
|
+
import { analyzeNextPages } from './frameworks/nextjs-pages.js';
|
|
6
|
+
import { analyzeNextApp } from './frameworks/nextjs-app.js';
|
|
7
|
+
import { analyzeVite } from './frameworks/vite.js';
|
|
8
|
+
import { analyzeReactRouter } from './frameworks/react-router.js';
|
|
9
|
+
import { detectFramework } from './detect.js';
|
|
10
|
+
import { exportToJSON } from './export.js';
|
|
11
|
+
import { uploadRoutes } from './uploader.js';
|
|
12
|
+
// Global state for debouncing
|
|
13
|
+
const analysisState = new Map();
|
|
14
|
+
const DEBOUNCE_TIME = 5000;
|
|
15
|
+
/**
|
|
16
|
+
* Main analyze function with auto-detection
|
|
17
|
+
*/
|
|
18
|
+
export async function analyze(app, options) {
|
|
19
|
+
// Skip in production
|
|
20
|
+
if (options.skipInProduction !== false && process.env.NODE_ENV === 'production') {
|
|
21
|
+
console.log('⏭️ Skipping analysis in production');
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
// Debounce check
|
|
25
|
+
if (options.runOnce !== false) {
|
|
26
|
+
const cached = analysisState.get(options.projectName);
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
if (cached && (now - cached.timestamp) < DEBOUNCE_TIME) {
|
|
29
|
+
console.log(`⚡ Analysis skipped - already ran ${Math.round((now - cached.timestamp) / 1000)}s ago`);
|
|
30
|
+
return cached.routes;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
console.log(`🔍 Analyzing ${options.projectName}...`);
|
|
34
|
+
// CRITICAL: Prevent restart loops - force saveJson=false when uploading
|
|
35
|
+
if (options.upload !== false && options.apiKey) {
|
|
36
|
+
options.saveJson = false;
|
|
37
|
+
}
|
|
38
|
+
let routes = [];
|
|
39
|
+
let framework = options.framework;
|
|
40
|
+
// Auto-detect framework if not specified
|
|
41
|
+
if (!framework) {
|
|
42
|
+
const detected = detectFramework(app);
|
|
43
|
+
framework = detected.name;
|
|
44
|
+
console.log(`📦 Detected framework: ${framework}`);
|
|
45
|
+
}
|
|
46
|
+
// Analyze based on framework
|
|
47
|
+
switch (framework) {
|
|
48
|
+
case 'express':
|
|
49
|
+
if (!app) {
|
|
50
|
+
console.error('❌ Express app instance required');
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
routes = await analyzeExpress(app);
|
|
54
|
+
break;
|
|
55
|
+
case 'nextjs-pages':
|
|
56
|
+
routes = await analyzeNextPages(options.nextDir || 'pages/api');
|
|
57
|
+
break;
|
|
58
|
+
case 'nextjs-app':
|
|
59
|
+
routes = await analyzeNextApp(options.nextDir || 'app');
|
|
60
|
+
break;
|
|
61
|
+
case 'vite':
|
|
62
|
+
routes = await analyzeVite(options.nextDir || 'vite.config.js');
|
|
63
|
+
break;
|
|
64
|
+
case 'react-router':
|
|
65
|
+
routes = await analyzeReactRouter(options.nextDir || 'src/routes.tsx');
|
|
66
|
+
break;
|
|
67
|
+
case 'node-http':
|
|
68
|
+
case 'unknown':
|
|
69
|
+
default:
|
|
70
|
+
console.warn(`⚠️ Framework "${framework}" not supported yet`);
|
|
71
|
+
console.log('💡 Supported: express, nextjs-pages, nextjs-app, vite, react-router');
|
|
72
|
+
console.log('💡 Tip: Specify framework manually: { framework: "express" }');
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
console.log(`✅ Found ${routes.length} routes`);
|
|
76
|
+
// Cache results
|
|
77
|
+
if (options.runOnce !== false) {
|
|
78
|
+
analysisState.set(options.projectName, {
|
|
79
|
+
timestamp: Date.now(),
|
|
80
|
+
routes
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Save JSON (only if not uploading)
|
|
84
|
+
if (options.saveJson) {
|
|
85
|
+
const filePath = options.jsonPath || 'api-export.json';
|
|
86
|
+
await exportToJSON(routes, options.projectName, filePath, options.baseUrl);
|
|
87
|
+
}
|
|
88
|
+
// Upload to APIForge
|
|
89
|
+
if (options.upload !== false && options.apiKey) {
|
|
90
|
+
await uploadRoutes(routes, {
|
|
91
|
+
projectName: options.projectName,
|
|
92
|
+
apiKey: options.apiKey,
|
|
93
|
+
baseUrl: options.baseUrl,
|
|
94
|
+
apiForgeUrl: options.apiForgeUrl,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return routes;
|
|
98
|
+
}
|
|
99
|
+
// Re-export framework-specific analyzers
|
|
100
|
+
export { analyzeExpress, analyzeNextPages, analyzeNextApp, analyzeVite, analyzeReactRouter };
|
|
101
|
+
export { exportToJSON } from './export.js';
|
|
102
|
+
export { uploadRoutes } from './uploader.js';
|
|
103
|
+
export { detectFramework } from './detect.js';
|
|
104
|
+
// Default export for CommonJS
|
|
105
|
+
export default {
|
|
106
|
+
analyze,
|
|
107
|
+
analyzeExpress,
|
|
108
|
+
analyzeNextPages,
|
|
109
|
+
analyzeNextApp,
|
|
110
|
+
analyzeVite,
|
|
111
|
+
analyzeReactRouter,
|
|
112
|
+
exportToJSON,
|
|
113
|
+
uploadRoutes,
|
|
114
|
+
detectFramework
|
|
115
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { enrichRoutes } from './export.js';
|
|
3
|
+
/**
|
|
4
|
+
* Upload routes to APIForge server
|
|
5
|
+
*/
|
|
6
|
+
export async function uploadRoutes(routes, options) {
|
|
7
|
+
const url = options.apiForgeUrl || process.env.APIFORGE_URL || 'http://localhost:3000';
|
|
8
|
+
const enrichedRoutes = enrichRoutes(routes);
|
|
9
|
+
try {
|
|
10
|
+
const response = await axios.post(`${url}/api/upload-routes`, {
|
|
11
|
+
projectName: options.projectName,
|
|
12
|
+
routes: enrichedRoutes,
|
|
13
|
+
baseUrl: options.baseUrl,
|
|
14
|
+
framework: enrichedRoutes[0]?.framework || 'unknown',
|
|
15
|
+
}, {
|
|
16
|
+
headers: {
|
|
17
|
+
'Content-Type': 'application/json',
|
|
18
|
+
'X-API-Key': options.apiKey,
|
|
19
|
+
},
|
|
20
|
+
timeout: 30000,
|
|
21
|
+
});
|
|
22
|
+
console.log('✅ Uploaded to APIForge:', response.data);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.error('❌ Upload failed:', error.message);
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework detection utilities
|
|
3
|
+
*/
|
|
4
|
+
export type Framework = 'express' | 'nextjs-pages' | 'nextjs-app' | 'vite' | 'react-router' | 'node-http' | 'unknown';
|
|
5
|
+
export interface FrameworkInfo {
|
|
6
|
+
name: Framework;
|
|
7
|
+
version?: string;
|
|
8
|
+
confidence: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Detect which framework is being used
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectFramework(app?: any): FrameworkInfo;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface Route {
|
|
2
|
+
id?: number;
|
|
3
|
+
routeKey?: string;
|
|
4
|
+
method: string;
|
|
5
|
+
path: string;
|
|
6
|
+
tag: string;
|
|
7
|
+
tags?: string[];
|
|
8
|
+
operationId: string;
|
|
9
|
+
summary: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
middleware: string[];
|
|
12
|
+
authRequired: boolean;
|
|
13
|
+
pathParams: string[];
|
|
14
|
+
queryParams: string[];
|
|
15
|
+
requestBodyFields: string[];
|
|
16
|
+
headerParams?: string[];
|
|
17
|
+
responseStatuses?: number[];
|
|
18
|
+
parameters?: Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
in: 'path' | 'query' | 'header' | 'cookie';
|
|
21
|
+
required: boolean;
|
|
22
|
+
type: string;
|
|
23
|
+
schema?: any;
|
|
24
|
+
description: string;
|
|
25
|
+
}>;
|
|
26
|
+
requestBody?: any;
|
|
27
|
+
responses?: Record<string, any>;
|
|
28
|
+
linkedPaths: string[];
|
|
29
|
+
hasAuth: boolean;
|
|
30
|
+
framework: string;
|
|
31
|
+
sourceContext?: Record<string, any>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Export routes to JSON file
|
|
35
|
+
*/
|
|
36
|
+
export declare function exportToJSON(routes: Route[], projectName: string, filePath: string, baseUrl?: string): Promise<void>;
|
|
37
|
+
export declare function enrichRoutes(routes: Route[]): Route[];
|
|
38
|
+
export declare function enrichRoute(route: Route, index: number): Route;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express analyzer — accurate route + payload extraction
|
|
3
|
+
*/
|
|
4
|
+
export interface Route {
|
|
5
|
+
id?: number;
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
tag: string;
|
|
9
|
+
operationId: string;
|
|
10
|
+
summary: string;
|
|
11
|
+
middleware: string[];
|
|
12
|
+
authRequired: boolean;
|
|
13
|
+
pathParams: string[];
|
|
14
|
+
queryParams: string[];
|
|
15
|
+
requestBodyFields: string[];
|
|
16
|
+
linkedPaths: string[];
|
|
17
|
+
hasAuth: boolean;
|
|
18
|
+
framework: string;
|
|
19
|
+
responseStatuses?: number[];
|
|
20
|
+
headerParams?: string[];
|
|
21
|
+
description?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function analyzeExpress(app: any): Route[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js App Router analyzer
|
|
3
|
+
* Analyzes route handlers in app directory
|
|
4
|
+
*/
|
|
5
|
+
import { Route } from '../export.js';
|
|
6
|
+
export interface AnalyzerOptions {
|
|
7
|
+
projectName: string;
|
|
8
|
+
appDir?: string;
|
|
9
|
+
saveJson?: boolean;
|
|
10
|
+
jsonPath?: string;
|
|
11
|
+
upload?: boolean;
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
apiForgeUrl?: string;
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Analyze Next.js App Router routes
|
|
18
|
+
*/
|
|
19
|
+
export declare function analyzeNextApp(options?: AnalyzerOptions | string): Promise<Route[]>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js Pages Router analyzer
|
|
3
|
+
* Analyzes API routes in pages/api directory
|
|
4
|
+
*/
|
|
5
|
+
export interface NextPagesRoute {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
description: string;
|
|
9
|
+
tags: string[];
|
|
10
|
+
middleware: string[];
|
|
11
|
+
parameters?: any[];
|
|
12
|
+
responses: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Analyze Next.js Pages Router API routes
|
|
16
|
+
*/
|
|
17
|
+
export declare function analyzeNextPages(pagesDir?: string): Promise<NextPagesRoute[]>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Router analyzer
|
|
3
|
+
* Analyzes React Router route definitions
|
|
4
|
+
*/
|
|
5
|
+
export interface Route {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
description: string;
|
|
9
|
+
tags: string[];
|
|
10
|
+
middleware: string[];
|
|
11
|
+
parameters?: any[];
|
|
12
|
+
responses: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Analyze React Router routes
|
|
16
|
+
*/
|
|
17
|
+
export declare function analyzeReactRouter(routesFile?: string): Promise<Route[]>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite analyzer
|
|
3
|
+
* Analyzes Vite projects with API proxy configurations
|
|
4
|
+
*/
|
|
5
|
+
export interface Route {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
description: string;
|
|
9
|
+
tags: string[];
|
|
10
|
+
middleware: string[];
|
|
11
|
+
parameters?: any[];
|
|
12
|
+
responses: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Analyze Vite API proxy routes
|
|
16
|
+
*/
|
|
17
|
+
export declare function analyzeVite(viteConfigPath?: string): Promise<Route[]>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main analyzer entry point with dual module support
|
|
3
|
+
*/
|
|
4
|
+
import { analyzeExpress } from './frameworks/express.js';
|
|
5
|
+
import { analyzeNextPages } from './frameworks/nextjs-pages.js';
|
|
6
|
+
import { analyzeNextApp } from './frameworks/nextjs-app.js';
|
|
7
|
+
import { analyzeVite } from './frameworks/vite.js';
|
|
8
|
+
import { analyzeReactRouter } from './frameworks/react-router.js';
|
|
9
|
+
import { detectFramework } from './detect.js';
|
|
10
|
+
import { exportToJSON } from './export.js';
|
|
11
|
+
import { uploadRoutes } from './uploader.js';
|
|
12
|
+
export interface AnalyzerOptions {
|
|
13
|
+
projectName: string;
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
apiForgeUrl?: string;
|
|
17
|
+
upload?: boolean;
|
|
18
|
+
saveJson?: boolean;
|
|
19
|
+
jsonPath?: string;
|
|
20
|
+
framework?: string;
|
|
21
|
+
nextDir?: string;
|
|
22
|
+
skipInProduction?: boolean;
|
|
23
|
+
runOnce?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Main analyze function with auto-detection
|
|
27
|
+
*/
|
|
28
|
+
export declare function analyze(app: any, options: AnalyzerOptions): Promise<any[]>;
|
|
29
|
+
export { analyzeExpress, analyzeNextPages, analyzeNextApp, analyzeVite, analyzeReactRouter };
|
|
30
|
+
export { exportToJSON } from './export.js';
|
|
31
|
+
export { uploadRoutes } from './uploader.js';
|
|
32
|
+
export { detectFramework } from './detect.js';
|
|
33
|
+
declare const _default: {
|
|
34
|
+
analyze: typeof analyze;
|
|
35
|
+
analyzeExpress: typeof analyzeExpress;
|
|
36
|
+
analyzeNextPages: typeof analyzeNextPages;
|
|
37
|
+
analyzeNextApp: typeof analyzeNextApp;
|
|
38
|
+
analyzeVite: typeof analyzeVite;
|
|
39
|
+
analyzeReactRouter: typeof analyzeReactRouter;
|
|
40
|
+
exportToJSON: typeof exportToJSON;
|
|
41
|
+
uploadRoutes: typeof uploadRoutes;
|
|
42
|
+
detectFramework: typeof detectFramework;
|
|
43
|
+
};
|
|
44
|
+
export default _default;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Route } from './export.js';
|
|
2
|
+
export interface UploadOptions {
|
|
3
|
+
projectName: string;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
apiForgeUrl?: string;
|
|
6
|
+
apiKey: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Upload routes to APIForge server
|
|
10
|
+
*/
|
|
11
|
+
export declare function uploadRoutes(routes: Route[], options: UploadOptions): Promise<void>;
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "apiforge-analyzer",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Universal API documentation generator for Express, Next.js, Vite, React Router, and Node.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/cjs/index.js",
|
|
7
|
+
"module": "./dist/esm/index.js",
|
|
8
|
+
"types": "./dist/types/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"apiforge-analyzer": "./dist/esm/cli.js",
|
|
11
|
+
"apiforge": "./dist/esm/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./dist/esm/index.js",
|
|
16
|
+
"require": "./dist/cjs/index.js",
|
|
17
|
+
"types": "./dist/types/index.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "npm run build:esm && npm run build:cjs && npm run build:types",
|
|
26
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
27
|
+
"build:cjs": "tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
|
|
28
|
+
"build:types": "tsc -p tsconfig.types.json",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"api",
|
|
33
|
+
"documentation",
|
|
34
|
+
"openapi",
|
|
35
|
+
"swagger",
|
|
36
|
+
"express",
|
|
37
|
+
"nextjs",
|
|
38
|
+
"vite",
|
|
39
|
+
"react-router",
|
|
40
|
+
"nodejs",
|
|
41
|
+
"analyzer",
|
|
42
|
+
"cli"
|
|
43
|
+
],
|
|
44
|
+
"author": "APIForge",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"express": "^4.0.0 || ^5.0.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"express": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@clack/prompts": "^0.9.1",
|
|
56
|
+
"axios": "^1.6.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/express": "^4.17.21",
|
|
60
|
+
"@types/node": "^20.0.0",
|
|
61
|
+
"typescript": "^5.3.0"
|
|
62
|
+
}
|
|
63
|
+
}
|