@aimeloic/monkey-tester 4.0.3 → 4.0.5
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/html.backup.js +109 -101
- package/htmlTemplate.js +376 -120
- package/monkey.backup.js +50 -97
- package/monkey.js +38 -100
- package/package.json +1 -1
- package/temp.backup.js +279 -0
package/monkey.backup.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { getHtmlTemplate } from './htmlTemplate.js';
|
|
4
4
|
|
|
5
|
-
// ─── Field type inference ─────────────────────────────────────────────────────
|
|
6
5
|
function inferType(name) {
|
|
7
6
|
const n = name.toLowerCase();
|
|
8
7
|
if (n.includes('email')) return 'email';
|
|
@@ -28,15 +27,12 @@ function buildField(name) {
|
|
|
28
27
|
};
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
// ─── Extract req.body fields from handler source ──────────────────────────────
|
|
32
30
|
function extractBodyFields(handler) {
|
|
33
31
|
try {
|
|
34
32
|
const source = handler.toString();
|
|
35
33
|
if (!source || source.includes('[native code]')) return [];
|
|
36
34
|
|
|
37
35
|
const seen = new Map();
|
|
38
|
-
|
|
39
|
-
// Pattern 1 — destructuring: const { email, password } = req.body
|
|
40
36
|
const destructRe = /(?:const|let|var)\s*\{\s*([^}]+)\s*\}\s*=\s*req\.body/g;
|
|
41
37
|
let m;
|
|
42
38
|
while ((m = destructRe.exec(source)) !== null) {
|
|
@@ -48,7 +44,6 @@ function extractBodyFields(handler) {
|
|
|
48
44
|
});
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
// Pattern 2 — property access: req.body.email / req.body['email']
|
|
52
47
|
const accessRe = /req\.body\.([a-zA-Z_$][a-zA-Z0-9_$]*)|req\.body\[['"]([a-zA-Z_$][a-zA-Z0-9_$]*)['"]]/g;
|
|
53
48
|
while ((m = accessRe.exec(source)) !== null) {
|
|
54
49
|
const name = m[1] || m[2];
|
|
@@ -61,67 +56,35 @@ function extractBodyFields(handler) {
|
|
|
61
56
|
}
|
|
62
57
|
}
|
|
63
58
|
|
|
64
|
-
// ─── Path-based fallback fields ───────────────────────────────────────────────
|
|
65
59
|
function fallbackFields(path) {
|
|
66
60
|
const p = path.toLowerCase();
|
|
67
|
-
|
|
68
|
-
if (p.includes('
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (p.includes('
|
|
72
|
-
return ['username', 'email', 'password'].map(buildField);
|
|
73
|
-
}
|
|
74
|
-
if (p.includes('user')) {
|
|
75
|
-
return ['username', 'email', 'password'].map(buildField);
|
|
76
|
-
}
|
|
77
|
-
if (p.includes('product')) {
|
|
78
|
-
return ['name', 'price', 'stock'].map(buildField);
|
|
79
|
-
}
|
|
80
|
-
if (p.includes('order')) {
|
|
81
|
-
return ['productId', 'quantity', 'address'].map(buildField);
|
|
82
|
-
}
|
|
61
|
+
if (p.includes('login') || p.includes('signin') || p.includes('auth/login')) return ['email', 'password'].map(buildField);
|
|
62
|
+
if (p.includes('register') || p.includes('signup') || p.includes('auth/register')) return ['username', 'email', 'password'].map(buildField);
|
|
63
|
+
if (p.includes('user')) return ['username', 'email', 'password'].map(buildField);
|
|
64
|
+
if (p.includes('product')) return ['name', 'price', 'stock'].map(buildField);
|
|
65
|
+
if (p.includes('order')) return ['productId', 'quantity', 'address'].map(buildField);
|
|
83
66
|
return [];
|
|
84
67
|
}
|
|
85
68
|
|
|
86
|
-
// ─── Extract router prefix safely from Express layer ─────────────────────────
|
|
87
69
|
function extractRouterPrefix(layer) {
|
|
88
|
-
// Prefer explicit path if available
|
|
89
|
-
if (layer.path && typeof layer.path === 'string') {
|
|
90
|
-
return layer.path === '/' ? '' : layer.path;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
70
|
if (!layer.regexp) return '';
|
|
94
|
-
|
|
95
|
-
// Convert the regexp back to a path prefix by looking at the regexp source
|
|
96
|
-
// Express generates regexps like: /^\/api\/v1\/?(?=\/|$)/i
|
|
97
71
|
const src = layer.regexp.source;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// Unescape the extracted path
|
|
105
|
-
const raw = match[1].replace(/\\\//g, '/');
|
|
106
|
-
|
|
107
|
-
// Remove trailing slash if present
|
|
108
|
-
return raw.replace(/\/$/, '') || '';
|
|
72
|
+
const patterns = [/^\^\\\/([^\\?$]+)/, /^\^\\\/([a-zA-Z0-9_/-]+)/];
|
|
73
|
+
for (const re of patterns) {
|
|
74
|
+
const m = re.exec(src);
|
|
75
|
+
if (m && m[1]) return '/' + m[1].replace(/\\\//g, '/').replace(/\\/g, '');
|
|
76
|
+
}
|
|
77
|
+
return '';
|
|
109
78
|
}
|
|
110
79
|
|
|
111
|
-
// ─── Walk the Express router stack recursively ────────────────────────────────
|
|
112
80
|
function parseStack(stack, detectedEndpoints, prefix = '') {
|
|
113
81
|
if (!Array.isArray(stack)) return;
|
|
114
82
|
|
|
115
83
|
for (const layer of stack) {
|
|
116
|
-
// ── Named route (app.get / app.post …) ──────────────────────────────────
|
|
117
84
|
if (layer.route) {
|
|
118
|
-
const rawPath = typeof layer.route.path === 'string'
|
|
119
|
-
? layer.route.path
|
|
120
|
-
: (layer.route.path ? String(layer.route.path) : '');
|
|
121
|
-
|
|
85
|
+
const rawPath = typeof layer.route.path === 'string' ? layer.route.path : (layer.route.path ? String(layer.route.path) : '');
|
|
122
86
|
const fullPath = (prefix + rawPath).replace(/\/+/g, '/') || '/';
|
|
123
87
|
|
|
124
|
-
// Skip the tester route itself
|
|
125
88
|
if (fullPath.startsWith('/api/tester')) continue;
|
|
126
89
|
|
|
127
90
|
const methods = Object.keys(layer.route.methods || {});
|
|
@@ -130,76 +93,66 @@ function parseStack(stack, detectedEndpoints, prefix = '') {
|
|
|
130
93
|
const httpMethod = method.toUpperCase();
|
|
131
94
|
const key = `${httpMethod}::${fullPath}`;
|
|
132
95
|
|
|
133
|
-
// ── Path params (:id, :slug …) ────────────────────────────────────
|
|
134
96
|
const pathParams = [];
|
|
135
97
|
const paramRe = /:([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
pathParams.push({
|
|
140
|
-
name: pm[1],
|
|
141
|
-
label: pm[1].charAt(0).toUpperCase() + pm[1].slice(1),
|
|
142
|
-
placeholder: 'value'
|
|
143
|
-
});
|
|
98
|
+
let pm;
|
|
99
|
+
while ((pm = paramRe.exec(fullPath)) !== null) {
|
|
100
|
+
pathParams.push({ name: pm[1], label: pm[1].charAt(0).toUpperCase() + pm[1].slice(1), placeholder: 'value' });
|
|
144
101
|
}
|
|
145
102
|
|
|
146
|
-
// ── Body fields ──────────────────────────────────────────────────
|
|
147
103
|
let bodyFields = [];
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
104
|
+
// Inside your parseStack function in monkey.js, update this block:
|
|
105
|
+
if (['POST', 'PUT', 'PATCH'].includes(httpMethod)) {
|
|
106
|
+
const handlers = (layer.route.stack || []).map(sl => sl.handle).filter(Boolean);
|
|
107
|
+
for (const handler of handlers) {
|
|
108
|
+
bodyFields.push(...extractBodyFields(handler));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const seen = new Map();
|
|
112
|
+
bodyFields = bodyFields.filter(f => {
|
|
113
|
+
if (seen.has(f.name)) return false;
|
|
114
|
+
seen.set(f.name, true);
|
|
115
|
+
return true;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// CHANGE THIS: Only apply generic fallbacks if there are no explicit path parameters
|
|
119
|
+
if (bodyFields.length === 0 && pathParams.length === 0) {
|
|
120
|
+
bodyFields = fallbackFields(fullPath);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
164
123
|
|
|
165
124
|
detectedEndpoints[key] = {
|
|
166
|
-
method:
|
|
167
|
-
path:
|
|
168
|
-
title:
|
|
169
|
-
desc:
|
|
170
|
-
params:
|
|
171
|
-
fields:
|
|
125
|
+
method: httpMethod,
|
|
126
|
+
path: fullPath,
|
|
127
|
+
title: `${httpMethod} ${fullPath}`,
|
|
128
|
+
desc: `Auto-discovered endpoint - ${fullPath}`, // Safe ASCII character
|
|
129
|
+
params: pathParams,
|
|
130
|
+
fields: bodyFields,
|
|
172
131
|
};
|
|
173
132
|
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// ── Nested router (app.use('/prefix', router)) ───────────────────────────
|
|
177
|
-
else if (layer.handle && typeof layer.handle === 'function' && layer.handle.stack) {
|
|
133
|
+
} else if (layer.name === 'router' && layer.handle && layer.handle.stack) {
|
|
178
134
|
const routerPrefix = extractRouterPrefix(layer);
|
|
179
135
|
parseStack(layer.handle.stack, detectedEndpoints, prefix + routerPrefix);
|
|
180
136
|
}
|
|
181
137
|
}
|
|
182
138
|
}
|
|
183
139
|
|
|
184
|
-
//
|
|
185
|
-
function endtesterExpress() {
|
|
186
|
-
return function monkeyTesterMiddleware(req, res, next) {
|
|
187
|
-
// Normalize path: strip trailing slash, handle both req.path and req.url
|
|
188
|
-
const rawPath = (req.path || req.url || '').split('?')[0].replace(/\/+$/, '');
|
|
140
|
+
// function endtesterExpress() { ... }
|
|
189
141
|
|
|
190
|
-
|
|
142
|
+
|
|
143
|
+
// Change it to this instead:
|
|
144
|
+
export function endtesterExpress() {
|
|
145
|
+
return function monkeyTesterMiddleware(req, res, next) {
|
|
146
|
+
if (req.path !== '/api/tester' && req.path !== '/api/tester/') {
|
|
191
147
|
return next();
|
|
192
148
|
}
|
|
193
149
|
|
|
194
150
|
const app = req.app;
|
|
195
|
-
|
|
196
|
-
// Wait a tick to ensure all routes are registered before scanning
|
|
197
|
-
// (handles edge cases where middleware is mounted before some routes)
|
|
198
151
|
const detectedEndpoints = {};
|
|
199
152
|
|
|
200
153
|
const rootStack =
|
|
201
|
-
(app._router && app._router.stack) ||
|
|
202
|
-
(app.router && app.router.stack) ||
|
|
154
|
+
(app._router && app._router.stack) ||
|
|
155
|
+
(app.router && app.router.stack) ||
|
|
203
156
|
[];
|
|
204
157
|
|
|
205
158
|
parseStack(rootStack, detectedEndpoints);
|
|
@@ -210,4 +163,4 @@ function endtesterExpress() {
|
|
|
210
163
|
};
|
|
211
164
|
}
|
|
212
165
|
|
|
213
|
-
export { endtesterExpress };
|
|
166
|
+
// export { endtesterExpress };
|
package/monkey.js
CHANGED
|
@@ -1,98 +1,59 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { UI } from './htmlTemplate.js';
|
|
4
4
|
|
|
5
5
|
function inferType(name) {
|
|
6
6
|
const n = name.toLowerCase();
|
|
7
|
-
if (n.includes('email'))
|
|
8
|
-
if (n.includes('password') || n.includes('pass'))
|
|
9
|
-
if (n.includes('date')
|
|
10
|
-
if (n.includes('
|
|
11
|
-
if (n.includes('url') || n.includes('website') || n.includes('link')) return 'url';
|
|
12
|
-
if (
|
|
13
|
-
n.includes('age') || n.includes('price') || n.includes('amount') ||
|
|
14
|
-
n.includes('count') || n.includes('qty') || n.includes('quantity') ||
|
|
15
|
-
n.includes('stock') || n.includes('salary') || n.includes('total') ||
|
|
16
|
-
(n === 'id') || n.endsWith('_id') || n.endsWith('Id')
|
|
17
|
-
) return 'number';
|
|
7
|
+
if (n.includes('email')) return 'email';
|
|
8
|
+
if (n.includes('password') || n.includes('pass')) return 'password';
|
|
9
|
+
if (n.includes('date')) return 'date';
|
|
10
|
+
if (n.includes('age') || n.includes('price') || n.includes('quantity') || n.includes('stock') || n === 'id' || n.endsWith('id')) return 'number';
|
|
18
11
|
return 'text';
|
|
19
12
|
}
|
|
20
13
|
|
|
21
14
|
function buildField(name) {
|
|
22
|
-
return {
|
|
23
|
-
name,
|
|
24
|
-
label: name.charAt(0).toUpperCase() + name.slice(1).replace(/([A-Z])/g, ' $1'),
|
|
25
|
-
type: inferType(name),
|
|
26
|
-
placeholder: `Enter ${name}`
|
|
27
|
-
};
|
|
15
|
+
return { name, label: name.charAt(0).toUpperCase() + name.slice(1).replace(/([A-Z])/g, ' $1'), type: inferType(name), placeholder: `Enter ${name}` };
|
|
28
16
|
}
|
|
29
17
|
|
|
30
18
|
function extractBodyFields(handler) {
|
|
31
19
|
try {
|
|
32
20
|
const source = handler.toString();
|
|
33
21
|
if (!source || source.includes('[native code]')) return [];
|
|
34
|
-
|
|
35
22
|
const seen = new Map();
|
|
36
23
|
const destructRe = /(?:const|let|var)\s*\{\s*([^}]+)\s*\}\s*=\s*req\.body/g;
|
|
37
24
|
let m;
|
|
38
25
|
while ((m = destructRe.exec(source)) !== null) {
|
|
39
26
|
m[1].split(',').forEach(part => {
|
|
40
27
|
const name = part.split(':')[0].split('=')[0].trim();
|
|
41
|
-
if (name && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) && !seen.has(name))
|
|
42
|
-
seen.set(name, buildField(name));
|
|
43
|
-
}
|
|
28
|
+
if (name && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) && !seen.has(name)) seen.set(name, buildField(name));
|
|
44
29
|
});
|
|
45
30
|
}
|
|
46
|
-
|
|
47
|
-
const accessRe = /req\.body\.([a-zA-Z_$][a-zA-Z0-9_$]*)|req\.body\[['"]([a-zA-Z_$][a-zA-Z0-9_$]*)['"]]/g;
|
|
31
|
+
const accessRe = /req\.body\.([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
|
|
48
32
|
while ((m = accessRe.exec(source)) !== null) {
|
|
49
|
-
const name = m[1]
|
|
50
|
-
if (name && !seen.has(name)) seen.set(name, buildField(name));
|
|
33
|
+
const name = m[1]; if (name && !seen.has(name)) seen.set(name, buildField(name));
|
|
51
34
|
}
|
|
52
|
-
|
|
53
35
|
return Array.from(seen.values());
|
|
54
|
-
} catch {
|
|
55
|
-
return [];
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function fallbackFields(path) {
|
|
60
|
-
const p = path.toLowerCase();
|
|
61
|
-
if (p.includes('login') || p.includes('signin') || p.includes('auth/login')) return ['email', 'password'].map(buildField);
|
|
62
|
-
if (p.includes('register') || p.includes('signup') || p.includes('auth/register')) return ['username', 'email', 'password'].map(buildField);
|
|
63
|
-
if (p.includes('user')) return ['username', 'email', 'password'].map(buildField);
|
|
64
|
-
if (p.includes('product')) return ['name', 'price', 'stock'].map(buildField);
|
|
65
|
-
if (p.includes('order')) return ['productId', 'quantity', 'address'].map(buildField);
|
|
66
|
-
return [];
|
|
36
|
+
} catch { return []; }
|
|
67
37
|
}
|
|
68
38
|
|
|
69
39
|
function extractRouterPrefix(layer) {
|
|
70
40
|
if (!layer.regexp) return '';
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
for (const re of patterns) {
|
|
74
|
-
const m = re.exec(src);
|
|
75
|
-
if (m && m[1]) return '/' + m[1].replace(/\\\//g, '/').replace(/\\/g, '');
|
|
76
|
-
}
|
|
77
|
-
return '';
|
|
41
|
+
const m = [/^\^\\\/([^\\?$]+)/, /^\^\\\/([a-zA-Z0-9_/-]+)/].reduce((acc, re) => acc || re.exec(layer.regexp.source), null);
|
|
42
|
+
return m && m[1] ? '/' + m[1].replace(/\\\//g, '/').replace(/\\/g, '') : '';
|
|
78
43
|
}
|
|
79
44
|
|
|
80
45
|
function parseStack(stack, detectedEndpoints, prefix = '') {
|
|
81
46
|
if (!Array.isArray(stack)) return;
|
|
82
|
-
|
|
83
47
|
for (const layer of stack) {
|
|
84
48
|
if (layer.route) {
|
|
85
49
|
const rawPath = typeof layer.route.path === 'string' ? layer.route.path : (layer.route.path ? String(layer.route.path) : '');
|
|
86
50
|
const fullPath = (prefix + rawPath).replace(/\/+/g, '/') || '/';
|
|
87
|
-
|
|
88
51
|
if (fullPath.startsWith('/api/tester')) continue;
|
|
89
52
|
|
|
90
53
|
const methods = Object.keys(layer.route.methods || {});
|
|
91
|
-
|
|
92
54
|
for (const method of methods) {
|
|
93
55
|
const httpMethod = method.toUpperCase();
|
|
94
56
|
const key = `${httpMethod}::${fullPath}`;
|
|
95
|
-
|
|
96
57
|
const pathParams = [];
|
|
97
58
|
const paramRe = /:([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
|
|
98
59
|
let pm;
|
|
@@ -101,66 +62,43 @@ function parseStack(stack, detectedEndpoints, prefix = '') {
|
|
|
101
62
|
}
|
|
102
63
|
|
|
103
64
|
let bodyFields = [];
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const seen = new Map();
|
|
112
|
-
bodyFields = bodyFields.filter(f => {
|
|
113
|
-
if (seen.has(f.name)) return false;
|
|
114
|
-
seen.set(f.name, true);
|
|
115
|
-
return true;
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// CHANGE THIS: Only apply generic fallbacks if there are no explicit path parameters
|
|
119
|
-
if (bodyFields.length === 0 && pathParams.length === 0) {
|
|
120
|
-
bodyFields = fallbackFields(fullPath);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
65
|
+
if (['POST', 'PUT', 'PATCH'].includes(httpMethod)) {
|
|
66
|
+
const handlers = (layer.route.stack || []).map(sl => sl.handle).filter(Boolean);
|
|
67
|
+
for (const handler of handlers) bodyFields.push(...extractBodyFields(handler));
|
|
68
|
+
const seen = new Map();
|
|
69
|
+
bodyFields = bodyFields.filter(f => !seen.has(f.name) && seen.set(f.name, true));
|
|
70
|
+
}
|
|
123
71
|
|
|
124
|
-
detectedEndpoints[key] = {
|
|
125
|
-
method: httpMethod,
|
|
126
|
-
path: fullPath,
|
|
127
|
-
title: `${httpMethod} ${fullPath}`,
|
|
128
|
-
desc: `Auto-discovered endpoint - ${fullPath}`, // Safe ASCII character
|
|
129
|
-
params: pathParams,
|
|
130
|
-
fields: bodyFields,
|
|
131
|
-
};
|
|
72
|
+
detectedEndpoints[key] = { method: httpMethod, path: fullPath, title: `${httpMethod} ${fullPath}`, desc: `Auto-discovered endpoint - ${fullPath}`, params: pathParams, fields: bodyFields };
|
|
132
73
|
}
|
|
133
74
|
} else if (layer.name === 'router' && layer.handle && layer.handle.stack) {
|
|
134
|
-
|
|
135
|
-
parseStack(layer.handle.stack, detectedEndpoints, prefix + routerPrefix);
|
|
75
|
+
parseStack(layer.handle.stack, detectedEndpoints, prefix + extractRouterPrefix(layer));
|
|
136
76
|
}
|
|
137
77
|
}
|
|
138
78
|
}
|
|
139
79
|
|
|
140
|
-
// function endtesterExpress() { ... }
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// Change it to this instead:
|
|
144
80
|
export function endtesterExpress() {
|
|
145
81
|
return function monkeyTesterMiddleware(req, res, next) {
|
|
146
|
-
|
|
147
|
-
|
|
82
|
+
const route = req.path.toLowerCase().replace(/\/$/, '');
|
|
83
|
+
|
|
84
|
+
// 1. Serve Standalone Frontend Templates Interceptions
|
|
85
|
+
if (route === '/login') return res.send(UI.login());
|
|
86
|
+
if (route === '/signup') return res.send(UI.signup());
|
|
87
|
+
if (route === '/dashboard') return res.send(UI.dashboard());
|
|
88
|
+
|
|
89
|
+
// 2. Serve the main Tester UI environment
|
|
90
|
+
if (route === '/api/tester') {
|
|
91
|
+
const app = req.app;
|
|
92
|
+
const detectedEndpoints = {};
|
|
93
|
+
const rootStack = (app._router && app._router.stack) || (app.router && app.router.stack) || [];
|
|
94
|
+
parseStack(rootStack, detectedEndpoints);
|
|
95
|
+
const b64 = Buffer.from(JSON.stringify(detectedEndpoints)).toString('base64');
|
|
96
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
97
|
+
return res.send(UI.tester(b64));
|
|
148
98
|
}
|
|
149
99
|
|
|
150
|
-
|
|
151
|
-
const detectedEndpoints = {};
|
|
152
|
-
|
|
153
|
-
const rootStack =
|
|
154
|
-
(app._router && app._router.stack) ||
|
|
155
|
-
(app.router && app.router.stack) ||
|
|
156
|
-
[];
|
|
157
|
-
|
|
158
|
-
parseStack(rootStack, detectedEndpoints);
|
|
159
|
-
|
|
160
|
-
const html = getHtmlTemplate(detectedEndpoints);
|
|
161
|
-
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
162
|
-
return res.send(html);
|
|
100
|
+
next();
|
|
163
101
|
};
|
|
164
102
|
}
|
|
165
103
|
|
|
166
|
-
|
|
104
|
+
export default { endtesterExpress };
|