@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
|
@@ -0,0 +1,67 @@
|
|
|
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 { RouteType } = require('@contrast/common');
|
|
18
|
+
const { patchType, formatHandler } = require('./../../utils/route-info');
|
|
19
|
+
const isArray = (arr) => Array.isArray(arr);
|
|
20
|
+
module.exports = function init(core) {
|
|
21
|
+
const { patcher, depHooks, routeCoverage } = core;
|
|
22
|
+
|
|
23
|
+
return core.routeCoverage.fastifyMiddie = {
|
|
24
|
+
install() {
|
|
25
|
+
depHooks.resolve({ name: '@fastify/middie', version: '*', file: 'lib/engine.js' }, (middie) => patcher.patch(middie, {
|
|
26
|
+
name: 'fastifyMiddie',
|
|
27
|
+
patchType,
|
|
28
|
+
post(data) {
|
|
29
|
+
patcher.patch(data.result, 'use', {
|
|
30
|
+
name: 'use',
|
|
31
|
+
patchType,
|
|
32
|
+
pre(data) {
|
|
33
|
+
const [url, fn] = data.args;
|
|
34
|
+
if (!url || !fn || !core.config.getEffectiveValue('assess.report_middleware_routes')) return;
|
|
35
|
+
|
|
36
|
+
const middleware = isArray(fn) ? fn : [fn];
|
|
37
|
+
const formattedPath = isArray(url) ? `[${url.join(', ')}]` : url;
|
|
38
|
+
const patchedMiddleware = middleware.map((f) => {
|
|
39
|
+
const formattedHandler = formatHandler(f);
|
|
40
|
+
const signature = `fastify.use(${formattedPath}, ${formattedHandler})`;
|
|
41
|
+
|
|
42
|
+
const routeInfo = {
|
|
43
|
+
signature,
|
|
44
|
+
url: formattedPath,
|
|
45
|
+
method: 'use',
|
|
46
|
+
normalizedUrl: formattedPath,
|
|
47
|
+
type: RouteType.MIDDLEWARE,
|
|
48
|
+
framework: 'fastify'
|
|
49
|
+
};
|
|
50
|
+
routeCoverage.discover(routeInfo);
|
|
51
|
+
|
|
52
|
+
return patcher.patch(f, {
|
|
53
|
+
name: 'middleware',
|
|
54
|
+
patchType,
|
|
55
|
+
post() {
|
|
56
|
+
routeCoverage.observe(routeInfo);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
data.args[1] = patchedMiddleware;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
};
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
const { getFastifyMethods } = require('
|
|
17
|
+
const { getFastifyMethods } = require('../../utils/methods');
|
|
18
18
|
const {
|
|
19
19
|
primordials: { StringPrototypeToLowerCase, StringPrototypeSplit },
|
|
20
20
|
RouteType,
|
|
21
21
|
} = require('@contrast/common');
|
|
22
|
-
const { patchType } = require('
|
|
22
|
+
const { patchType, formatHandler } = require('./../../utils/route-info');
|
|
23
23
|
|
|
24
24
|
// Spec: https://contrast.atlassian.net/wiki/spaces/NOD/pages/3454861621/Node.js+Agent+Route+Signatures#Fastify
|
|
25
25
|
module.exports = function init(core) {
|
|
@@ -38,12 +38,12 @@ module.exports = function init(core) {
|
|
|
38
38
|
return route?.[kRoutePrefix];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function createRouteInfo(method, url, fullyDeclared, type) {
|
|
41
|
+
function createRouteInfo(method, url, fullyDeclared, type, handler) {
|
|
42
42
|
method = StringPrototypeToLowerCase.call(method);
|
|
43
43
|
|
|
44
44
|
const signature = fullyDeclared
|
|
45
|
-
? `fastify.route({ method:
|
|
46
|
-
: `fastify.${method}(
|
|
45
|
+
? `fastify.route({ method: ${method}, url: ${url}, handler: ${formatHandler(handler)} })`
|
|
46
|
+
: `fastify.${method}(${url}, ${formatHandler(handler)})`;
|
|
47
47
|
|
|
48
48
|
const routeInfo = {
|
|
49
49
|
signature,
|
|
@@ -89,30 +89,30 @@ module.exports = function init(core) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
function discoverAndPatch(method, path, routeObj, handle, methods, fullyDeclared) {
|
|
92
|
+
function discoverAndPatch(method, path, routeObj, handle, handler, methods, fullyDeclared) {
|
|
93
93
|
const type = routeObj?.options?.websocket ? RouteType.MESSAGE_BROKER : RouteType.HTTP;
|
|
94
94
|
|
|
95
95
|
if (Array.isArray(method)) {
|
|
96
96
|
// If all valid methods are included in `method` then .all shorthand was most likely used
|
|
97
97
|
if (methods.every(m => method.includes(m))) {
|
|
98
|
-
const routeInfo = createRouteInfo('all', path, fullyDeclared, type);
|
|
98
|
+
const routeInfo = createRouteInfo('all', path, fullyDeclared, type, handler);
|
|
99
99
|
routeCoverage.discover(routeInfo);
|
|
100
100
|
patchHandler(routeObj, handle, routeInfo);
|
|
101
101
|
} else {
|
|
102
102
|
method.forEach((verb) => {
|
|
103
|
-
const routeInfo = createRouteInfo(verb, path, fullyDeclared, type);
|
|
103
|
+
const routeInfo = createRouteInfo(verb, path, fullyDeclared, type, handler);
|
|
104
104
|
routeCoverage.discover(routeInfo);
|
|
105
105
|
patchHandler(routeObj, handle, routeInfo);
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
} else {
|
|
109
|
-
const routeInfo = createRouteInfo(method, path, fullyDeclared, type);
|
|
109
|
+
const routeInfo = createRouteInfo(method, path, fullyDeclared, type, handler);
|
|
110
110
|
routeCoverage.discover(routeInfo);
|
|
111
111
|
patchHandler(routeObj, handle, routeInfo);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
return core.routeCoverage.
|
|
115
|
+
return core.routeCoverage.fastifyCore = {
|
|
116
116
|
install() {
|
|
117
117
|
/**
|
|
118
118
|
* There are some subtle differences between fastify minor versions the instrumentation must account for
|
|
@@ -158,7 +158,7 @@ module.exports = function init(core) {
|
|
|
158
158
|
let handle = 'handler';
|
|
159
159
|
if (!handler && typeof options === 'function') handle = 'options';
|
|
160
160
|
|
|
161
|
-
discoverAndPatch(method, path, routeObj ? data.args[0] : data.args, handle, fastifyMethods, false);
|
|
161
|
+
discoverAndPatch(method, path, routeObj ? data.args[0] : data.args, handle, options || handler, fastifyMethods, false);
|
|
162
162
|
}
|
|
163
163
|
});
|
|
164
164
|
|
|
@@ -175,7 +175,7 @@ module.exports = function init(core) {
|
|
|
175
175
|
|
|
176
176
|
const prefix = getPrefix(data.obj);
|
|
177
177
|
const path = prefix ? prefix + url : url;
|
|
178
|
-
discoverAndPatch(method, path, routeArgs, 'handler', fastifyMethods, true);
|
|
178
|
+
discoverAndPatch(method, path, routeArgs, 'handler', handler, fastifyMethods, true);
|
|
179
179
|
}
|
|
180
180
|
});
|
|
181
181
|
}
|
|
@@ -18,14 +18,15 @@
|
|
|
18
18
|
const { callChildComponentMethodsSync } = require('@contrast/common');
|
|
19
19
|
|
|
20
20
|
module.exports = function(core) {
|
|
21
|
-
const
|
|
21
|
+
const fastifyRouteCoverage = core.routeCoverage.fastify = {
|
|
22
22
|
install() {
|
|
23
|
-
callChildComponentMethodsSync(
|
|
23
|
+
callChildComponentMethodsSync(fastifyRouteCoverage, 'install');
|
|
24
24
|
},
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
require('./
|
|
28
|
-
require('./
|
|
27
|
+
require('./fastify')(core);
|
|
28
|
+
require('./fastify-express')(core);
|
|
29
|
+
require('./fastify-middie')(core);
|
|
29
30
|
|
|
30
|
-
return
|
|
31
|
+
return fastifyRouteCoverage;
|
|
31
32
|
};
|
package/lib/install/hapi.js
CHANGED
|
@@ -12,76 +12,139 @@
|
|
|
12
12
|
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
13
|
* way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
|
+
// @ts-check
|
|
15
16
|
'use strict';
|
|
16
17
|
|
|
17
|
-
const {
|
|
18
|
-
const {
|
|
18
|
+
const { AsyncLocalStorage } = require('node:async_hooks');
|
|
19
|
+
const { RouteType, set } = require('@contrast/common');
|
|
20
|
+
const { Core } = require('@contrast/core/lib/ioc/core');
|
|
21
|
+
const { formatHandler, patchType } = require('../utils/route-info');
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
/**
|
|
24
|
+
* The hapi `Route` class from lib/route.js is not defined or exported.
|
|
25
|
+
* @typedef {Object} Route
|
|
26
|
+
* @property {boolean} _special internal hapi property for special routes
|
|
27
|
+
* @property {string} method
|
|
28
|
+
* @property {string} path
|
|
29
|
+
* @property {Object} settings
|
|
30
|
+
* @property {(request: { method: string, path: string}) => any} settings.handler set by the Route constructor
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
class HapiRouteCoverage {
|
|
34
|
+
/**
|
|
35
|
+
* @param {import('..').Core & {
|
|
36
|
+
* routeCoverage: import('..').RouteCoverage;
|
|
37
|
+
* }} core
|
|
38
|
+
*/
|
|
39
|
+
constructor(core) {
|
|
40
|
+
set(core, 'routeCoverage.hapi', this);
|
|
41
|
+
this.core = core;
|
|
42
|
+
this.depHooks = core.depHooks;
|
|
43
|
+
this.patcher = core.patcher;
|
|
44
|
+
this.routeCoverage = core.routeCoverage;
|
|
45
|
+
this.registerScope = new AsyncLocalStorage();
|
|
46
|
+
}
|
|
23
47
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
install() {
|
|
49
|
+
this.depHooks.resolve(
|
|
50
|
+
{ name: '@hapi/hapi', version: '>=18 <22', file: 'lib/server.js' },
|
|
51
|
+
/** @param {typeof import('@hapi/hapi').Server} Server */
|
|
52
|
+
(Server) => this.patchServer(Server),
|
|
53
|
+
);
|
|
54
|
+
this.depHooks.resolve(
|
|
55
|
+
{ name: '@hapi/hapi', version: '>=18 <22', file: 'lib/route.js' },
|
|
56
|
+
/** @param {abstract new () => Route} Route */
|
|
57
|
+
(Route) => this.patchRoute(Route),
|
|
58
|
+
);
|
|
35
59
|
}
|
|
36
60
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Spec: https://contrast.atlassian.net/wiki/spaces/NOD/pages/3454861621/Node.js+Agent+Route+Signatures#Hapi
|
|
63
|
+
* @param {Route} route
|
|
64
|
+
*/
|
|
65
|
+
createSignature(route) {
|
|
66
|
+
const handler = formatHandler(this.patcher.unwrap(route.settings.handler));
|
|
67
|
+
return `server.route({ method: '${route.method}', path: '${route.path}', handler: ${handler} })`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @param {typeof import('@hapi/hapi').Server} Server */
|
|
71
|
+
patchServer(Server) {
|
|
72
|
+
const self = this;
|
|
73
|
+
return this.patcher.patch(Server, {
|
|
74
|
+
name: 'hapi.Server',
|
|
75
|
+
patchType,
|
|
76
|
+
post({ result: server }) {
|
|
77
|
+
self.patcher.patch(server, 'register', {
|
|
78
|
+
name: 'server.register',
|
|
79
|
+
patchType,
|
|
80
|
+
around(next) {
|
|
81
|
+
if (self.registerScope.getStore()) return next();
|
|
82
|
+
return self.registerScope.run({ isMiddleware: true }, next);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
50
88
|
|
|
51
|
-
|
|
52
|
-
|
|
89
|
+
/**
|
|
90
|
+
* @param {Route} route
|
|
91
|
+
* @param {string} signature
|
|
92
|
+
* @param {RouteType} type
|
|
93
|
+
*/
|
|
94
|
+
patchRouteHandler(route, signature, type) {
|
|
95
|
+
const self = this;
|
|
96
|
+
this.patcher.patch(route.settings, 'handler', {
|
|
97
|
+
name: 'route.settings.handler',
|
|
98
|
+
patchType,
|
|
99
|
+
// this needs to be in a pre-hook so that the route
|
|
100
|
+
// data is in the store before our dataflow hooks run
|
|
101
|
+
pre({ args: [request] }) {
|
|
102
|
+
self.routeCoverage.observe({
|
|
103
|
+
signature,
|
|
104
|
+
method: request.method,
|
|
105
|
+
url: request.path,
|
|
106
|
+
normalizedUrl: route.path, // should also be defined at `request.route.path`
|
|
107
|
+
framework: 'hapi',
|
|
108
|
+
type,
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
53
113
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
114
|
+
/**
|
|
115
|
+
* @param {abstract new () => Route} Route
|
|
116
|
+
*/
|
|
117
|
+
patchRoute(Route) {
|
|
118
|
+
const self = this;
|
|
119
|
+
return this.patcher.patch(Route, {
|
|
120
|
+
name: 'hapi.Route',
|
|
121
|
+
patchType,
|
|
122
|
+
post({ result: route }) {
|
|
123
|
+
if (route._special) return; // skip special internal routes
|
|
124
|
+
const signature = self.createSignature(route);
|
|
125
|
+
const type = self.registerScope.getStore()?.isMiddleware ? RouteType.MIDDLEWARE : RouteType.HTTP;
|
|
61
126
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
//TODO: Will this signature always be associated with an existing route?
|
|
70
|
-
const signature = createSignature(method, path);
|
|
71
|
-
routeCoverage.observe({
|
|
72
|
-
signature,
|
|
73
|
-
url,
|
|
74
|
-
method: StringPrototypeToLowerCase.call(method),
|
|
75
|
-
normalizedUrl: route.path,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
});
|
|
127
|
+
self.routeCoverage.discover({
|
|
128
|
+
signature,
|
|
129
|
+
method: route.method,
|
|
130
|
+
url: route.path,
|
|
131
|
+
normalizedUrl: route.path,
|
|
132
|
+
framework: 'hapi',
|
|
133
|
+
type,
|
|
83
134
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
};
|
|
135
|
+
|
|
136
|
+
self.patchRouteHandler(route, signature, type);
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = Core.makeComponent({
|
|
143
|
+
name: 'routeCoverage.hapi',
|
|
144
|
+
/**
|
|
145
|
+
* @param {import('..').Core & {
|
|
146
|
+
* routeCoverage: import('..').RouteCoverage;
|
|
147
|
+
* }} core
|
|
148
|
+
*/
|
|
149
|
+
factory: (core) => new HapiRouteCoverage(core),
|
|
150
|
+
});
|
package/lib/install/koa.js
CHANGED
|
@@ -15,28 +15,15 @@
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
17
|
const { METHODS } = require('./../utils/methods');
|
|
18
|
-
const { isString, primordials: { StringPrototypeToLowerCase, StringPrototypeSplit } } = require('@contrast/common');
|
|
19
|
-
const {
|
|
18
|
+
const { isString, RouteType, primordials: { StringPrototypeToLowerCase, StringPrototypeSplit } } = require('@contrast/common');
|
|
19
|
+
const { patchType, formatHandler } = require('./../utils/route-info');
|
|
20
20
|
|
|
21
21
|
// Spec: https://contrast.atlassian.net/wiki/spaces/NOD/pages/3454861621/Node.js+Agent+Route+Signatures#Koa
|
|
22
22
|
module.exports = function init(core) {
|
|
23
23
|
const { patcher, depHooks, routeCoverage } = core;
|
|
24
|
-
|
|
25
|
-
function createRouteInfo(method, url) {
|
|
26
|
-
method = StringPrototypeToLowerCase.call(method);
|
|
27
|
-
const routeInfo = {
|
|
28
|
-
signature: createSignature(url, method),
|
|
29
|
-
url,
|
|
30
|
-
method,
|
|
31
|
-
normalizedUrl: url,
|
|
32
|
-
framework: 'koa'
|
|
33
|
-
};
|
|
34
|
-
return routeInfo;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
24
|
return core.routeCoverage.koa = {
|
|
38
25
|
install() {
|
|
39
|
-
depHooks.resolve({ name: 'koa', version: '>=2.3.0 <4' }, (Koa) => {
|
|
26
|
+
depHooks.resolve({ name: 'koa', version: '>=2.3.0 <4' }, (Koa, pkgMeta) => {
|
|
40
27
|
// Koa uses its own routing library @koa/router to define routes before
|
|
41
28
|
// mounting them on the app with .use so instrumenting use and traversing
|
|
42
29
|
// the constructed routes is the more technically correct approach than
|
|
@@ -48,40 +35,47 @@ module.exports = function init(core) {
|
|
|
48
35
|
if (args?.length === 0) return;
|
|
49
36
|
const [router] = args;
|
|
50
37
|
|
|
51
|
-
if (!router?.router)
|
|
38
|
+
if (!router?.router) {
|
|
39
|
+
core.logger.debug('no routes detected in koa router stack: %s@%s', pkgMeta.name, pkgMeta.version);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
52
42
|
|
|
53
43
|
router.router.stack.forEach((Layer) => {
|
|
54
|
-
const { methods, path } = Layer;
|
|
55
|
-
if (!path || !isString(path)) return;
|
|
44
|
+
const { methods, path, stack } = Layer;
|
|
45
|
+
if (!path || !isString(path) || !stack || stack.length === 0) return;
|
|
56
46
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
47
|
+
const patchedMiddleware = [];
|
|
48
|
+
stack.forEach((handler) => {
|
|
49
|
+
const method = methods.length === 0 ? 'use' : METHODS.every(m => methods.includes(m)) ? 'all' : StringPrototypeToLowerCase.call(methods[methods.length - 1]);
|
|
50
|
+
if (method === 'use' && !core.config.getEffectiveValue('assess.report_middleware_routes')) return;
|
|
51
|
+
const routeInfo = {
|
|
52
|
+
signature: `Router.${method}(${path}, ${formatHandler(handler)})`,
|
|
53
|
+
method,
|
|
54
|
+
url: path,
|
|
55
|
+
normalizedUrl: path,
|
|
56
|
+
framework: 'koa',
|
|
57
|
+
type: method === 'use' ? RouteType.MIDDLEWARE : RouteType.HTTP
|
|
58
|
+
};
|
|
65
59
|
routeCoverage.discover(routeInfo);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
const patchedHandler = patcher.patch(handler, {
|
|
61
|
+
name: 'handler',
|
|
62
|
+
patchType,
|
|
63
|
+
pre(data) {
|
|
64
|
+
const { request } = data.args[0];
|
|
65
|
+
if (!request) return;
|
|
72
66
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Layer.stack =
|
|
67
|
+
const { method, url } = request;
|
|
68
|
+
if (!method | !url) return;
|
|
69
|
+
routeCoverage.observe({
|
|
70
|
+
...routeInfo,
|
|
71
|
+
method: StringPrototypeToLowerCase.call(method),
|
|
72
|
+
url: StringPrototypeSplit.call(url, /\?/)[0]
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
patchedMiddleware.push(patchedHandler);
|
|
77
|
+
});
|
|
78
|
+
Layer.stack = patchedMiddleware;
|
|
85
79
|
});
|
|
86
80
|
}
|
|
87
81
|
});
|