@koa/router 10.1.1 → 11.0.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/LICENSE +2 -3
- package/README.md +53 -24
- package/lib/layer.js +28 -22
- package/lib/router.js +97 -55
- package/package.json +34 -15
- package/history.md +0 -157
package/LICENSE
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2015 Alexander C. Mingoia
|
|
4
|
-
Copyright (c) 2019-present Nick Baugh and Koajs contributors
|
|
3
|
+
Copyright (c) 2015 Alexander C. Mingoia and @koajs contributors
|
|
5
4
|
|
|
6
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -19,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
19
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
-
THE SOFTWARE.
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
# [@koa/router](https://github.com/koajs/router)
|
|
2
2
|
|
|
3
|
-
> Router middleware for [Koa](https://github.com/koajs/koa).
|
|
3
|
+
> Router middleware for [Koa](https://github.com/koajs/koa). Maintained by [Forward Email][forward-email] and [Lad][].
|
|
4
4
|
|
|
5
|
-
[](https://github.com/koajs/router/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/sindresorhus/xo)
|
|
7
|
+
[](https://github.com/prettier/prettier)
|
|
8
|
+
[](https://lass.js.org)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
* [Features](#features)
|
|
15
|
+
* [Migrating to 7 / Koa 2](#migrating-to-7--koa-2)
|
|
16
|
+
* [Install](#install)
|
|
17
|
+
* [Typescript Support](#typescript-support)
|
|
18
|
+
* [API Reference](#api-reference)
|
|
19
|
+
* [Contributors](#contributors)
|
|
20
|
+
* [License](#license)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Features
|
|
10
24
|
|
|
11
25
|
* Express-style routing (`app.get`, `app.put`, `app.post`, etc.)
|
|
12
26
|
* Named URL parameters
|
|
@@ -17,39 +31,54 @@
|
|
|
17
31
|
* Multiple and nestable routers
|
|
18
32
|
* `async/await` support
|
|
19
33
|
|
|
34
|
+
|
|
20
35
|
## Migrating to 7 / Koa 2
|
|
21
36
|
|
|
22
|
-
|
|
37
|
+
* The API has changed to match the new promise-based middleware
|
|
23
38
|
signature of koa 2. See the [koa 2.x readme](https://github.com/koajs/koa/tree/2.0.0-alpha.3) for more
|
|
24
39
|
information.
|
|
25
|
-
|
|
40
|
+
* Middleware is now always run in the order declared by `.use()` (or `.get()`,
|
|
26
41
|
etc.), which matches Express 4 API.
|
|
27
42
|
|
|
28
|
-
## Installation
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
npm
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
[npm][]:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
npm install @koa/router
|
|
35
50
|
```
|
|
36
51
|
|
|
37
|
-
## [API Reference](./API.md)
|
|
38
52
|
|
|
39
|
-
##
|
|
53
|
+
## Typescript Support
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npm install @types/koa__router
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## API Reference
|
|
61
|
+
|
|
62
|
+
See [API Reference](./API.md) for more documentation.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Contributors
|
|
66
|
+
|
|
67
|
+
| Name |
|
|
68
|
+
| ---------------- |
|
|
69
|
+
| **Alex Mingoia** |
|
|
70
|
+
| **@koajs** |
|
|
40
71
|
|
|
41
|
-
Please submit all issues and pull requests to the [koajs/router](http://github.com/koajs/router) repository!
|
|
42
72
|
|
|
43
|
-
##
|
|
73
|
+
## License
|
|
44
74
|
|
|
45
|
-
|
|
75
|
+
[MIT](LICENSE) © Alex Mingoia
|
|
46
76
|
|
|
47
|
-
## Call for Maintainers
|
|
48
77
|
|
|
49
|
-
|
|
78
|
+
##
|
|
50
79
|
|
|
51
|
-
|
|
80
|
+
[forward-email]: https://forwardemail.net
|
|
52
81
|
|
|
53
|
-
|
|
82
|
+
[lad]: https://lad.js.org
|
|
54
83
|
|
|
55
|
-
[
|
|
84
|
+
[npm]: https//www.npmjs.com
|
package/lib/layer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const { pathToRegexp, compile, parse } = require('path-to-regexp');
|
|
2
1
|
const { parse: parseUrl, format: formatUrl } = require('url');
|
|
2
|
+
const { pathToRegexp, compile, parse } = require('path-to-regexp');
|
|
3
3
|
|
|
4
4
|
module.exports = Layer;
|
|
5
5
|
|
|
@@ -17,31 +17,33 @@ module.exports = Layer;
|
|
|
17
17
|
* @private
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
function Layer(path, methods, middleware, opts) {
|
|
21
|
-
this.opts = opts
|
|
20
|
+
function Layer(path, methods, middleware, opts = {}) {
|
|
21
|
+
this.opts = opts;
|
|
22
22
|
this.name = this.opts.name || null;
|
|
23
23
|
this.methods = [];
|
|
24
24
|
this.paramNames = [];
|
|
25
25
|
this.stack = Array.isArray(middleware) ? middleware : [middleware];
|
|
26
26
|
|
|
27
|
-
for (
|
|
28
|
-
const l = this.methods.push(
|
|
27
|
+
for (const method of methods) {
|
|
28
|
+
const l = this.methods.push(method.toUpperCase());
|
|
29
29
|
if (this.methods[l - 1] === 'GET') this.methods.unshift('HEAD');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// ensure middleware is a function
|
|
33
33
|
for (let i = 0; i < this.stack.length; i++) {
|
|
34
34
|
const fn = this.stack[i];
|
|
35
|
-
const type =
|
|
35
|
+
const type = typeof fn;
|
|
36
36
|
if (type !== 'function')
|
|
37
37
|
throw new Error(
|
|
38
|
-
`${methods.toString()} \`${
|
|
38
|
+
`${methods.toString()} \`${
|
|
39
|
+
this.opts.name || path
|
|
40
|
+
}\`: \`middleware\` must be a function, not \`${type}\``
|
|
39
41
|
);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
this.path = path;
|
|
43
45
|
this.regexp = pathToRegexp(path, this.paramNames, this.opts);
|
|
44
|
-
}
|
|
46
|
+
}
|
|
45
47
|
|
|
46
48
|
/**
|
|
47
49
|
* Returns whether request `path` matches route.
|
|
@@ -60,18 +62,17 @@ Layer.prototype.match = function (path) {
|
|
|
60
62
|
*
|
|
61
63
|
* @param {String} path
|
|
62
64
|
* @param {Array.<String>} captures
|
|
63
|
-
* @param {Object=}
|
|
65
|
+
* @param {Object=} params
|
|
64
66
|
* @returns {Object}
|
|
65
67
|
* @private
|
|
66
68
|
*/
|
|
67
69
|
|
|
68
|
-
Layer.prototype.params = function (path, captures,
|
|
69
|
-
const params = existingParams || {};
|
|
70
|
-
|
|
70
|
+
Layer.prototype.params = function (path, captures, params = {}) {
|
|
71
71
|
for (let len = captures.length, i = 0; i < len; i++) {
|
|
72
72
|
if (this.paramNames[i]) {
|
|
73
73
|
const c = captures[i];
|
|
74
|
-
if (c && c.length
|
|
74
|
+
if (c && c.length > 0)
|
|
75
|
+
params[this.paramNames[i].name] = c ? safeDecodeURIComponent(c) : c;
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -110,11 +111,11 @@ Layer.prototype.url = function (params, options) {
|
|
|
110
111
|
let args = params;
|
|
111
112
|
const url = this.path.replace(/\(\.\*\)/g, '');
|
|
112
113
|
|
|
113
|
-
if (typeof params
|
|
114
|
+
if (typeof params !== 'object') {
|
|
114
115
|
args = Array.prototype.slice.call(arguments);
|
|
115
|
-
if (typeof args[args.length - 1]
|
|
116
|
+
if (typeof args[args.length - 1] === 'object') {
|
|
116
117
|
options = args[args.length - 1];
|
|
117
|
-
args = args.slice(0,
|
|
118
|
+
args = args.slice(0, -1);
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
@@ -124,11 +125,11 @@ Layer.prototype.url = function (params, options) {
|
|
|
124
125
|
const tokens = parse(url);
|
|
125
126
|
let replace = {};
|
|
126
127
|
|
|
127
|
-
if (args
|
|
128
|
+
if (Array.isArray(args)) {
|
|
128
129
|
for (let len = tokens.length, i = 0, j = 0; i < len; i++) {
|
|
129
130
|
if (tokens[i].name) replace[tokens[i].name] = args[j++];
|
|
130
131
|
}
|
|
131
|
-
} else if (tokens.some(token => token.name)) {
|
|
132
|
+
} else if (tokens.some((token) => token.name)) {
|
|
132
133
|
replace = params;
|
|
133
134
|
} else if (!options) {
|
|
134
135
|
options = params;
|
|
@@ -144,6 +145,7 @@ Layer.prototype.url = function (params, options) {
|
|
|
144
145
|
replaced.search = undefined;
|
|
145
146
|
replaced.query = options.query;
|
|
146
147
|
}
|
|
148
|
+
|
|
147
149
|
return formatUrl(replaced);
|
|
148
150
|
}
|
|
149
151
|
|
|
@@ -159,7 +161,7 @@ Layer.prototype.url = function (params, options) {
|
|
|
159
161
|
* router
|
|
160
162
|
* .param('user', function (id, ctx, next) {
|
|
161
163
|
* ctx.user = users[id];
|
|
162
|
-
* if (!user) return ctx.status = 404;
|
|
164
|
+
* if (!ctx.user) return ctx.status = 404;
|
|
163
165
|
* next();
|
|
164
166
|
* })
|
|
165
167
|
* .get('/users/:user', function (ctx, next) {
|
|
@@ -174,11 +176,12 @@ Layer.prototype.url = function (params, options) {
|
|
|
174
176
|
*/
|
|
175
177
|
|
|
176
178
|
Layer.prototype.param = function (param, fn) {
|
|
177
|
-
const stack = this
|
|
179
|
+
const { stack } = this;
|
|
178
180
|
const params = this.paramNames;
|
|
179
181
|
const middleware = function (ctx, next) {
|
|
180
182
|
return fn.call(this, ctx.params[param], ctx, next);
|
|
181
183
|
};
|
|
184
|
+
|
|
182
185
|
middleware.param = param;
|
|
183
186
|
|
|
184
187
|
const names = params.map(function (p) {
|
|
@@ -212,7 +215,10 @@ Layer.prototype.param = function (param, fn) {
|
|
|
212
215
|
|
|
213
216
|
Layer.prototype.setPrefix = function (prefix) {
|
|
214
217
|
if (this.path) {
|
|
215
|
-
this.path =
|
|
218
|
+
this.path =
|
|
219
|
+
this.path !== '/' || this.opts.strict === true
|
|
220
|
+
? `${prefix}${this.path}`
|
|
221
|
+
: prefix;
|
|
216
222
|
this.paramNames = [];
|
|
217
223
|
this.regexp = pathToRegexp(this.path, this.paramNames, this.opts);
|
|
218
224
|
}
|
|
@@ -232,7 +238,7 @@ Layer.prototype.setPrefix = function (prefix) {
|
|
|
232
238
|
function safeDecodeURIComponent(text) {
|
|
233
239
|
try {
|
|
234
240
|
return decodeURIComponent(text);
|
|
235
|
-
} catch
|
|
241
|
+
} catch {
|
|
236
242
|
return text;
|
|
237
243
|
}
|
|
238
244
|
}
|
package/lib/router.js
CHANGED
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
* @link https://github.com/alexmingoia/koa-router
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const { debuglog } = require('util');
|
|
9
|
+
|
|
9
10
|
const compose = require('koa-compose');
|
|
10
11
|
const HttpError = require('http-errors');
|
|
11
12
|
const methods = require('methods');
|
|
12
|
-
const Layer = require('./layer');
|
|
13
13
|
const { pathToRegexp } = require('path-to-regexp');
|
|
14
|
+
const Layer = require('./layer');
|
|
15
|
+
|
|
16
|
+
const debug = debuglog('koa-router');
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* @module koa-router
|
|
@@ -43,14 +46,15 @@ module.exports = Router;
|
|
|
43
46
|
*
|
|
44
47
|
* @alias module:koa-router
|
|
45
48
|
* @param {Object=} opts
|
|
49
|
+
* @param {Boolean=false} opts.exclusive only run last matched route's controller when there are multiple matches
|
|
46
50
|
* @param {String=} opts.prefix prefix router paths
|
|
47
51
|
* @constructor
|
|
48
52
|
*/
|
|
49
53
|
|
|
50
|
-
function Router(opts) {
|
|
54
|
+
function Router(opts = {}) {
|
|
51
55
|
if (!(this instanceof Router)) return new Router(opts);
|
|
52
56
|
|
|
53
|
-
this.opts = opts
|
|
57
|
+
this.opts = opts;
|
|
54
58
|
this.methods = this.opts.methods || [
|
|
55
59
|
'HEAD',
|
|
56
60
|
'OPTIONS',
|
|
@@ -60,10 +64,11 @@ function Router(opts) {
|
|
|
60
64
|
'POST',
|
|
61
65
|
'DELETE'
|
|
62
66
|
];
|
|
67
|
+
this.exclusive = Boolean(this.opts.exclusive);
|
|
63
68
|
|
|
64
69
|
this.params = {};
|
|
65
70
|
this.stack = [];
|
|
66
|
-
}
|
|
71
|
+
}
|
|
67
72
|
|
|
68
73
|
/**
|
|
69
74
|
* Create `router.verb()` methods, where *verb* is one of the HTTP verbs such
|
|
@@ -72,7 +77,7 @@ function Router(opts) {
|
|
|
72
77
|
* Match URL patterns to callback functions or controller actions using `router.verb()`,
|
|
73
78
|
* where **verb** is one of the HTTP verbs such as `router.get()` or `router.post()`.
|
|
74
79
|
*
|
|
75
|
-
*
|
|
80
|
+
* Additionally, `router.all()` can be used to match against all methods.
|
|
76
81
|
*
|
|
77
82
|
* ```javascript
|
|
78
83
|
* router
|
|
@@ -186,10 +191,10 @@ function Router(opts) {
|
|
|
186
191
|
* @returns {Router}
|
|
187
192
|
*/
|
|
188
193
|
|
|
189
|
-
for (
|
|
194
|
+
for (const method_ of methods) {
|
|
190
195
|
function setMethodVerb(method) {
|
|
191
|
-
Router.prototype[method] = function(name, path, middleware) {
|
|
192
|
-
if (typeof path ===
|
|
196
|
+
Router.prototype[method] = function (name, path, middleware) {
|
|
197
|
+
if (typeof path === 'string' || path instanceof RegExp) {
|
|
193
198
|
middleware = Array.prototype.slice.call(arguments, 2);
|
|
194
199
|
} else {
|
|
195
200
|
middleware = Array.prototype.slice.call(arguments, 1);
|
|
@@ -197,17 +202,27 @@ for (let i = 0; i < methods.length; i++) {
|
|
|
197
202
|
name = null;
|
|
198
203
|
}
|
|
199
204
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
205
|
+
// Sanity check to ensure we have a viable path candidate (eg: string|regex|non-empty array)
|
|
206
|
+
if (
|
|
207
|
+
typeof path !== 'string' &&
|
|
208
|
+
!(path instanceof RegExp) &&
|
|
209
|
+
(!Array.isArray(path) || path.length === 0)
|
|
210
|
+
)
|
|
211
|
+
throw new Error(
|
|
212
|
+
`You have to provide a path when adding a ${method} handler`
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
this.register(path, [method], middleware, { name });
|
|
203
216
|
|
|
204
217
|
return this;
|
|
205
218
|
};
|
|
206
219
|
}
|
|
207
|
-
|
|
220
|
+
|
|
221
|
+
setMethodVerb(method_);
|
|
208
222
|
}
|
|
209
223
|
|
|
210
224
|
// Alias for `router.delete()` because delete is a reserved word
|
|
225
|
+
// eslint-disable-next-line dot-notation
|
|
211
226
|
Router.prototype.del = Router.prototype['delete'];
|
|
212
227
|
|
|
213
228
|
/**
|
|
@@ -247,9 +262,8 @@ Router.prototype.use = function () {
|
|
|
247
262
|
|
|
248
263
|
// support array of paths
|
|
249
264
|
if (Array.isArray(middleware[0]) && typeof middleware[0][0] === 'string') {
|
|
250
|
-
|
|
251
|
-
for (
|
|
252
|
-
const p = arrPaths[i];
|
|
265
|
+
const arrPaths = middleware[0];
|
|
266
|
+
for (const p of arrPaths) {
|
|
253
267
|
router.use.apply(router, [p].concat(middleware.slice(1)));
|
|
254
268
|
}
|
|
255
269
|
|
|
@@ -259,12 +273,15 @@ Router.prototype.use = function () {
|
|
|
259
273
|
const hasPath = typeof middleware[0] === 'string';
|
|
260
274
|
if (hasPath) path = middleware.shift();
|
|
261
275
|
|
|
262
|
-
for (
|
|
263
|
-
const m = middleware[i];
|
|
276
|
+
for (const m of middleware) {
|
|
264
277
|
if (m.router) {
|
|
265
|
-
const cloneRouter = Object.assign(
|
|
266
|
-
|
|
267
|
-
|
|
278
|
+
const cloneRouter = Object.assign(
|
|
279
|
+
Object.create(Router.prototype),
|
|
280
|
+
m.router,
|
|
281
|
+
{
|
|
282
|
+
stack: [...m.router.stack]
|
|
283
|
+
}
|
|
284
|
+
);
|
|
268
285
|
|
|
269
286
|
for (let j = 0; j < cloneRouter.stack.length; j++) {
|
|
270
287
|
const nestedLayer = cloneRouter.stack[j];
|
|
@@ -282,18 +299,21 @@ Router.prototype.use = function () {
|
|
|
282
299
|
if (router.params) {
|
|
283
300
|
function setRouterParams(paramArr) {
|
|
284
301
|
const routerParams = paramArr;
|
|
285
|
-
for (
|
|
286
|
-
const key = routerParams[j];
|
|
302
|
+
for (const key of routerParams) {
|
|
287
303
|
cloneRouter.param(key, router.params[key]);
|
|
288
304
|
}
|
|
289
305
|
}
|
|
306
|
+
|
|
290
307
|
setRouterParams(Object.keys(router.params));
|
|
291
308
|
}
|
|
292
309
|
} else {
|
|
293
310
|
const keys = [];
|
|
294
311
|
pathToRegexp(router.opts.prefix || '', keys);
|
|
295
312
|
const routerPrefixHasParam = router.opts.prefix && keys.length;
|
|
296
|
-
router.register(path || '([
|
|
313
|
+
router.register(path || '([^/]*)', [], m, {
|
|
314
|
+
end: false,
|
|
315
|
+
ignoreCaptures: !hasPath && !routerPrefixHasParam
|
|
316
|
+
});
|
|
297
317
|
}
|
|
298
318
|
}
|
|
299
319
|
|
|
@@ -335,7 +355,7 @@ Router.prototype.prefix = function (prefix) {
|
|
|
335
355
|
Router.prototype.routes = Router.prototype.middleware = function () {
|
|
336
356
|
const router = this;
|
|
337
357
|
|
|
338
|
-
|
|
358
|
+
const dispatch = function dispatch(ctx, next) {
|
|
339
359
|
debug('%s %s', ctx.method, ctx.path);
|
|
340
360
|
|
|
341
361
|
const path = router.opts.routerPath || ctx.routerPath || ctx.path;
|
|
@@ -352,23 +372,30 @@ Router.prototype.routes = Router.prototype.middleware = function () {
|
|
|
352
372
|
|
|
353
373
|
if (!matched.route) return next();
|
|
354
374
|
|
|
355
|
-
const matchedLayers = matched.pathAndMethod
|
|
356
|
-
const mostSpecificLayer = matchedLayers[matchedLayers.length - 1]
|
|
375
|
+
const matchedLayers = matched.pathAndMethod;
|
|
376
|
+
const mostSpecificLayer = matchedLayers[matchedLayers.length - 1];
|
|
357
377
|
ctx._matchedRoute = mostSpecificLayer.path;
|
|
358
378
|
if (mostSpecificLayer.name) {
|
|
359
379
|
ctx._matchedRouteName = mostSpecificLayer.name;
|
|
360
380
|
}
|
|
361
381
|
|
|
362
|
-
layerChain =
|
|
363
|
-
|
|
382
|
+
layerChain = (
|
|
383
|
+
router.exclusive ? [mostSpecificLayer] : matchedLayers
|
|
384
|
+
).reduce(function (memo, layer) {
|
|
385
|
+
memo.push(function (ctx, next) {
|
|
364
386
|
ctx.captures = layer.captures(path, ctx.captures);
|
|
365
|
-
ctx.params = ctx.request.params = layer.params(
|
|
387
|
+
ctx.params = ctx.request.params = layer.params(
|
|
388
|
+
path,
|
|
389
|
+
ctx.captures,
|
|
390
|
+
ctx.params
|
|
391
|
+
);
|
|
366
392
|
ctx.routerPath = layer.path;
|
|
367
393
|
ctx.routerName = layer.name;
|
|
368
394
|
ctx._matchedRoute = layer.path;
|
|
369
395
|
if (layer.name) {
|
|
370
396
|
ctx._matchedRouteName = layer.name;
|
|
371
397
|
}
|
|
398
|
+
|
|
372
399
|
return next();
|
|
373
400
|
});
|
|
374
401
|
return memo.concat(layer.stack);
|
|
@@ -425,12 +452,11 @@ Router.prototype.routes = Router.prototype.middleware = function () {
|
|
|
425
452
|
* @returns {Function}
|
|
426
453
|
*/
|
|
427
454
|
|
|
428
|
-
Router.prototype.allowedMethods = function (options) {
|
|
429
|
-
options = options || {};
|
|
455
|
+
Router.prototype.allowedMethods = function (options = {}) {
|
|
430
456
|
const implemented = this.methods;
|
|
431
457
|
|
|
432
458
|
return function allowedMethods(ctx, next) {
|
|
433
|
-
return next().then(function() {
|
|
459
|
+
return next().then(function () {
|
|
434
460
|
const allowed = {};
|
|
435
461
|
|
|
436
462
|
if (!ctx.status || ctx.status === 404) {
|
|
@@ -446,25 +472,27 @@ Router.prototype.allowedMethods = function (options) {
|
|
|
446
472
|
|
|
447
473
|
if (!~implemented.indexOf(ctx.method)) {
|
|
448
474
|
if (options.throw) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
475
|
+
const notImplementedThrowable =
|
|
476
|
+
typeof options.notImplemented === 'function'
|
|
477
|
+
? options.notImplemented() // set whatever the user returns from their function
|
|
478
|
+
: new HttpError.NotImplemented();
|
|
452
479
|
|
|
453
480
|
throw notImplementedThrowable;
|
|
454
481
|
} else {
|
|
455
482
|
ctx.status = 501;
|
|
456
483
|
ctx.set('Allow', allowedArr.join(', '));
|
|
457
484
|
}
|
|
458
|
-
} else if (allowedArr.length) {
|
|
485
|
+
} else if (allowedArr.length > 0) {
|
|
459
486
|
if (ctx.method === 'OPTIONS') {
|
|
460
487
|
ctx.status = 200;
|
|
461
488
|
ctx.body = '';
|
|
462
489
|
ctx.set('Allow', allowedArr.join(', '));
|
|
463
490
|
} else if (!allowed[ctx.method]) {
|
|
464
491
|
if (options.throw) {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
492
|
+
const notAllowedThrowable =
|
|
493
|
+
typeof options.methodNotAllowed === 'function'
|
|
494
|
+
? options.methodNotAllowed() // set whatever the user returns from their function
|
|
495
|
+
: new HttpError.MethodNotAllowed();
|
|
468
496
|
|
|
469
497
|
throw notAllowedThrowable;
|
|
470
498
|
} else {
|
|
@@ -486,7 +514,6 @@ Router.prototype.allowedMethods = function (options) {
|
|
|
486
514
|
* @param {Function=} middleware You may also pass multiple middleware.
|
|
487
515
|
* @param {Function} callback
|
|
488
516
|
* @returns {Router}
|
|
489
|
-
* @private
|
|
490
517
|
*/
|
|
491
518
|
|
|
492
519
|
Router.prototype.all = function (name, path, middleware) {
|
|
@@ -498,6 +525,14 @@ Router.prototype.all = function (name, path, middleware) {
|
|
|
498
525
|
name = null;
|
|
499
526
|
}
|
|
500
527
|
|
|
528
|
+
// Sanity check to ensure we have a viable path candidate (eg: string|regex|non-empty array)
|
|
529
|
+
if (
|
|
530
|
+
typeof path !== 'string' &&
|
|
531
|
+
!(path instanceof RegExp) &&
|
|
532
|
+
(!Array.isArray(path) || path.length === 0)
|
|
533
|
+
)
|
|
534
|
+
throw new Error('You have to provide a path when adding an all handler');
|
|
535
|
+
|
|
501
536
|
this.register(path, methods, middleware, { name });
|
|
502
537
|
|
|
503
538
|
return this;
|
|
@@ -529,12 +564,21 @@ Router.prototype.all = function (name, path, middleware) {
|
|
|
529
564
|
|
|
530
565
|
Router.prototype.redirect = function (source, destination, code) {
|
|
531
566
|
// lookup source route by name
|
|
532
|
-
if (source[0] !== '/')
|
|
567
|
+
if (typeof source === 'symbol' || source[0] !== '/') {
|
|
568
|
+
source = this.url(source);
|
|
569
|
+
if (source instanceof Error) throw source;
|
|
570
|
+
}
|
|
533
571
|
|
|
534
572
|
// lookup destination route by name
|
|
535
|
-
if (
|
|
573
|
+
if (
|
|
574
|
+
typeof destination === 'symbol' ||
|
|
575
|
+
(destination[0] !== '/' && !destination.includes('://'))
|
|
576
|
+
) {
|
|
577
|
+
destination = this.url(destination);
|
|
578
|
+
if (destination instanceof Error) throw destination;
|
|
579
|
+
}
|
|
536
580
|
|
|
537
|
-
return this.all(source, ctx => {
|
|
581
|
+
return this.all(source, (ctx) => {
|
|
538
582
|
ctx.redirect(destination);
|
|
539
583
|
ctx.status = code || 301;
|
|
540
584
|
});
|
|
@@ -550,16 +594,13 @@ Router.prototype.redirect = function (source, destination, code) {
|
|
|
550
594
|
* @private
|
|
551
595
|
*/
|
|
552
596
|
|
|
553
|
-
Router.prototype.register = function (path, methods, middleware, opts) {
|
|
554
|
-
opts = opts || {};
|
|
555
|
-
|
|
597
|
+
Router.prototype.register = function (path, methods, middleware, opts = {}) {
|
|
556
598
|
const router = this;
|
|
557
|
-
const stack = this
|
|
599
|
+
const { stack } = this;
|
|
558
600
|
|
|
559
601
|
// support array of paths
|
|
560
602
|
if (Array.isArray(path)) {
|
|
561
|
-
for (
|
|
562
|
-
const curPath = path[i];
|
|
603
|
+
for (const curPath of path) {
|
|
563
604
|
router.register.call(router, curPath, methods, middleware, opts);
|
|
564
605
|
}
|
|
565
606
|
|
|
@@ -572,7 +613,7 @@ Router.prototype.register = function (path, methods, middleware, opts) {
|
|
|
572
613
|
name: opts.name,
|
|
573
614
|
sensitive: opts.sensitive || this.opts.sensitive || false,
|
|
574
615
|
strict: opts.strict || this.opts.strict || false,
|
|
575
|
-
prefix: opts.prefix || this.opts.prefix ||
|
|
616
|
+
prefix: opts.prefix || this.opts.prefix || '',
|
|
576
617
|
ignoreCaptures: opts.ignoreCaptures
|
|
577
618
|
});
|
|
578
619
|
|
|
@@ -603,7 +644,7 @@ Router.prototype.register = function (path, methods, middleware, opts) {
|
|
|
603
644
|
Router.prototype.route = function (name) {
|
|
604
645
|
const routes = this.stack;
|
|
605
646
|
|
|
606
|
-
for (let len = routes.length, i=0; i<len; i++) {
|
|
647
|
+
for (let len = routes.length, i = 0; i < len; i++) {
|
|
607
648
|
if (routes[i].name && routes[i].name === name) return routes[i];
|
|
608
649
|
}
|
|
609
650
|
|
|
@@ -653,7 +694,7 @@ Router.prototype.url = function (name, params) {
|
|
|
653
694
|
return route.url.apply(route, args);
|
|
654
695
|
}
|
|
655
696
|
|
|
656
|
-
return new Error(`No route found for name: ${name}`);
|
|
697
|
+
return new Error(`No route found for name: ${String(name)}`);
|
|
657
698
|
};
|
|
658
699
|
|
|
659
700
|
/**
|
|
@@ -680,12 +721,13 @@ Router.prototype.match = function (path, method) {
|
|
|
680
721
|
|
|
681
722
|
debug('test %s %s', layer.path, layer.regexp);
|
|
682
723
|
|
|
724
|
+
// eslint-disable-next-line unicorn/prefer-regexp-test
|
|
683
725
|
if (layer.match(path)) {
|
|
684
726
|
matched.path.push(layer);
|
|
685
727
|
|
|
686
728
|
if (layer.methods.length === 0 || ~layer.methods.indexOf(method)) {
|
|
687
729
|
matched.pathAndMethod.push(layer);
|
|
688
|
-
if (layer.methods.length) matched.route = true;
|
|
730
|
+
if (layer.methods.length > 0) matched.route = true;
|
|
689
731
|
}
|
|
690
732
|
}
|
|
691
733
|
}
|
|
@@ -723,7 +765,7 @@ Router.prototype.match = function (path, method) {
|
|
|
723
765
|
* @returns {Router}
|
|
724
766
|
*/
|
|
725
767
|
|
|
726
|
-
Router.prototype.param = function(param, middleware) {
|
|
768
|
+
Router.prototype.param = function (param, middleware) {
|
|
727
769
|
this.params[param] = middleware;
|
|
728
770
|
for (let i = 0; i < this.stack.length; i++) {
|
|
729
771
|
const route = this.stack[i];
|
package/package.json
CHANGED
|
@@ -1,32 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koa/router",
|
|
3
|
-
"description": "Router middleware for koa.
|
|
4
|
-
"version": "
|
|
3
|
+
"description": "Router middleware for koa. Maintained by Forward Email and Lad.",
|
|
4
|
+
"version": "11.0.0",
|
|
5
5
|
"author": "Alex Mingoia <talk@alexmingoia.com>",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/koajs/router/issues",
|
|
8
8
|
"email": "niftylettuce@gmail.com"
|
|
9
9
|
},
|
|
10
|
+
"contributors": [
|
|
11
|
+
"Alex Mingoia <talk@alexmingoia.com>",
|
|
12
|
+
"@koajs"
|
|
13
|
+
],
|
|
10
14
|
"dependencies": {
|
|
11
|
-
"
|
|
12
|
-
"http-errors": "^1.7.3",
|
|
15
|
+
"http-errors": "^2.0.0",
|
|
13
16
|
"koa-compose": "^4.1.0",
|
|
14
17
|
"methods": "^1.1.2",
|
|
15
|
-
"path-to-regexp": "^6.1
|
|
18
|
+
"path-to-regexp": "^6.2.1"
|
|
16
19
|
},
|
|
17
20
|
"devDependencies": {
|
|
18
|
-
"@
|
|
21
|
+
"@commitlint/cli": "^17.0.3",
|
|
22
|
+
"@commitlint/config-conventional": "^17.0.3",
|
|
23
|
+
"@ladjs/env": "^3.0.0",
|
|
24
|
+
"ava": "^4.3.0",
|
|
25
|
+
"cross-env": "^7.0.3",
|
|
26
|
+
"eslint": "^8.19.0",
|
|
27
|
+
"eslint-config-xo-lass": "^2.0.1",
|
|
19
28
|
"expect.js": "^0.3.1",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
29
|
+
"fixpack": "^4.0.0",
|
|
30
|
+
"husky": "^8.0.1",
|
|
31
|
+
"jsdoc-to-markdown": "^7.1.1",
|
|
32
|
+
"koa": "^2.13.4",
|
|
33
|
+
"lint-staged": "^13.0.3",
|
|
34
|
+
"mocha": "^10.0.0",
|
|
35
|
+
"nyc": "^15.1.0",
|
|
36
|
+
"remark-cli": "^11.0.0",
|
|
37
|
+
"remark-preset-github": "^4.0.4",
|
|
24
38
|
"should": "^13.2.3",
|
|
25
|
-
"supertest": "^
|
|
26
|
-
"wrk": "^1.2.
|
|
39
|
+
"supertest": "^6.2.4",
|
|
40
|
+
"wrk": "^1.2.1",
|
|
41
|
+
"xo": "^0.50.0"
|
|
27
42
|
},
|
|
28
43
|
"engines": {
|
|
29
|
-
"node": ">=
|
|
44
|
+
"node": ">= 12"
|
|
30
45
|
},
|
|
31
46
|
"files": [
|
|
32
47
|
"lib"
|
|
@@ -45,9 +60,13 @@
|
|
|
45
60
|
"url": "https://github.com/koajs/router.git"
|
|
46
61
|
},
|
|
47
62
|
"scripts": {
|
|
63
|
+
"bench": "make -C bench",
|
|
64
|
+
"coverage": "nyc npm run test",
|
|
48
65
|
"docs": "NODE_ENV=test jsdoc2md -t ./lib/API_tpl.hbs --src ./lib/*.js >| API.md",
|
|
66
|
+
"lint": "xo --fix && remark . -qfo && fixpack",
|
|
67
|
+
"prepare": "husky install",
|
|
68
|
+
"pretest": "npm run lint",
|
|
49
69
|
"test": "mocha test/**/*.js",
|
|
50
|
-
"
|
|
51
|
-
"bench": "make -C bench"
|
|
70
|
+
"test:watch": "mocha test/**/*.js --watch"
|
|
52
71
|
}
|
|
53
72
|
}
|
package/history.md
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
9.0.0 / 2020-04-09
|
|
2
|
-
==================
|
|
3
|
-
|
|
4
|
-
- Update `path-to-regexp`. Migration path: change usage of `'*'` in routes to `(.*)` or `:splat*`.
|
|
5
|
-
- Example: `router.get('*', ....)` becomes `router.get('(.*)') ....)`
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
8.0.0 / 2019-06-16
|
|
9
|
-
==================
|
|
10
|
-
|
|
11
|
-
**others**
|
|
12
|
-
* [[`b5dd5e8`](http://github.com/koajs/koa-router/commit/b5dd5e8f00e841b7061a62ab6228cbe96a999470)] - chore: rename to @koa/router (dead-horse <<dead_horse@qq.com>>)
|
|
13
|
-
|
|
14
|
-
-------------
|
|
15
|
-
|
|
16
|
-
# Changelogs inherit from koa-router.
|
|
17
|
-
|
|
18
|
-
## 7.4.0
|
|
19
|
-
|
|
20
|
-
- Fix router.url() for multiple nested routers [#407](https://github.com/alexmingoia/koa-router/pull/407)
|
|
21
|
-
- `layer.name` added to `ctx` at `ctx.routerName` during routing [#412](https://github.com/alexmingoia/koa-router/pull/412)
|
|
22
|
-
- Router.use() was erroneously settings `(.*)` as a prefix to all routers nested with .use that did not pass an explicit prefix string as the first argument. This resulted in routes being matched that should not have been, included the running of multiple route handlers in error. [#369](https://github.com/alexmingoia/koa-router/issues/369) and [#410](https://github.com/alexmingoia/koa-router/issues/410) include information on this issue.
|
|
23
|
-
|
|
24
|
-
## 7.3.0
|
|
25
|
-
|
|
26
|
-
- Router#url() now accepts query parameters to add to generated urls [#396](https://github.com/alexmingoia/koa-router/pull/396)
|
|
27
|
-
|
|
28
|
-
## 7.2.1
|
|
29
|
-
|
|
30
|
-
- Respond to CORS preflights with 200, 0 length body [#359](https://github.com/alexmingoia/koa-router/issues/359)
|
|
31
|
-
|
|
32
|
-
## 7.2.0
|
|
33
|
-
|
|
34
|
-
- Fix a bug in Router#url and append Router object to ctx. [#350](https://github.com/alexmingoia/koa-router/pull/350)
|
|
35
|
-
- Adds `_matchedRouteName` to context [#337](https://github.com/alexmingoia/koa-router/pull/337)
|
|
36
|
-
- Respond to CORS preflights with 200, 0 length body [#359](https://github.com/alexmingoia/koa-router/issues/359)
|
|
37
|
-
|
|
38
|
-
## 7.1.1
|
|
39
|
-
|
|
40
|
-
- Fix bug where param handlers were run out of order [#282](https://github.com/alexmingoia/koa-router/pull/282)
|
|
41
|
-
|
|
42
|
-
## 7.1.0
|
|
43
|
-
|
|
44
|
-
- Backports: merge 5.4 work into the 7.x upstream. See 5.4.0 updates for more details.
|
|
45
|
-
|
|
46
|
-
## 7.0.1
|
|
47
|
-
|
|
48
|
-
- Fix: allowedMethods should be ctx.method not this.method [#215](https://github.com/alexmingoia/koa-router/pull/215)
|
|
49
|
-
|
|
50
|
-
## 7.0.0
|
|
51
|
-
|
|
52
|
-
- The API has changed to match the new promise-based middleware
|
|
53
|
-
signature of koa 2. See the
|
|
54
|
-
[koa 2.x readme](https://github.com/koajs/koa/tree/2.0.0-alpha.3) for more
|
|
55
|
-
information.
|
|
56
|
-
- Middleware is now always run in the order declared by `.use()` (or `.get()`,
|
|
57
|
-
etc.), which matches Express 4 API.
|
|
58
|
-
|
|
59
|
-
## 5.4.0
|
|
60
|
-
|
|
61
|
-
- Expose matched route at `ctx._matchedRoute`.
|
|
62
|
-
|
|
63
|
-
## 5.3.0
|
|
64
|
-
|
|
65
|
-
- Register multiple routes with array of paths [#203](https://github.com/alexmingoia/koa-router/issue/143).
|
|
66
|
-
- Improved router.url() [#143](https://github.com/alexmingoia/koa-router/pull/143)
|
|
67
|
-
- Adds support for named routes and regular expressions
|
|
68
|
-
[#152](https://github.com/alexmingoia/koa-router/pull/152)
|
|
69
|
-
- Add support for custom throw functions for 405 and 501 responses [#206](https://github.com/alexmingoia/koa-router/pull/206)
|
|
70
|
-
|
|
71
|
-
## 5.2.3
|
|
72
|
-
|
|
73
|
-
- Fix for middleware running twice when nesting routes [#184](https://github.com/alexmingoia/koa-router/issues/184)
|
|
74
|
-
|
|
75
|
-
## 5.2.2
|
|
76
|
-
|
|
77
|
-
- Register routes without params before those with params [#183](https://github.com/alexmingoia/koa-router/pull/183)
|
|
78
|
-
- Fix for allowed methods [#182](https://github.com/alexmingoia/koa-router/issues/182)
|
|
79
|
-
|
|
80
|
-
## 5.2.0
|
|
81
|
-
|
|
82
|
-
- Add support for async/await. Resolves [#130](https://github.com/alexmingoia/koa-router/issues/130).
|
|
83
|
-
- Add support for array of paths by Router#use(). Resolves [#175](https://github.com/alexmingoia/koa-router/issues/175).
|
|
84
|
-
- Inherit param middleware when nesting routers. Fixes [#170](https://github.com/alexmingoia/koa-router/issues/170).
|
|
85
|
-
- Default router middleware without path to root. Fixes [#161](https://github.com/alexmingoia/koa-router/issues/161), [#155](https://github.com/alexmingoia/koa-router/issues/155), [#156](https://github.com/alexmingoia/koa-router/issues/156).
|
|
86
|
-
- Run nested router middleware after parent's. Fixes [#156](https://github.com/alexmingoia/koa-router/issues/156).
|
|
87
|
-
- Remove dependency on koa-compose.
|
|
88
|
-
|
|
89
|
-
## 5.1.1
|
|
90
|
-
|
|
91
|
-
- Match routes in order they were defined. Fixes #131.
|
|
92
|
-
|
|
93
|
-
## 5.1.0
|
|
94
|
-
|
|
95
|
-
- Support mounting router middleware at a given path.
|
|
96
|
-
|
|
97
|
-
## 5.0.1
|
|
98
|
-
|
|
99
|
-
- Fix bug with missing parameters when nesting routers.
|
|
100
|
-
|
|
101
|
-
## 5.0.0
|
|
102
|
-
|
|
103
|
-
- Remove confusing API for extending koa app with router methods. Router#use()
|
|
104
|
-
does not have the same behavior as app#use().
|
|
105
|
-
- Add support for nesting routes.
|
|
106
|
-
- Remove support for regular expression routes to achieve nestable routers and
|
|
107
|
-
enable future trie-based routing optimizations.
|
|
108
|
-
|
|
109
|
-
## 4.3.2
|
|
110
|
-
|
|
111
|
-
- Do not send 405 if route matched but status is 404. Fixes #112, closes #114.
|
|
112
|
-
|
|
113
|
-
## 4.3.1
|
|
114
|
-
|
|
115
|
-
- Do not run middleware if not yielded to by previous middleware. Fixes #115.
|
|
116
|
-
|
|
117
|
-
## 4.3.0
|
|
118
|
-
|
|
119
|
-
- Add support for router prefixes.
|
|
120
|
-
- Add MIT license.
|
|
121
|
-
|
|
122
|
-
## 4.2.0
|
|
123
|
-
|
|
124
|
-
- Fixed issue with router middleware being applied even if no route was
|
|
125
|
-
matched.
|
|
126
|
-
- Router.url - new static method to generate url from url pattern and data
|
|
127
|
-
|
|
128
|
-
## 4.1.0
|
|
129
|
-
|
|
130
|
-
Private API changed to separate context parameter decoration from route
|
|
131
|
-
matching. `Router#match` and `Route#match` are now pure functions that return
|
|
132
|
-
an array of routes that match the URL path.
|
|
133
|
-
|
|
134
|
-
For modules using this private API that need to determine if a method and path
|
|
135
|
-
match a route, `route.methods` must be checked against the routes returned from
|
|
136
|
-
`router.match()`:
|
|
137
|
-
|
|
138
|
-
```javascript
|
|
139
|
-
var matchedRoute = router.match(path).filter(function (route) {
|
|
140
|
-
return ~route.methods.indexOf(method);
|
|
141
|
-
}).shift();
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## 4.0.0
|
|
145
|
-
|
|
146
|
-
405, 501, and OPTIONS response handling was moved into separate middleware
|
|
147
|
-
`router.allowedMethods()`. This resolves incorrect 501 or 405 responses when
|
|
148
|
-
using multiple routers.
|
|
149
|
-
|
|
150
|
-
### Breaking changes
|
|
151
|
-
|
|
152
|
-
4.x is mostly backwards compatible with 3.x, except for the following:
|
|
153
|
-
|
|
154
|
-
- Instantiating a router with `new` and `app` returns the router instance,
|
|
155
|
-
whereas 3.x returns the router middleware. When creating a router in 4.x, the
|
|
156
|
-
only time router middleware is returned is when creating using the
|
|
157
|
-
`Router(app)` signature (with `app` and without `new`).
|