@contrast/route-coverage 1.51.0 → 1.53.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/lib/index.d.ts +2 -0
- package/lib/index.js +5 -4
- package/lib/install/express.js +535 -0
- package/lib/install/fastify/fastify-express.js +71 -0
- package/lib/install/fastify/fastify-middie.js +67 -0
- package/lib/install/{fastify.js → fastify/fastify.js} +12 -12
- package/lib/install/{express → fastify}/index.js +6 -5
- package/lib/install/hapi.js +126 -63
- package/lib/install/koa.js +39 -45
- package/lib/install/restify.js +226 -44
- package/lib/utils/route-info.js +26 -1
- package/package.json +8 -8
- package/lib/install/express/express4.js +0 -157
- package/lib/install/express/express5.js +0 -553
package/lib/install/restify.js
CHANGED
|
@@ -14,58 +14,240 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
const {
|
|
18
|
-
const {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
const { AsyncLocalStorage } = require('async_hooks');
|
|
18
|
+
const {
|
|
19
|
+
isString,
|
|
20
|
+
primordials: {
|
|
21
|
+
StringPrototypeToLowerCase,
|
|
22
|
+
StringPrototypeSplit,
|
|
23
|
+
StringPrototypeSubstring,
|
|
24
|
+
},
|
|
25
|
+
set,
|
|
26
|
+
RouteType,
|
|
27
|
+
} = require('@contrast/common');
|
|
28
|
+
const { Core } = require('@contrast/core/lib/ioc/core');
|
|
29
|
+
const { formatHandler, patchType } = require('../utils/route-info');
|
|
30
|
+
|
|
31
|
+
const COMPONENT_NAME = 'routeCoverage.restify';
|
|
32
|
+
const FRAMEWORK = 'restify';
|
|
33
|
+
|
|
34
|
+
module.exports = Core.makeComponent({
|
|
35
|
+
name: COMPONENT_NAME,
|
|
36
|
+
factory: (core) => new RestifyInstrumentation(core),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
class RestifyInstrumentation {
|
|
40
|
+
constructor(core) {
|
|
41
|
+
set(core, COMPONENT_NAME, this);
|
|
42
|
+
Object.defineProperty(this, 'core', { value: core });
|
|
43
|
+
this.routeScope = new AsyncLocalStorage();
|
|
44
|
+
this.conditionalHandlers = new WeakMap();
|
|
35
45
|
}
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
formatHandler(fn) {
|
|
48
|
+
return formatHandler(this.core.patcher.unwrap(fn));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
install() {
|
|
52
|
+
const self = this;
|
|
53
|
+
const { depHooks } = this.core;
|
|
54
|
+
|
|
55
|
+
depHooks.resolve({ name: 'restify', version: '>=10 <12' }, (restify, pkgMeta) => {
|
|
56
|
+
self.patchPlugins(restify, pkgMeta);
|
|
57
|
+
self.patchServer(restify, pkgMeta);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
patchPlugins(restify) {
|
|
62
|
+
const self = this;
|
|
63
|
+
const { patcher, routeCoverage } = this.core;
|
|
64
|
+
if (!restify.plugins?.conditionalHandler) return;
|
|
65
|
+
|
|
66
|
+
const name = 'restify.plugins.conditionalHandler';
|
|
67
|
+
patcher.patch(restify.plugins, 'conditionalHandler', {
|
|
68
|
+
name,
|
|
69
|
+
patchType,
|
|
70
|
+
around(next, data) {
|
|
71
|
+
const { args } = data;
|
|
72
|
+
const conditionals = Array.isArray(args[0]) ? args[0] : [args[0]];
|
|
73
|
+
const formattedHandlers = [];
|
|
74
|
+
|
|
75
|
+
// we have to do this before calling next() to get return value
|
|
76
|
+
// since restify potentially alters conditional.handlers
|
|
77
|
+
for (const conditional of conditionals) {
|
|
78
|
+
const isHandlerArr = Array.isArray(conditional.handler);
|
|
79
|
+
const target = isHandlerArr ? conditional.handler : conditional;
|
|
80
|
+
const propsIter = isHandlerArr ? Object.keys(conditional.handler) : ['handler'];
|
|
81
|
+
|
|
82
|
+
for (const propName of propsIter) {
|
|
83
|
+
if (typeof target[propName] !== 'function') continue;
|
|
84
|
+
|
|
85
|
+
const formattedHandler = self.formatHandler(target[propName]);
|
|
86
|
+
formattedHandlers.push(formattedHandler);
|
|
87
|
+
|
|
88
|
+
patcher.patch(target, propName, {
|
|
89
|
+
name: 'restify.plugins.conditionalHandler',
|
|
46
90
|
patchType,
|
|
47
|
-
|
|
48
|
-
const {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
91
|
+
pre(data) {
|
|
92
|
+
const { args: [req] } = data;
|
|
93
|
+
const store = self.routeScope.getStore();
|
|
94
|
+
if (!store) return;
|
|
95
|
+
|
|
96
|
+
for (const routeInfo of store.observables) {
|
|
97
|
+
if (routeInfo.signature.indexOf(formattedHandler) >= 0) {
|
|
98
|
+
routeCoverage.observe({
|
|
99
|
+
...routeInfo,
|
|
100
|
+
method: StringPrototypeToLowerCase.call(req.method),
|
|
101
|
+
url: StringPrototypeSplit.call(req.url, '?')[0],
|
|
102
|
+
});
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const result = next();
|
|
112
|
+
|
|
113
|
+
// save list of handlers that have been registered under the returned
|
|
114
|
+
// consolidated one. when this return value gets mounted at an actual
|
|
115
|
+
// path(s), we can lookup the handlers and discover each individually
|
|
116
|
+
// with that additional route info.
|
|
117
|
+
self.conditionalHandlers.set(result, formattedHandlers);
|
|
118
|
+
|
|
119
|
+
return result;
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
patchServer(restify, pkgMeta) {
|
|
125
|
+
const self = this;
|
|
126
|
+
const { logger, patcher, routeCoverage } = this.core;
|
|
127
|
+
|
|
128
|
+
patcher.patch(restify, 'createServer', {
|
|
129
|
+
name: 'restify.createServer',
|
|
130
|
+
patchType,
|
|
131
|
+
post({ result: server }) {
|
|
132
|
+
patcher.patch(server.router, 'mount', {
|
|
133
|
+
name: 'restify.router.mount',
|
|
134
|
+
patchType,
|
|
135
|
+
post({ result: route }) {
|
|
136
|
+
if (!route.path || !route.method || !isString(route.path)) {
|
|
137
|
+
logger.error({ route }, 'unable to process restify route');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const { path } = route;
|
|
142
|
+
const method = StringPrototypeToLowerCase.call(route.method);
|
|
143
|
+
const baseInfo = {
|
|
144
|
+
method,
|
|
145
|
+
url: path,
|
|
146
|
+
normalizedUrl: path,
|
|
147
|
+
framework: FRAMEWORK,
|
|
148
|
+
type: RouteType.HTTP,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
for (let idx = 0; idx < route.chain._stack.length; idx++) {
|
|
152
|
+
const handler = route.chain._stack[idx];
|
|
153
|
+
const routeInfo = {
|
|
154
|
+
...baseInfo,
|
|
155
|
+
signature: `server.${method}(${path}, ${self.formatHandler(handler)})`,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
if (!self.conditionalHandlers.has(handler)) {
|
|
159
|
+
// "regular" handlers
|
|
160
|
+
routeCoverage.discover(routeInfo);
|
|
161
|
+
route.chain._stack[idx] = patcher.patch(route.chain._stack[idx], {
|
|
162
|
+
name: 'restify.route.chain._stack',
|
|
56
163
|
patchType,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
164
|
+
pre({ args: [req] }) {
|
|
165
|
+
routeCoverage.observe({
|
|
166
|
+
...routeInfo,
|
|
167
|
+
method: StringPrototypeToLowerCase.call(req.method),
|
|
168
|
+
url: StringPrototypeSplit.call(req.url, '?')[0],
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
} else {
|
|
173
|
+
// "conditional" handlers dispatch to their registered handlers
|
|
174
|
+
const formattedHandlers = self.conditionalHandlers.get(handler);
|
|
175
|
+
const store = { observables: [] };
|
|
176
|
+
|
|
177
|
+
for (const formattedHandler of formattedHandlers) {
|
|
178
|
+
const routeInfo = {
|
|
179
|
+
...baseInfo,
|
|
180
|
+
signature: `server.${method}(${path}, ${formattedHandler})`,
|
|
181
|
+
};
|
|
182
|
+
routeCoverage.discover(routeInfo);
|
|
183
|
+
store.observables.push(routeInfo);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
route.chain._stack[idx] = patcher.patch(route.chain._stack[idx], {
|
|
187
|
+
name: 'restify.route.chain._stack',
|
|
188
|
+
patchType,
|
|
189
|
+
around(next) {
|
|
190
|
+
return self.routeScope.run(store, next);
|
|
62
191
|
}
|
|
63
192
|
});
|
|
64
193
|
}
|
|
65
|
-
}
|
|
194
|
+
}
|
|
66
195
|
}
|
|
67
196
|
});
|
|
197
|
+
|
|
198
|
+
self.patchMiddlewareChains(server, pkgMeta);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
patchMiddlewareChains(server) {
|
|
204
|
+
const self = this;
|
|
205
|
+
const { config, routeCoverage, patcher } = this.core;
|
|
206
|
+
|
|
207
|
+
if (!config.getEffectiveValue('assess.report_middleware_routes')) return;
|
|
208
|
+
|
|
209
|
+
for (const propName of ['preChain', 'useChain']) {
|
|
210
|
+
patcher.patch(server[propName], 'add', {
|
|
211
|
+
name: `restify.server.${propName}.add`,
|
|
212
|
+
patchType,
|
|
213
|
+
around(next, data) {
|
|
214
|
+
const len = data.obj._stack.length;
|
|
215
|
+
const ret = next();
|
|
216
|
+
|
|
217
|
+
if (data.obj._stack.length > len) {
|
|
218
|
+
const method = StringPrototypeSubstring.call(propName, 0, 3);
|
|
219
|
+
const baseData = {
|
|
220
|
+
method,
|
|
221
|
+
url: '/',
|
|
222
|
+
normalizedUrl: '/',
|
|
223
|
+
type: RouteType.MIDDLEWARE,
|
|
224
|
+
framework: FRAMEWORK,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
for (let idx = 0; idx < data.obj._stack.length; idx++) {
|
|
228
|
+
const routeData = {
|
|
229
|
+
...baseData,
|
|
230
|
+
signature: `server.${method}(${self.formatHandler(data.obj._stack[idx])})`,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
routeCoverage.discover(routeData);
|
|
234
|
+
patcher.patch(data.obj._stack, idx, {
|
|
235
|
+
name: `restify.server.${propName}`,
|
|
236
|
+
patchType,
|
|
237
|
+
pre({ args: [req] }) {
|
|
238
|
+
routeCoverage.observe({
|
|
239
|
+
...routeData,
|
|
240
|
+
method: StringPrototypeToLowerCase.call(req.method),
|
|
241
|
+
url: StringPrototypeSplit.call(req.url, '?')[0],
|
|
242
|
+
});
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return ret;
|
|
248
|
+
}
|
|
68
249
|
});
|
|
69
250
|
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
package/lib/utils/route-info.js
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
17
|
const patchType = 'route-coverage';
|
|
18
|
+
const { funcInfo } = require('@contrast/fn-inspect');
|
|
19
|
+
const { primordials: { StringPrototypeReplace, StringPrototypeSubstring } } = require('@contrast/common');
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Creates a formatted "signature" for a route
|
|
@@ -28,4 +30,27 @@ function createSignature(path, method = '', obj = 'Router', handler = '[Function
|
|
|
28
30
|
return `${obj}.${method}('${path}', ${handler})`;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Creates a formatted handler signature for a route
|
|
35
|
+
* @param {function} handler
|
|
36
|
+
* @param {string=} appDir
|
|
37
|
+
* @return {string} formatted handler
|
|
38
|
+
*/
|
|
39
|
+
function formatHandler(handler, appDir) {
|
|
40
|
+
const info = funcInfo(handler);
|
|
41
|
+
if (!info) return '[Function]';
|
|
42
|
+
|
|
43
|
+
let file = info.file ?
|
|
44
|
+
StringPrototypeReplace.call(info.file, appDir, '') :
|
|
45
|
+
'';
|
|
46
|
+
if (file.length > 30) {
|
|
47
|
+
file = `...${StringPrototypeSubstring.call(file, file.length - 40)}`;
|
|
48
|
+
}
|
|
49
|
+
const handlerName = info.method || handler.name || 'anonymous';
|
|
50
|
+
const formattedHandler = (file && Number.isFinite(info.lineNumber) && Number.isFinite(info.column)) ?
|
|
51
|
+
`[${handlerName} ${file} ${info.lineNumber}:${info.column}]` :
|
|
52
|
+
`[Function: ${handlerName}]`; // what util.inspect(handler) would return
|
|
53
|
+
return formattedHandler;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { createSignature, patchType, formatHandler };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/route-coverage",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.53.0",
|
|
4
4
|
"description": "Handles route discovery and observation",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
"test": "bash ../scripts/test.sh"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@contrast/common": "1.
|
|
24
|
-
"@contrast/config": "1.
|
|
25
|
-
"@contrast/core": "1.
|
|
26
|
-
"@contrast/dep-hooks": "1.
|
|
23
|
+
"@contrast/common": "1.39.0",
|
|
24
|
+
"@contrast/config": "1.55.0",
|
|
25
|
+
"@contrast/core": "1.60.0",
|
|
26
|
+
"@contrast/dep-hooks": "1.29.0",
|
|
27
27
|
"@contrast/fn-inspect": "^5.0.2",
|
|
28
|
-
"@contrast/logger": "1.
|
|
29
|
-
"@contrast/patcher": "1.
|
|
30
|
-
"@contrast/scopes": "1.
|
|
28
|
+
"@contrast/logger": "1.33.0",
|
|
29
|
+
"@contrast/patcher": "1.32.0",
|
|
30
|
+
"@contrast/scopes": "1.30.0",
|
|
31
31
|
"semver": "^7.6.0"
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright: 2025 Contrast Security, Inc
|
|
3
|
-
* Contact: support@contrastsecurity.com
|
|
4
|
-
* License: Commercial
|
|
5
|
-
|
|
6
|
-
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
-
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
-
* made available through public repositories, use of this Software is subject to
|
|
9
|
-
* the applicable End User Licensing Agreement found at
|
|
10
|
-
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
-
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
-
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
-
* way not consistent with the End User License Agreement.
|
|
14
|
-
*/
|
|
15
|
-
'use strict';
|
|
16
|
-
|
|
17
|
-
const METHODS = [
|
|
18
|
-
'all',
|
|
19
|
-
'get',
|
|
20
|
-
'post',
|
|
21
|
-
'put',
|
|
22
|
-
'delete',
|
|
23
|
-
'patch',
|
|
24
|
-
'options',
|
|
25
|
-
'head',
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
const fnInspect = require('@contrast/fn-inspect');
|
|
29
|
-
const { createSignature, patchType } = require('../../utils/route-info');
|
|
30
|
-
const { isString, primordials: { ArrayPrototypeJoin, StringPrototypeToLowerCase, StringPrototypeReplace, StringPrototypeReplaceAll, StringPrototypeSplit, StringPrototypeSlice } } = require('@contrast/common');
|
|
31
|
-
|
|
32
|
-
// Spec: https://contrast.atlassian.net/wiki/spaces/NOD/pages/3454861621/Node.js+Agent+Route+Signatures#Express
|
|
33
|
-
module.exports = function init(core) {
|
|
34
|
-
const { patcher, depHooks, routeCoverage } = core;
|
|
35
|
-
const discover = (route) => routeCoverage.discover(route);
|
|
36
|
-
const observe = (route) => routeCoverage.observe(route);
|
|
37
|
-
|
|
38
|
-
const isRoute = (layer) => !!layer.route;
|
|
39
|
-
const isRouter = (layer) => layer.name && StringPrototypeToLowerCase.call(layer.name) === 'router';
|
|
40
|
-
const isValidPath = (path) => isString(path) || Array.isArray(path) || path instanceof RegExp;
|
|
41
|
-
const getHandleMethod = (layer) => fnInspect.funcInfo(layer.__handle)?.file.includes('express-async-errors') ? '__handle' : 'handle';
|
|
42
|
-
const getLastLayer = (router) => router?.stack[router.stack.length - 1];
|
|
43
|
-
|
|
44
|
-
function regExpToPath(regex) {
|
|
45
|
-
if (regex.source) {
|
|
46
|
-
let [path] = StringPrototypeSplit.call(regex?.source, '/?');
|
|
47
|
-
path = StringPrototypeReplaceAll.call(path, '\\', '');
|
|
48
|
-
path = StringPrototypeReplace.call(path, '^', '');
|
|
49
|
-
return path;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function format(url) {
|
|
54
|
-
if (Array.isArray(url)) {
|
|
55
|
-
return `/[${ArrayPrototypeJoin.call(url)}]`;
|
|
56
|
-
} else if (url instanceof RegExp) {
|
|
57
|
-
return `/{${StringPrototypeSlice.call(url.toString(), 1, -1)}}`;
|
|
58
|
-
} else {
|
|
59
|
-
return url;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function parseRoute(route) {
|
|
64
|
-
const { path } = route;
|
|
65
|
-
const method = route.methods._all ? 'all' : route.stack[0].method;
|
|
66
|
-
return { url: format(path), method };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
function createRouteInfo(url, method, obj) {
|
|
71
|
-
return {
|
|
72
|
-
signature: createSignature(url, method, obj),
|
|
73
|
-
url,
|
|
74
|
-
normalizedUrl: url,
|
|
75
|
-
method,
|
|
76
|
-
framework: 'express'
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function patchHandle(layer, routeInfo) {
|
|
81
|
-
const handle = getHandleMethod(layer);
|
|
82
|
-
patcher.patch(layer, handle, {
|
|
83
|
-
name: 'express.Route.handle',
|
|
84
|
-
patchType,
|
|
85
|
-
post({ args }) {
|
|
86
|
-
const [req] = args;
|
|
87
|
-
const [url] = StringPrototypeSplit.call(req.originalUrl, '?');
|
|
88
|
-
const { method } = req;
|
|
89
|
-
if (url && method) {
|
|
90
|
-
observe({ ...routeInfo, url, method: StringPrototypeToLowerCase.call(method) });
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function traverse(stack, path = '', depth = 0) {
|
|
97
|
-
path = format(path);
|
|
98
|
-
stack.forEach((layer) => {
|
|
99
|
-
if (isRoute(layer)) {
|
|
100
|
-
const { url, method } = parseRoute(layer.route);
|
|
101
|
-
const routeInfo = createRouteInfo(path + url, method);
|
|
102
|
-
discover(routeInfo);
|
|
103
|
-
patchHandle(layer, routeInfo);
|
|
104
|
-
} else if (isRouter(layer)) {
|
|
105
|
-
const regexPath = regExpToPath(layer.regexp);
|
|
106
|
-
if (depth < 3) traverse(layer.handle.stack, path + regexPath, depth += 1);
|
|
107
|
-
} else {
|
|
108
|
-
const regexPath = regExpToPath(layer.regexp);
|
|
109
|
-
const routeInfo = createRouteInfo(path + regexPath, 'use');
|
|
110
|
-
discover(routeInfo);
|
|
111
|
-
patchHandle(layer, routeInfo);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
return core.routeCoverage.express4 = {
|
|
116
|
-
install() {
|
|
117
|
-
depHooks.resolve({ name: 'express', version: '>=4 <5' }, (express) => {
|
|
118
|
-
patcher.patch(express.application, 'use', {
|
|
119
|
-
name: 'express.application.use',
|
|
120
|
-
patchType,
|
|
121
|
-
post({ args, result }) {
|
|
122
|
-
const len = args.length;
|
|
123
|
-
const fn = args[len - 1];
|
|
124
|
-
const path = len > 1 ? args[0] : undefined;
|
|
125
|
-
if (path && !isValidPath(path)) return;
|
|
126
|
-
const handlers = Array.isArray(fn) ? fn : [fn];
|
|
127
|
-
handlers.forEach((layer) => {
|
|
128
|
-
if (isRouter(layer)) {
|
|
129
|
-
traverse(layer.stack, path);
|
|
130
|
-
} else if (path) {
|
|
131
|
-
const routeInfo = createRouteInfo(format(path), 'use', 'App');
|
|
132
|
-
discover(routeInfo);
|
|
133
|
-
const lastLayer = getLastLayer(result._router);
|
|
134
|
-
if (lastLayer) patchHandle(lastLayer, routeInfo);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
METHODS.forEach((method) => {
|
|
141
|
-
patcher.patch(express.application, method, {
|
|
142
|
-
name: `express.application.${method}`,
|
|
143
|
-
patchType,
|
|
144
|
-
post({ args, result }) {
|
|
145
|
-
const [url, fn] = args;
|
|
146
|
-
if (!url || !fn || !isValidPath(url)) return;
|
|
147
|
-
const routeInfo = createRouteInfo(format(url), method, 'App');
|
|
148
|
-
discover(routeInfo);
|
|
149
|
-
const lastLayer = getLastLayer(result._router);
|
|
150
|
-
if (lastLayer) patchHandle(lastLayer, routeInfo);
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
};
|