@pirxpilot/router 1.1.0 → 2.0.1
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 +57 -1
- package/index.js +241 -290
- package/lib/layer.js +38 -154
- package/lib/matcher.js +147 -0
- package/lib/route.js +58 -88
- package/package.json +7 -10
- package/HISTORY.md +0 -217
- package/SECURITY.md +0 -24
package/lib/layer.js
CHANGED
|
@@ -1,60 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
* router
|
|
3
|
-
* Copyright(c) 2013 Roman Shtylman
|
|
4
|
-
* Copyright(c) 2014-2022 Douglas Christopher Wilson
|
|
5
|
-
* MIT Licensed
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use strict'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Module dependencies.
|
|
12
|
-
* @private
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const pathRegexp = require('path-to-regexp')
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Module variables.
|
|
19
|
-
* @private
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
const TRAILING_SLASH_REGEXP = /\/+$/
|
|
23
|
-
const MATCHING_GROUP_REGEXP = /\((?:\?<(.*?)>)?(?!\?)/g
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Expose `Layer`.
|
|
27
|
-
*/
|
|
1
|
+
import matcher from './matcher.js';
|
|
28
2
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
function Layer (path, opts = {}, fn, method) {
|
|
3
|
+
export default function Layer(path, opts = {}, fn, method) {
|
|
32
4
|
if (typeof fn !== 'function') {
|
|
33
|
-
throw new TypeError('argument handler must be a function')
|
|
5
|
+
throw new TypeError('argument handler must be a function');
|
|
34
6
|
}
|
|
35
|
-
this.handle = fn
|
|
36
|
-
this.keys = []
|
|
37
|
-
this.name = fn.name || '<anonymous>'
|
|
38
|
-
this.params = undefined
|
|
39
|
-
this.path = undefined
|
|
40
|
-
this.slash = path === '/' && opts.end === false
|
|
41
|
-
this.matchers = this.slash
|
|
42
|
-
|
|
43
|
-
: Array.isArray(path)
|
|
44
|
-
? path.map(p => matcher(p, opts))
|
|
45
|
-
: [matcher(path, opts)]
|
|
46
|
-
this.method = method
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function matcher (path, { sensitive, end, strict }) {
|
|
50
|
-
return path instanceof RegExp
|
|
51
|
-
? createRegexMatcher(path)
|
|
52
|
-
: pathRegexp.match((strict ? path : loosen(path)), {
|
|
53
|
-
sensitive,
|
|
54
|
-
end,
|
|
55
|
-
trailing: !strict,
|
|
56
|
-
decode: decodeParam
|
|
57
|
-
})
|
|
7
|
+
this.handle = fn;
|
|
8
|
+
this.keys = [];
|
|
9
|
+
this.name = fn.name || '<anonymous>';
|
|
10
|
+
this.params = undefined;
|
|
11
|
+
this.path = undefined;
|
|
12
|
+
this.slash = path === '/' && opts.end === false;
|
|
13
|
+
this.matchers = this.slash ? [] : Array.isArray(path) ? path.map(p => matcher(p, opts)) : [matcher(path, opts)];
|
|
14
|
+
this.method = method;
|
|
58
15
|
}
|
|
59
16
|
|
|
60
17
|
/**
|
|
@@ -67,26 +24,26 @@ function matcher (path, { sensitive, end, strict }) {
|
|
|
67
24
|
* @api private
|
|
68
25
|
*/
|
|
69
26
|
|
|
70
|
-
Layer.prototype.handleError = function handleError
|
|
71
|
-
const fn = this.handle
|
|
27
|
+
Layer.prototype.handleError = function handleError(error, req, res, next) {
|
|
28
|
+
const fn = this.handle;
|
|
72
29
|
|
|
73
30
|
if (fn.length !== 4) {
|
|
74
31
|
// not a standard error handler
|
|
75
|
-
return next(error)
|
|
32
|
+
return next(error);
|
|
76
33
|
}
|
|
77
34
|
|
|
78
35
|
try {
|
|
79
36
|
// invoke function
|
|
80
|
-
const ret = fn(error, req, res, next)
|
|
37
|
+
const ret = fn(error, req, res, next);
|
|
81
38
|
|
|
82
39
|
// wait for returned promise
|
|
83
40
|
if (ret instanceof Promise) {
|
|
84
|
-
ret.catch((error = new Error('Rejected promise')) => next(error))
|
|
41
|
+
ret.catch((error = new Error('Rejected promise')) => next(error));
|
|
85
42
|
}
|
|
86
43
|
} catch (err) {
|
|
87
|
-
next(err)
|
|
44
|
+
next(err);
|
|
88
45
|
}
|
|
89
|
-
}
|
|
46
|
+
};
|
|
90
47
|
|
|
91
48
|
/**
|
|
92
49
|
* Handle the request for the layer.
|
|
@@ -97,26 +54,26 @@ Layer.prototype.handleError = function handleError (error, req, res, next) {
|
|
|
97
54
|
* @api private
|
|
98
55
|
*/
|
|
99
56
|
|
|
100
|
-
Layer.prototype.handleRequest = function handleRequest
|
|
101
|
-
const fn = this.handle
|
|
57
|
+
Layer.prototype.handleRequest = function handleRequest(req, res, next) {
|
|
58
|
+
const fn = this.handle;
|
|
102
59
|
|
|
103
60
|
if (fn.length > 3) {
|
|
104
61
|
// not a standard request handler
|
|
105
|
-
return next()
|
|
62
|
+
return next();
|
|
106
63
|
}
|
|
107
64
|
|
|
108
65
|
try {
|
|
109
66
|
// invoke function
|
|
110
|
-
const ret = fn(req, res, next)
|
|
67
|
+
const ret = fn(req, res, next);
|
|
111
68
|
|
|
112
69
|
// wait for returned promise
|
|
113
70
|
if (ret instanceof Promise) {
|
|
114
|
-
ret.catch((error = new Error('Rejected promise')) => next(error))
|
|
71
|
+
ret.catch((error = new Error('Rejected promise')) => next(error));
|
|
115
72
|
}
|
|
116
73
|
} catch (err) {
|
|
117
|
-
next(err)
|
|
74
|
+
next(err);
|
|
118
75
|
}
|
|
119
|
-
}
|
|
76
|
+
};
|
|
120
77
|
|
|
121
78
|
/**
|
|
122
79
|
* Check if this route matches `path`, if so
|
|
@@ -127,101 +84,28 @@ Layer.prototype.handleRequest = function handleRequest (req, res, next) {
|
|
|
127
84
|
* @api private
|
|
128
85
|
*/
|
|
129
86
|
|
|
130
|
-
Layer.prototype.match = function match
|
|
87
|
+
Layer.prototype.match = function match(path) {
|
|
131
88
|
if (path != null) {
|
|
132
89
|
// fast path non-ending match for / (any path matches)
|
|
133
90
|
if (this.slash) {
|
|
134
|
-
this.params = {}
|
|
135
|
-
this.path = ''
|
|
136
|
-
return true
|
|
91
|
+
this.params = {};
|
|
92
|
+
this.path = '';
|
|
93
|
+
return true;
|
|
137
94
|
}
|
|
138
95
|
|
|
139
96
|
for (const matcher of this.matchers) {
|
|
140
|
-
const matched = matcher(path)
|
|
97
|
+
const matched = matcher(path);
|
|
141
98
|
if (matched) {
|
|
142
99
|
// store values
|
|
143
|
-
this.params = matched.params
|
|
144
|
-
this.path = matched.path
|
|
145
|
-
this.keys =
|
|
146
|
-
return true
|
|
100
|
+
this.params = matched.params;
|
|
101
|
+
this.path = matched.path;
|
|
102
|
+
this.keys = matched.keys;
|
|
103
|
+
return true;
|
|
147
104
|
}
|
|
148
105
|
}
|
|
149
106
|
}
|
|
150
107
|
|
|
151
|
-
this.params = undefined
|
|
152
|
-
this.path = undefined
|
|
153
|
-
return false
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function createRegexMatcher (path) {
|
|
157
|
-
const keys = []
|
|
158
|
-
let name = 0
|
|
159
|
-
|
|
160
|
-
for (const m of path.source.matchAll(MATCHING_GROUP_REGEXP)) {
|
|
161
|
-
keys.push({
|
|
162
|
-
name: m[1] || name++,
|
|
163
|
-
offset: m.index
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return function regexpMatcher (p) {
|
|
168
|
-
const match = path.exec(p)
|
|
169
|
-
if (!match) {
|
|
170
|
-
return false
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const params = {}
|
|
174
|
-
for (let i = 1; i < match.length; i++) {
|
|
175
|
-
const key = keys[i - 1]
|
|
176
|
-
const prop = key.name
|
|
177
|
-
const val = decodeParam(match[i])
|
|
178
|
-
|
|
179
|
-
if (val !== undefined) {
|
|
180
|
-
params[prop] = val
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
params,
|
|
186
|
-
path: match[0]
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Decode param value.
|
|
193
|
-
*
|
|
194
|
-
* @param {string} val
|
|
195
|
-
* @return {string}
|
|
196
|
-
* @private
|
|
197
|
-
*/
|
|
198
|
-
|
|
199
|
-
function decodeParam (val) {
|
|
200
|
-
if (typeof val !== 'string' || val.length === 0) {
|
|
201
|
-
return val
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
return decodeURIComponent(val)
|
|
206
|
-
} catch (err) {
|
|
207
|
-
if (err instanceof URIError) {
|
|
208
|
-
err.message = `Failed to decode param '${val}'`
|
|
209
|
-
err.status = 400
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
throw err
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Loosens the given path for path-to-regexp matching.
|
|
218
|
-
*/
|
|
219
|
-
function loosen (path) {
|
|
220
|
-
if (path instanceof RegExp || path === '/') {
|
|
221
|
-
return path
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return Array.isArray(path)
|
|
225
|
-
? path.map(p => loosen(p))
|
|
226
|
-
: String(path).replace(TRAILING_SLASH_REGEXP, '')
|
|
227
|
-
}
|
|
108
|
+
this.params = undefined;
|
|
109
|
+
this.path = undefined;
|
|
110
|
+
return false;
|
|
111
|
+
};
|
package/lib/matcher.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
const MATCHING_GROUP_REGEXP = /\((?:\?<(.*?)>)?(?!\?)/g;
|
|
2
|
+
|
|
3
|
+
export default function matcher(path, { sensitive = false, end = true, strict = false } = {}) {
|
|
4
|
+
return path instanceof RegExp
|
|
5
|
+
? createRegexMatcher(path)
|
|
6
|
+
: createURLPatternMatcher(path, {
|
|
7
|
+
sensitive,
|
|
8
|
+
end,
|
|
9
|
+
strict
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createRegexMatcher(path) {
|
|
14
|
+
const regexKeys = [];
|
|
15
|
+
let name = 0;
|
|
16
|
+
|
|
17
|
+
for (const m of path.source.matchAll(MATCHING_GROUP_REGEXP)) {
|
|
18
|
+
regexKeys.push({
|
|
19
|
+
name: m[1] || name++,
|
|
20
|
+
offset: m.index
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return function regexpMatcher(p) {
|
|
25
|
+
const match = path.exec(p);
|
|
26
|
+
if (!match) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const params = {};
|
|
31
|
+
const keys = [];
|
|
32
|
+
for (let i = 1; i < match.length; i++) {
|
|
33
|
+
const { name } = regexKeys[i - 1];
|
|
34
|
+
const val = decodeParam(match[i]);
|
|
35
|
+
|
|
36
|
+
if (val !== undefined) {
|
|
37
|
+
keys.push(name);
|
|
38
|
+
params[name] = val;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
params,
|
|
44
|
+
keys,
|
|
45
|
+
path: match[0]
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const parameterRegex = /:([a-zA-Z_]\w*)/g;
|
|
51
|
+
|
|
52
|
+
export function createURLPatternMatcher(pathname, { sensitive, end, strict }) {
|
|
53
|
+
// Extract parameter names from the pattern in order
|
|
54
|
+
const parameterOrder = new Set(pathname.matchAll(parameterRegex).map(m => m[1]));
|
|
55
|
+
|
|
56
|
+
// Build the pattern:
|
|
57
|
+
// - For end=false (prefix matching): add {/*}? to match optional trailing content
|
|
58
|
+
// - For end=true with trailing=true: add {/}? to match optional trailing slash
|
|
59
|
+
|
|
60
|
+
if (!end) {
|
|
61
|
+
if (pathname.endsWith('/')) pathname = pathname.slice(0, -1);
|
|
62
|
+
pathname += '{/*}?';
|
|
63
|
+
} else if (!strict) {
|
|
64
|
+
if (pathname.endsWith('/')) pathname = pathname.slice(0, -1);
|
|
65
|
+
pathname += '{/}?';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const pattern = new URLPattern({ pathname }, { ignoreCase: !sensitive });
|
|
69
|
+
|
|
70
|
+
return function urlPatternMatcher(p) {
|
|
71
|
+
const match = pattern.exec(p);
|
|
72
|
+
if (!match) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const params = {};
|
|
77
|
+
const keys = [];
|
|
78
|
+
|
|
79
|
+
// Process parameters in the order they appear in the pattern
|
|
80
|
+
// Use the preserved parameter order
|
|
81
|
+
for (const key of parameterOrder) {
|
|
82
|
+
const value = match.pathname.groups[key];
|
|
83
|
+
if (value !== undefined) {
|
|
84
|
+
keys.push(key);
|
|
85
|
+
params[key] = decodeParam(value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Also handle any additional groups not in parameterOrder (e.g., numbered groups)
|
|
90
|
+
for (const [key, value] of Object.entries(match.pathname.groups)) {
|
|
91
|
+
// Skip the default wildcard capture group '0'
|
|
92
|
+
if (key === '0') continue;
|
|
93
|
+
// Skip if we already processed this key
|
|
94
|
+
if (parameterOrder.has(key)) continue;
|
|
95
|
+
if (value !== undefined) {
|
|
96
|
+
keys.push(key);
|
|
97
|
+
params[key] = decodeParam(value);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// For prefix matching, calculate the matched path length
|
|
102
|
+
let matchedPath = match.pathname.input;
|
|
103
|
+
if (!end) {
|
|
104
|
+
// Remove the wildcard matched portion to get just the prefix
|
|
105
|
+
const wildcardMatch = match.pathname.groups['0'];
|
|
106
|
+
if (wildcardMatch !== undefined && wildcardMatch !== '') {
|
|
107
|
+
// The wildcard captures content after the /, so we need to remove "/" + wildcard
|
|
108
|
+
matchedPath = matchedPath.slice(0, -(wildcardMatch.length + 1));
|
|
109
|
+
} else if (wildcardMatch === '') {
|
|
110
|
+
// Empty string means we matched a trailing slash: /foo/ with pattern /foo{/*}?
|
|
111
|
+
// Remove the trailing slash to normalize
|
|
112
|
+
matchedPath = matchedPath.slice(0, -1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
params,
|
|
118
|
+
keys,
|
|
119
|
+
path: matchedPath
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Decode param value.
|
|
126
|
+
*
|
|
127
|
+
* @param {string} val
|
|
128
|
+
* @return {string}
|
|
129
|
+
* @private
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
export function decodeParam(val) {
|
|
133
|
+
if (typeof val !== 'string' || val.length === 0) {
|
|
134
|
+
return val;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
return decodeURIComponent(val);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
if (err instanceof URIError) {
|
|
141
|
+
err.message = `Failed to decode param '${val}'`;
|
|
142
|
+
err.status = 400;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
throw err;
|
|
146
|
+
}
|
|
147
|
+
}
|
package/lib/route.js
CHANGED
|
@@ -1,30 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Copyright(c) 2013 Roman Shtylman
|
|
4
|
-
* Copyright(c) 2014-2022 Douglas Christopher Wilson
|
|
5
|
-
* MIT Licensed
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use strict'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Module dependencies.
|
|
12
|
-
* @private
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const { METHODS } = require('node:http')
|
|
16
|
-
const Layer = require('./layer')
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Module variables.
|
|
20
|
-
* @private
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Expose `Route`.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
module.exports = Route
|
|
1
|
+
import { METHODS } from 'node:http';
|
|
2
|
+
import Layer from './layer.js';
|
|
28
3
|
|
|
29
4
|
/**
|
|
30
5
|
* Initialize `Route` with the given `path`,
|
|
@@ -33,45 +8,41 @@ module.exports = Route
|
|
|
33
8
|
* @api private
|
|
34
9
|
*/
|
|
35
10
|
|
|
36
|
-
function Route
|
|
37
|
-
this.path = path
|
|
38
|
-
this.stack = []
|
|
11
|
+
export default function Route(path) {
|
|
12
|
+
this.path = path;
|
|
13
|
+
this.stack = [];
|
|
39
14
|
|
|
40
15
|
// route handlers for various http methods
|
|
41
|
-
this.methods = new Set()
|
|
16
|
+
this.methods = new Set();
|
|
42
17
|
}
|
|
43
18
|
|
|
44
|
-
|
|
45
|
-
* @private
|
|
46
|
-
*/
|
|
47
|
-
|
|
48
|
-
Route.prototype._handlesMethod = function _handlesMethod (method) {
|
|
19
|
+
Route.prototype._handlesMethod = function _handlesMethod(method) {
|
|
49
20
|
if (this.methods._all) {
|
|
50
|
-
return true
|
|
21
|
+
return true;
|
|
51
22
|
}
|
|
52
23
|
|
|
53
24
|
if (method === 'HEAD' && !this.methods.has('HEAD')) {
|
|
54
|
-
method = 'GET'
|
|
25
|
+
method = 'GET';
|
|
55
26
|
}
|
|
56
27
|
|
|
57
|
-
return this.methods.has(method)
|
|
58
|
-
}
|
|
28
|
+
return this.methods.has(method);
|
|
29
|
+
};
|
|
59
30
|
|
|
60
31
|
/**
|
|
61
32
|
* @return {array} supported HTTP methods
|
|
62
33
|
* @private
|
|
63
34
|
*/
|
|
64
35
|
|
|
65
|
-
Route.prototype._methods = function _methods
|
|
66
|
-
const methods = [...this.methods]
|
|
36
|
+
Route.prototype._methods = function _methods() {
|
|
37
|
+
const methods = [...this.methods];
|
|
67
38
|
|
|
68
39
|
// append automatic head
|
|
69
40
|
if (this.methods.has('GET') && !this.methods.has('HEAD')) {
|
|
70
|
-
methods.push('HEAD')
|
|
41
|
+
methods.push('HEAD');
|
|
71
42
|
}
|
|
72
43
|
|
|
73
|
-
return methods
|
|
74
|
-
}
|
|
44
|
+
return methods;
|
|
45
|
+
};
|
|
75
46
|
|
|
76
47
|
/**
|
|
77
48
|
* dispatch req, res into this route
|
|
@@ -79,71 +50,71 @@ Route.prototype._methods = function _methods () {
|
|
|
79
50
|
* @private
|
|
80
51
|
*/
|
|
81
52
|
|
|
82
|
-
Route.prototype.dispatch = function dispatch
|
|
83
|
-
let idx = 0
|
|
84
|
-
const stack = this.stack
|
|
85
|
-
let sync = 0
|
|
53
|
+
Route.prototype.dispatch = function dispatch(req, res, done) {
|
|
54
|
+
let idx = 0;
|
|
55
|
+
const stack = this.stack;
|
|
56
|
+
let sync = 0;
|
|
86
57
|
|
|
87
58
|
if (stack.length === 0) {
|
|
88
|
-
return done()
|
|
59
|
+
return done();
|
|
89
60
|
}
|
|
90
61
|
|
|
91
|
-
let { method } = req
|
|
62
|
+
let { method } = req;
|
|
92
63
|
if (method === 'HEAD' && !this.methods.has('HEAD')) {
|
|
93
|
-
method = 'GET'
|
|
64
|
+
method = 'GET';
|
|
94
65
|
}
|
|
95
66
|
|
|
96
|
-
req.route = this
|
|
67
|
+
req.route = this;
|
|
97
68
|
|
|
98
|
-
next()
|
|
69
|
+
next();
|
|
99
70
|
|
|
100
|
-
function next
|
|
71
|
+
function next(err) {
|
|
101
72
|
// signal to exit route
|
|
102
73
|
if (err && err === 'route') {
|
|
103
|
-
return done()
|
|
74
|
+
return done();
|
|
104
75
|
}
|
|
105
76
|
|
|
106
77
|
// signal to exit router
|
|
107
78
|
if (err && err === 'router') {
|
|
108
|
-
return done(err)
|
|
79
|
+
return done(err);
|
|
109
80
|
}
|
|
110
81
|
|
|
111
82
|
// no more matching layers
|
|
112
83
|
if (idx >= stack.length) {
|
|
113
|
-
return done(err)
|
|
84
|
+
return done(err);
|
|
114
85
|
}
|
|
115
86
|
|
|
116
87
|
// max sync stack
|
|
117
88
|
if (++sync > 100) {
|
|
118
|
-
return setImmediate(next, err)
|
|
89
|
+
return setImmediate(next, err);
|
|
119
90
|
}
|
|
120
91
|
|
|
121
|
-
let layer
|
|
122
|
-
let match
|
|
92
|
+
let layer;
|
|
93
|
+
let match;
|
|
123
94
|
|
|
124
95
|
// find next matching layer
|
|
125
96
|
while (idx < stack.length) {
|
|
126
|
-
layer = stack[idx++]
|
|
127
|
-
match = !layer.method || layer.method === method
|
|
97
|
+
layer = stack[idx++];
|
|
98
|
+
match = !layer.method || layer.method === method;
|
|
128
99
|
if (match) {
|
|
129
|
-
break
|
|
100
|
+
break;
|
|
130
101
|
}
|
|
131
102
|
}
|
|
132
103
|
|
|
133
104
|
// no match
|
|
134
105
|
if (match !== true) {
|
|
135
|
-
return done(err)
|
|
106
|
+
return done(err);
|
|
136
107
|
}
|
|
137
108
|
|
|
138
109
|
if (err) {
|
|
139
|
-
layer.handleError(err, req, res, next)
|
|
110
|
+
layer.handleError(err, req, res, next);
|
|
140
111
|
} else {
|
|
141
|
-
layer.handleRequest(req, res, next)
|
|
112
|
+
layer.handleRequest(req, res, next);
|
|
142
113
|
}
|
|
143
114
|
|
|
144
|
-
sync = 0
|
|
115
|
+
sync = 0;
|
|
145
116
|
}
|
|
146
|
-
}
|
|
117
|
+
};
|
|
147
118
|
|
|
148
119
|
/**
|
|
149
120
|
* Add a handler for all HTTP verbs to this route.
|
|
@@ -173,31 +144,30 @@ Route.prototype.dispatch = function dispatch (req, res, done) {
|
|
|
173
144
|
* @api public
|
|
174
145
|
*/
|
|
175
146
|
|
|
176
|
-
Route.prototype.all = function all
|
|
177
|
-
const callbacks = args.flat(
|
|
147
|
+
Route.prototype.all = function all(...args) {
|
|
148
|
+
const callbacks = args.flat(Number.POSITIVE_INFINITY);
|
|
178
149
|
|
|
179
150
|
if (callbacks.length === 0) {
|
|
180
|
-
throw new TypeError('argument handler is required')
|
|
151
|
+
throw new TypeError('argument handler is required');
|
|
181
152
|
}
|
|
182
153
|
|
|
183
|
-
this.methods._all = true
|
|
184
|
-
this.stack.push(...callbacks.map(fn => new Layer('/', {}, fn)))
|
|
154
|
+
this.methods._all = true;
|
|
155
|
+
this.stack.push(...callbacks.map(fn => new Layer('/', {}, fn)));
|
|
185
156
|
|
|
186
|
-
return this
|
|
187
|
-
}
|
|
157
|
+
return this;
|
|
158
|
+
};
|
|
188
159
|
|
|
189
|
-
METHODS
|
|
190
|
-
.
|
|
191
|
-
|
|
192
|
-
const callbacks = args.flat(Infinity)
|
|
160
|
+
METHODS.forEach(method => {
|
|
161
|
+
Route.prototype[method.toLowerCase()] = function (...args) {
|
|
162
|
+
const callbacks = args.flat(Number.POSITIVE_INFINITY);
|
|
193
163
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
164
|
+
if (callbacks.length === 0) {
|
|
165
|
+
throw new TypeError('argument handler is required');
|
|
166
|
+
}
|
|
197
167
|
|
|
198
|
-
|
|
199
|
-
|
|
168
|
+
this.methods.add(method);
|
|
169
|
+
this.stack.push(...callbacks.map(fn => new Layer('/', {}, fn, method)));
|
|
200
170
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
171
|
+
return this;
|
|
172
|
+
};
|
|
173
|
+
});
|
package/package.json
CHANGED
|
@@ -1,35 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pirxpilot/router",
|
|
3
3
|
"description": "Simple middleware-style router",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.1",
|
|
5
5
|
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
|
|
6
6
|
"contributors": [
|
|
7
7
|
"Blake Embrey <hello@blakeembrey.com>"
|
|
8
8
|
],
|
|
9
9
|
"license": "MIT",
|
|
10
|
+
"exports": "./index.js",
|
|
11
|
+
"type": "module",
|
|
10
12
|
"repository": {
|
|
11
13
|
"type": "git",
|
|
12
14
|
"url": "git+https://github.com/pirxpilot/router.git"
|
|
13
15
|
},
|
|
14
16
|
"dependencies": {
|
|
15
|
-
"parseurl": "~1"
|
|
16
|
-
"path-to-regexp": "~8"
|
|
17
|
+
"parseurl": "~1"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
19
|
-
"
|
|
20
|
-
"
|
|
20
|
+
"@biomejs/biome": "2.3.1",
|
|
21
|
+
"finalhandler": "~2",
|
|
21
22
|
"supertest": "~7"
|
|
22
23
|
},
|
|
23
24
|
"files": [
|
|
24
25
|
"lib/",
|
|
25
|
-
"LICENSE",
|
|
26
|
-
"HISTORY.md",
|
|
27
|
-
"README.md",
|
|
28
|
-
"SECURITY.md",
|
|
29
26
|
"index.js"
|
|
30
27
|
],
|
|
31
28
|
"engines": {
|
|
32
|
-
"node": ">= 0
|
|
29
|
+
"node": ">= 23.8.0"
|
|
33
30
|
},
|
|
34
31
|
"scripts": {
|
|
35
32
|
"test": "make check"
|