@aimeloic/monkey-tester 3.0.4 → 3.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/monkey.js +33 -26
- package/package.json +1 -1
package/monkey.js
CHANGED
|
@@ -85,20 +85,27 @@ function fallbackFields(path) {
|
|
|
85
85
|
|
|
86
86
|
// ─── Extract router prefix safely from Express layer ─────────────────────────
|
|
87
87
|
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
|
+
|
|
88
93
|
if (!layer.regexp) return '';
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
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
|
+
const src = layer.regexp.source;
|
|
98
|
+
|
|
99
|
+
// Extract the literal path segment before any optional/lookahead parts
|
|
100
|
+
// Match from start: ^\/ then literal segments
|
|
101
|
+
const match = src.match(/^\^((?:\\\/[^\\(?[*+{}|$^]+)+)/);
|
|
102
|
+
if (!match) return '';
|
|
103
|
+
|
|
104
|
+
// Unescape the extracted path
|
|
105
|
+
const raw = match[1].replace(/\\\//g, '/');
|
|
106
|
+
|
|
107
|
+
// Remove trailing slash if present
|
|
108
|
+
return raw.replace(/\/$/, '') || '';
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
// ─── Walk the Express router stack recursively ────────────────────────────────
|
|
@@ -114,6 +121,7 @@ function parseStack(stack, detectedEndpoints, prefix = '') {
|
|
|
114
121
|
|
|
115
122
|
const fullPath = (prefix + rawPath).replace(/\/+/g, '/') || '/';
|
|
116
123
|
|
|
124
|
+
// Skip the tester route itself
|
|
117
125
|
if (fullPath.startsWith('/api/tester')) continue;
|
|
118
126
|
|
|
119
127
|
const methods = Object.keys(layer.route.methods || {});
|
|
@@ -122,7 +130,7 @@ function parseStack(stack, detectedEndpoints, prefix = '') {
|
|
|
122
130
|
const httpMethod = method.toUpperCase();
|
|
123
131
|
const key = `${httpMethod}::${fullPath}`;
|
|
124
132
|
|
|
125
|
-
// ── Path params (:id, :slug …)
|
|
133
|
+
// ── Path params (:id, :slug …) ────────────────────────────────────
|
|
126
134
|
const pathParams = [];
|
|
127
135
|
const paramRe = /:([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
|
|
128
136
|
const matches = [...fullPath.matchAll(paramRe)];
|
|
@@ -135,13 +143,14 @@ function parseStack(stack, detectedEndpoints, prefix = '') {
|
|
|
135
143
|
});
|
|
136
144
|
}
|
|
137
145
|
|
|
138
|
-
// ── Body fields
|
|
146
|
+
// ── Body fields ──────────────────────────────────────────────────
|
|
139
147
|
let bodyFields = [];
|
|
140
148
|
if (['POST', 'PUT', 'PATCH'].includes(httpMethod)) {
|
|
141
149
|
const handlers = (layer.route.stack || []).map(sl => sl.handle).filter(Boolean);
|
|
142
150
|
for (const handler of handlers) {
|
|
143
151
|
bodyFields.push(...extractBodyFields(handler));
|
|
144
152
|
}
|
|
153
|
+
// Deduplicate
|
|
145
154
|
const seen = new Map();
|
|
146
155
|
bodyFields = bodyFields.filter(f => {
|
|
147
156
|
if (seen.has(f.name)) return false;
|
|
@@ -165,7 +174,7 @@ function parseStack(stack, detectedEndpoints, prefix = '') {
|
|
|
165
174
|
}
|
|
166
175
|
|
|
167
176
|
// ── Nested router (app.use('/prefix', router)) ───────────────────────────
|
|
168
|
-
else if (layer.
|
|
177
|
+
else if (layer.handle && typeof layer.handle === 'function' && layer.handle.stack) {
|
|
169
178
|
const routerPrefix = extractRouterPrefix(layer);
|
|
170
179
|
parseStack(layer.handle.stack, detectedEndpoints, prefix + routerPrefix);
|
|
171
180
|
}
|
|
@@ -173,19 +182,19 @@ function parseStack(stack, detectedEndpoints, prefix = '') {
|
|
|
173
182
|
}
|
|
174
183
|
|
|
175
184
|
// ─── Middleware ───────────────────────────────────────────────────────────────
|
|
176
|
-
// ─── Middleware in monkey.js ──────────────────────────────────────────────────
|
|
177
185
|
function endtesterExpress() {
|
|
178
186
|
return function monkeyTesterMiddleware(req, res, next) {
|
|
179
|
-
//
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
if (
|
|
187
|
+
// Normalize path: strip trailing slash, handle both req.path and req.url
|
|
188
|
+
const rawPath = (req.path || req.url || '').split('?')[0].replace(/\/+$/, '');
|
|
189
|
+
|
|
190
|
+
if (rawPath !== '/api/tester') {
|
|
183
191
|
return next();
|
|
184
192
|
}
|
|
185
193
|
|
|
186
194
|
const app = req.app;
|
|
187
|
-
|
|
188
|
-
//
|
|
195
|
+
|
|
196
|
+
// Wait a tick to ensure all routes are registered before scanning
|
|
197
|
+
// (handles edge cases where middleware is mounted before some routes)
|
|
189
198
|
const detectedEndpoints = {};
|
|
190
199
|
|
|
191
200
|
const rootStack =
|
|
@@ -193,12 +202,10 @@ function endtesterExpress() {
|
|
|
193
202
|
(app.router && app.router.stack) || // Express 5
|
|
194
203
|
[];
|
|
195
204
|
|
|
196
|
-
// Scan the live compiled routing stack
|
|
197
205
|
parseStack(rootStack, detectedEndpoints);
|
|
198
206
|
|
|
199
|
-
// Render page template
|
|
200
207
|
const html = getHtmlTemplate(detectedEndpoints);
|
|
201
|
-
res.setHeader('Content-Type', 'text/html');
|
|
208
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
202
209
|
return res.send(html);
|
|
203
210
|
};
|
|
204
211
|
}
|