@koa/router 14.0.0 → 15.1.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/dist/index.js ADDED
@@ -0,0 +1,1450 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ Router: () => RouterExport,
34
+ default: () => router_default
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/router.ts
39
+ var import_koa_compose = __toESM(require("koa-compose"));
40
+ var import_http_errors = __toESM(require("http-errors"));
41
+
42
+ // src/layer.ts
43
+ var import_node_url = require("url");
44
+
45
+ // src/utils/path-to-regexp-wrapper.ts
46
+ var import_path_to_regexp = require("path-to-regexp");
47
+ function compilePathToRegexp(path, options = {}) {
48
+ const normalizedOptions = { ...options };
49
+ if ("strict" in normalizedOptions && !("trailing" in normalizedOptions)) {
50
+ normalizedOptions.trailing = normalizedOptions.strict !== true;
51
+ delete normalizedOptions.strict;
52
+ }
53
+ delete normalizedOptions.pathAsRegExp;
54
+ delete normalizedOptions.ignoreCaptures;
55
+ delete normalizedOptions.prefix;
56
+ const { regexp, keys } = (0, import_path_to_regexp.pathToRegexp)(path, normalizedOptions);
57
+ return { regexp, keys };
58
+ }
59
+ function compilePath(path, options = {}) {
60
+ return (0, import_path_to_regexp.compile)(path, options);
61
+ }
62
+ function parsePath(path, options) {
63
+ return (0, import_path_to_regexp.parse)(path, options);
64
+ }
65
+ function normalizeLayerOptionsToPathToRegexp(options = {}) {
66
+ const normalized = {
67
+ sensitive: options.sensitive,
68
+ end: options.end,
69
+ strict: options.strict,
70
+ trailing: options.trailing
71
+ };
72
+ if ("strict" in normalized && !("trailing" in normalized)) {
73
+ normalized.trailing = normalized.strict !== true;
74
+ delete normalized.strict;
75
+ }
76
+ for (const key of Object.keys(normalized)) {
77
+ if (normalized[key] === void 0) {
78
+ delete normalized[key];
79
+ }
80
+ }
81
+ return normalized;
82
+ }
83
+
84
+ // src/utils/safe-decode-uri-components.ts
85
+ function safeDecodeURIComponent(text) {
86
+ try {
87
+ return decodeURIComponent(text);
88
+ } catch {
89
+ return text;
90
+ }
91
+ }
92
+
93
+ // src/layer.ts
94
+ var Layer = class {
95
+ opts;
96
+ name;
97
+ methods;
98
+ paramNames;
99
+ stack;
100
+ path;
101
+ regexp;
102
+ /**
103
+ * Initialize a new routing Layer with given `method`, `path`, and `middleware`.
104
+ *
105
+ * @param path - Path string or regular expression
106
+ * @param methods - Array of HTTP verbs
107
+ * @param middleware - Layer callback/middleware or series of
108
+ * @param opts - Layer options
109
+ * @private
110
+ */
111
+ constructor(path, methods, middleware, options = {}) {
112
+ this.opts = options;
113
+ this.name = this.opts.name || void 0;
114
+ this.methods = this._normalizeHttpMethods(methods);
115
+ this.stack = this._normalizeAndValidateMiddleware(
116
+ middleware,
117
+ methods,
118
+ path
119
+ );
120
+ this.path = path;
121
+ this.paramNames = [];
122
+ this._configurePathMatching();
123
+ }
124
+ /**
125
+ * Normalize HTTP methods and add automatic HEAD support for GET
126
+ * @private
127
+ */
128
+ _normalizeHttpMethods(methods) {
129
+ const normalizedMethods = [];
130
+ for (const method of methods) {
131
+ const upperMethod = method.toUpperCase();
132
+ normalizedMethods.push(upperMethod);
133
+ if (upperMethod === "GET") {
134
+ normalizedMethods.unshift("HEAD");
135
+ }
136
+ }
137
+ return normalizedMethods;
138
+ }
139
+ /**
140
+ * Normalize middleware to array and validate all are functions
141
+ * @private
142
+ */
143
+ _normalizeAndValidateMiddleware(middleware, methods, path) {
144
+ const middlewareArray = Array.isArray(middleware) ? middleware : [middleware];
145
+ for (const middlewareFunction of middlewareArray) {
146
+ const middlewareType = typeof middlewareFunction;
147
+ if (middlewareType !== "function") {
148
+ const routeIdentifier = this.opts.name || path;
149
+ throw new Error(
150
+ `${methods.toString()} \`${routeIdentifier}\`: \`middleware\` must be a function, not \`${middlewareType}\``
151
+ );
152
+ }
153
+ }
154
+ return middlewareArray;
155
+ }
156
+ /**
157
+ * Configure path matching regexp and parameters
158
+ * @private
159
+ */
160
+ _configurePathMatching() {
161
+ if (this.opts.pathAsRegExp === true) {
162
+ this.regexp = this.path instanceof RegExp ? this.path : new RegExp(this.path);
163
+ } else if (this.path) {
164
+ this._configurePathToRegexp();
165
+ }
166
+ }
167
+ /**
168
+ * Configure path-to-regexp for string paths
169
+ * @private
170
+ */
171
+ _configurePathToRegexp() {
172
+ const options = normalizeLayerOptionsToPathToRegexp(this.opts);
173
+ const { regexp, keys } = compilePathToRegexp(this.path, options);
174
+ this.regexp = regexp;
175
+ this.paramNames = keys;
176
+ }
177
+ /**
178
+ * Returns whether request `path` matches route.
179
+ *
180
+ * @param path - Request path
181
+ * @returns Whether path matches
182
+ * @private
183
+ */
184
+ match(path) {
185
+ return this.regexp.test(path);
186
+ }
187
+ /**
188
+ * Returns map of URL parameters for given `path` and `paramNames`.
189
+ *
190
+ * @param _path - Request path (not used, kept for API compatibility)
191
+ * @param captures - Captured values from regexp
192
+ * @param existingParams - Existing params to merge with
193
+ * @returns Parameter map
194
+ * @private
195
+ */
196
+ params(_path, captures, existingParameters = {}) {
197
+ const parameterValues = { ...existingParameters };
198
+ for (const [captureIndex, capturedValue] of captures.entries()) {
199
+ const parameterDefinition = this.paramNames[captureIndex];
200
+ if (parameterDefinition && capturedValue && capturedValue.length > 0) {
201
+ const parameterName = parameterDefinition.name;
202
+ parameterValues[parameterName] = safeDecodeURIComponent(capturedValue);
203
+ }
204
+ }
205
+ return parameterValues;
206
+ }
207
+ /**
208
+ * Returns array of regexp url path captures.
209
+ *
210
+ * @param path - Request path
211
+ * @returns Array of captured values
212
+ * @private
213
+ */
214
+ captures(path) {
215
+ if (this.opts.ignoreCaptures) {
216
+ return [];
217
+ }
218
+ const match = path.match(this.regexp);
219
+ return match ? match.slice(1) : [];
220
+ }
221
+ /**
222
+ * Generate URL for route using given `params`.
223
+ *
224
+ * @example
225
+ *
226
+ * ```javascript
227
+ * const route = new Layer('/users/:id', ['GET'], fn);
228
+ *
229
+ * route.url({ id: 123 }); // => "/users/123"
230
+ * ```
231
+ *
232
+ * @param args - URL parameters (various formats supported)
233
+ * @returns Generated URL
234
+ * @throws Error if route path is a RegExp (cannot generate URL from RegExp)
235
+ * @private
236
+ */
237
+ url(...arguments_) {
238
+ if (this.path instanceof RegExp) {
239
+ throw new TypeError(
240
+ "Cannot generate URL for routes defined with RegExp paths. Use string paths with named parameters instead."
241
+ );
242
+ }
243
+ const { params, options } = this._parseUrlArguments(arguments_);
244
+ const cleanPath = this.path.replaceAll("(.*)", "");
245
+ const pathCompiler = compilePath(cleanPath, {
246
+ encode: encodeURIComponent,
247
+ ...options
248
+ });
249
+ const parameterReplacements = this._buildParamReplacements(
250
+ params,
251
+ cleanPath
252
+ );
253
+ const generatedUrl = pathCompiler(parameterReplacements);
254
+ if (options && options.query) {
255
+ return this._addQueryString(generatedUrl, options.query);
256
+ }
257
+ return generatedUrl;
258
+ }
259
+ /**
260
+ * Parse url() arguments into params and options
261
+ * Supports multiple call signatures:
262
+ * - url({ id: 1 })
263
+ * - url(1, 2, 3)
264
+ * - url({ query: {...} })
265
+ * - url({ id: 1 }, { query: {...} })
266
+ * @private
267
+ */
268
+ _parseUrlArguments(allArguments) {
269
+ let parameters = allArguments[0] ?? {};
270
+ let options = allArguments[1];
271
+ if (typeof parameters !== "object" || parameters === null) {
272
+ const argumentsList = [...allArguments];
273
+ const lastArgument = argumentsList.at(-1);
274
+ if (typeof lastArgument === "object" && lastArgument !== null) {
275
+ options = lastArgument;
276
+ parameters = argumentsList.slice(0, -1);
277
+ } else {
278
+ parameters = argumentsList;
279
+ }
280
+ } else if (parameters && !options) {
281
+ const parameterKeys = Object.keys(parameters);
282
+ const isOnlyOptions = parameterKeys.length === 1 && parameterKeys[0] === "query";
283
+ if (isOnlyOptions) {
284
+ options = parameters;
285
+ parameters = {};
286
+ } else if ("query" in parameters && parameters.query) {
287
+ const { query, ...restParameters } = parameters;
288
+ options = { query };
289
+ parameters = restParameters;
290
+ }
291
+ }
292
+ return { params: parameters, options };
293
+ }
294
+ /**
295
+ * Build parameter replacements for URL generation
296
+ * @private
297
+ */
298
+ _buildParamReplacements(parameters, cleanPath) {
299
+ const { tokens } = parsePath(cleanPath);
300
+ const hasNamedParameters = tokens.some(
301
+ (token) => "name" in token && token.name
302
+ );
303
+ const parameterReplacements = {};
304
+ if (Array.isArray(parameters)) {
305
+ let parameterIndex = 0;
306
+ for (const token of tokens) {
307
+ if ("name" in token && token.name) {
308
+ parameterReplacements[token.name] = String(
309
+ parameters[parameterIndex++]
310
+ );
311
+ }
312
+ }
313
+ } else if (hasNamedParameters && typeof parameters === "object" && !("query" in parameters)) {
314
+ for (const [parameterName, parameterValue] of Object.entries(
315
+ parameters
316
+ )) {
317
+ parameterReplacements[parameterName] = String(parameterValue);
318
+ }
319
+ }
320
+ return parameterReplacements;
321
+ }
322
+ /**
323
+ * Add query string to URL
324
+ * @private
325
+ */
326
+ _addQueryString(baseUrl, query) {
327
+ const parsed = (0, import_node_url.parse)(baseUrl);
328
+ const urlObject = {
329
+ ...parsed,
330
+ query: parsed.query ?? void 0
331
+ };
332
+ if (typeof query === "string") {
333
+ urlObject.search = query;
334
+ urlObject.query = void 0;
335
+ } else {
336
+ urlObject.search = void 0;
337
+ urlObject.query = query;
338
+ }
339
+ return (0, import_node_url.format)(urlObject);
340
+ }
341
+ /**
342
+ * Run validations on route named parameters.
343
+ *
344
+ * @example
345
+ *
346
+ * ```javascript
347
+ * router
348
+ * .param('user', function (id, ctx, next) {
349
+ * ctx.user = users[id];
350
+ * if (!ctx.user) return ctx.status = 404;
351
+ * next();
352
+ * })
353
+ * .get('/users/:user', function (ctx, next) {
354
+ * ctx.body = ctx.user;
355
+ * });
356
+ * ```
357
+ *
358
+ * @param paramName - Parameter name
359
+ * @param paramHandler - Middleware function
360
+ * @returns This layer instance
361
+ * @private
362
+ */
363
+ param(parameterName, parameterHandler) {
364
+ const middlewareStack = this.stack;
365
+ const routeParameterNames = this.paramNames;
366
+ const parameterMiddleware = this._createParamMiddleware(
367
+ parameterName,
368
+ parameterHandler
369
+ );
370
+ const parameterNamesList = routeParameterNames.map(
371
+ (parameterDefinition) => parameterDefinition.name
372
+ );
373
+ const parameterPosition = parameterNamesList.indexOf(parameterName);
374
+ if (parameterPosition !== -1) {
375
+ this._insertParamMiddleware(
376
+ middlewareStack,
377
+ parameterMiddleware,
378
+ parameterNamesList,
379
+ parameterPosition
380
+ );
381
+ }
382
+ return this;
383
+ }
384
+ /**
385
+ * Create param middleware with deduplication tracking
386
+ * @private
387
+ */
388
+ _createParamMiddleware(parameterName, parameterHandler) {
389
+ const middleware = ((context, next) => {
390
+ if (!context._matchedParams) {
391
+ context._matchedParams = /* @__PURE__ */ new WeakMap();
392
+ }
393
+ if (context._matchedParams.has(parameterHandler)) {
394
+ return next();
395
+ }
396
+ context._matchedParams.set(parameterHandler, true);
397
+ return parameterHandler(context.params[parameterName], context, next);
398
+ });
399
+ middleware.param = parameterName;
400
+ middleware._originalFn = parameterHandler;
401
+ return middleware;
402
+ }
403
+ /**
404
+ * Insert param middleware at the correct position in the stack
405
+ * @private
406
+ */
407
+ _insertParamMiddleware(middlewareStack, parameterMiddleware, parameterNamesList, currentParameterPosition) {
408
+ let inserted = false;
409
+ for (let stackIndex = 0; stackIndex < middlewareStack.length; stackIndex++) {
410
+ const existingMiddleware = middlewareStack[stackIndex];
411
+ if (!existingMiddleware.param) {
412
+ middlewareStack.splice(stackIndex, 0, parameterMiddleware);
413
+ inserted = true;
414
+ break;
415
+ }
416
+ const existingParameterPosition = parameterNamesList.indexOf(
417
+ existingMiddleware.param
418
+ );
419
+ if (existingParameterPosition > currentParameterPosition) {
420
+ middlewareStack.splice(stackIndex, 0, parameterMiddleware);
421
+ inserted = true;
422
+ break;
423
+ }
424
+ }
425
+ if (!inserted) {
426
+ middlewareStack.push(parameterMiddleware);
427
+ }
428
+ }
429
+ /**
430
+ * Prefix route path.
431
+ *
432
+ * @param prefixPath - Prefix to prepend
433
+ * @returns This layer instance
434
+ * @private
435
+ */
436
+ setPrefix(prefixPath) {
437
+ if (!this.path) {
438
+ return this;
439
+ }
440
+ if (this.path instanceof RegExp) {
441
+ return this;
442
+ }
443
+ this.path = this._applyPrefix(prefixPath);
444
+ this._reconfigurePathMatching(prefixPath);
445
+ return this;
446
+ }
447
+ /**
448
+ * Apply prefix to the current path
449
+ * @private
450
+ */
451
+ _applyPrefix(prefixPath) {
452
+ const isRootPath = this.path === "/";
453
+ const isStrictMode = this.opts.strict === true;
454
+ const prefixHasParameters = prefixPath.includes(":");
455
+ const pathIsRawRegex = this.opts.pathAsRegExp === true && typeof this.path === "string";
456
+ if (prefixHasParameters && pathIsRawRegex) {
457
+ const currentPath = this.path;
458
+ if (currentPath === String.raw`(?:\/|$)` || currentPath === String.raw`(?:\\\/|$)`) {
459
+ this.path = "{/*rest}";
460
+ this.opts.pathAsRegExp = false;
461
+ }
462
+ }
463
+ if (isRootPath && !isStrictMode) {
464
+ return prefixPath;
465
+ }
466
+ return `${prefixPath}${this.path}`;
467
+ }
468
+ /**
469
+ * Reconfigure path matching after prefix is applied
470
+ * @private
471
+ */
472
+ _reconfigurePathMatching(prefixPath) {
473
+ const treatAsRegExp = this.opts.pathAsRegExp === true;
474
+ const prefixHasParameters = prefixPath && prefixPath.includes(":");
475
+ if (prefixHasParameters && treatAsRegExp) {
476
+ const options = normalizeLayerOptionsToPathToRegexp(this.opts);
477
+ const { regexp, keys } = compilePathToRegexp(
478
+ this.path,
479
+ options
480
+ );
481
+ this.regexp = regexp;
482
+ this.paramNames = keys;
483
+ this.opts.pathAsRegExp = false;
484
+ } else if (treatAsRegExp) {
485
+ const pathString = this.path;
486
+ const anchoredPattern = pathString.startsWith("^") ? pathString : `^${pathString}`;
487
+ this.regexp = this.path instanceof RegExp ? this.path : new RegExp(anchoredPattern);
488
+ } else {
489
+ const options = normalizeLayerOptionsToPathToRegexp(this.opts);
490
+ const { regexp, keys } = compilePathToRegexp(
491
+ this.path,
492
+ options
493
+ );
494
+ this.regexp = regexp;
495
+ this.paramNames = keys;
496
+ }
497
+ }
498
+ };
499
+
500
+ // src/utils/http-methods.ts
501
+ var import_node_http = __toESM(require("http"));
502
+ function getAllHttpMethods() {
503
+ return import_node_http.default.METHODS.map((method) => method.toLowerCase());
504
+ }
505
+ var COMMON_HTTP_METHODS = [
506
+ "get",
507
+ "post",
508
+ "put",
509
+ "patch",
510
+ "delete",
511
+ "del",
512
+ "head",
513
+ "options"
514
+ ];
515
+
516
+ // src/utils/parameter-helpers.ts
517
+ function normalizeParameterMiddleware(parameterMiddleware) {
518
+ if (!parameterMiddleware) {
519
+ return [];
520
+ }
521
+ if (Array.isArray(parameterMiddleware)) {
522
+ return parameterMiddleware;
523
+ }
524
+ return [parameterMiddleware];
525
+ }
526
+ function applyParameterMiddlewareToRoute(route, parameterName, parameterMiddleware) {
527
+ const middlewareList = normalizeParameterMiddleware(
528
+ parameterMiddleware
529
+ );
530
+ for (const middleware of middlewareList) {
531
+ route.param(parameterName, middleware);
532
+ }
533
+ }
534
+ function applyAllParameterMiddleware(route, parametersObject) {
535
+ const parameterNames = Object.keys(parametersObject);
536
+ for (const parameterName of parameterNames) {
537
+ const parameterMiddleware = parametersObject[parameterName];
538
+ applyParameterMiddlewareToRoute(
539
+ route,
540
+ parameterName,
541
+ parameterMiddleware
542
+ );
543
+ }
544
+ }
545
+
546
+ // src/utils/path-helpers.ts
547
+ function hasPathParameters(path, options = {}) {
548
+ if (!path) {
549
+ return false;
550
+ }
551
+ const { keys } = compilePathToRegexp(path, options);
552
+ return keys.length > 0;
553
+ }
554
+ function determineMiddlewarePath(explicitPath, hasPrefixParameters) {
555
+ if (explicitPath !== void 0) {
556
+ if (typeof explicitPath === "string") {
557
+ if (explicitPath === "") {
558
+ return {
559
+ path: "{/*rest}",
560
+ pathAsRegExp: false
561
+ };
562
+ }
563
+ if (explicitPath === "/") {
564
+ return {
565
+ path: "/",
566
+ pathAsRegExp: false
567
+ };
568
+ }
569
+ return {
570
+ path: explicitPath,
571
+ pathAsRegExp: false
572
+ };
573
+ }
574
+ return {
575
+ path: explicitPath,
576
+ pathAsRegExp: true
577
+ };
578
+ }
579
+ if (hasPrefixParameters) {
580
+ return {
581
+ path: "{/*rest}",
582
+ pathAsRegExp: false
583
+ };
584
+ }
585
+ return {
586
+ path: String.raw`(?:\/|$)`,
587
+ pathAsRegExp: true
588
+ };
589
+ }
590
+
591
+ // src/utils/debug.ts
592
+ var import_debug = __toESM(require("debug"));
593
+ var debug = (0, import_debug.default)("koa-router");
594
+
595
+ // src/router.ts
596
+ var httpMethods = getAllHttpMethods();
597
+ var Router = class {
598
+ opts;
599
+ methods;
600
+ exclusive;
601
+ params;
602
+ stack;
603
+ host;
604
+ /**
605
+ * Create a new router.
606
+ *
607
+ * @example
608
+ *
609
+ * Basic usage:
610
+ *
611
+ * ```javascript
612
+ * const Koa = require('koa');
613
+ * const Router = require('@koa/router');
614
+ *
615
+ * const app = new Koa();
616
+ * const router = new Router();
617
+ *
618
+ * router.get('/', (ctx, next) => {
619
+ * // ctx.router available
620
+ * });
621
+ *
622
+ * app
623
+ * .use(router.routes())
624
+ * .use(router.allowedMethods());
625
+ * ```
626
+ *
627
+ * @alias module:koa-router
628
+ * @param opts - Router options
629
+ * @constructor
630
+ */
631
+ constructor(options = {}) {
632
+ this.opts = options;
633
+ this.methods = this.opts.methods || [
634
+ "HEAD",
635
+ "OPTIONS",
636
+ "GET",
637
+ "PUT",
638
+ "PATCH",
639
+ "POST",
640
+ "DELETE"
641
+ ];
642
+ this.exclusive = Boolean(this.opts.exclusive);
643
+ this.params = {};
644
+ this.stack = [];
645
+ this.host = this.opts.host;
646
+ }
647
+ /**
648
+ * Generate URL from url pattern and given `params`.
649
+ *
650
+ * @example
651
+ *
652
+ * ```javascript
653
+ * const url = Router.url('/users/:id', {id: 1});
654
+ * // => "/users/1"
655
+ * ```
656
+ *
657
+ * @param path - URL pattern
658
+ * @param args - URL parameters
659
+ * @returns Generated URL
660
+ */
661
+ static url(path, ...arguments_) {
662
+ const temporaryLayer = new Layer(path, [], () => {
663
+ });
664
+ return temporaryLayer.url(...arguments_);
665
+ }
666
+ use(...middleware) {
667
+ let explicitPath;
668
+ if (this._isPathArray(middleware[0])) {
669
+ return this._useWithPathArray(middleware);
670
+ }
671
+ const hasExplicitPath = this._hasExplicitPath(middleware[0]);
672
+ if (hasExplicitPath) {
673
+ explicitPath = middleware.shift();
674
+ }
675
+ if (middleware.length === 0) {
676
+ throw new Error(
677
+ "You must provide at least one middleware function to router.use()"
678
+ );
679
+ }
680
+ for (const currentMiddleware of middleware) {
681
+ if (this._isNestedRouter(currentMiddleware)) {
682
+ this._mountNestedRouter(
683
+ currentMiddleware,
684
+ explicitPath
685
+ );
686
+ } else {
687
+ this._registerMiddleware(
688
+ currentMiddleware,
689
+ explicitPath,
690
+ hasExplicitPath
691
+ );
692
+ }
693
+ }
694
+ return this;
695
+ }
696
+ /**
697
+ * Check if first argument is an array of paths (all elements must be strings)
698
+ * @private
699
+ */
700
+ _isPathArray(firstArgument) {
701
+ return Array.isArray(firstArgument) && firstArgument.length > 0 && firstArgument.every((item) => typeof item === "string");
702
+ }
703
+ /**
704
+ * Check if first argument is an explicit path (string or RegExp)
705
+ * Empty string counts as explicit path to enable param capture
706
+ * @private
707
+ */
708
+ _hasExplicitPath(firstArgument) {
709
+ return typeof firstArgument === "string" || firstArgument instanceof RegExp;
710
+ }
711
+ /**
712
+ * Check if middleware contains a nested router
713
+ * @private
714
+ */
715
+ _isNestedRouter(middleware) {
716
+ return typeof middleware === "function" && "router" in middleware && middleware.router !== void 0;
717
+ }
718
+ /**
719
+ * Apply middleware to multiple paths
720
+ * @private
721
+ */
722
+ _useWithPathArray(middleware) {
723
+ const pathArray = middleware[0];
724
+ const remainingMiddleware = middleware.slice(1);
725
+ for (const singlePath of pathArray) {
726
+ Reflect.apply(this.use, this, [singlePath, ...remainingMiddleware]);
727
+ }
728
+ return this;
729
+ }
730
+ /**
731
+ * Mount a nested router
732
+ * @private
733
+ */
734
+ _mountNestedRouter(middlewareWithRouter, mountPath) {
735
+ const nestedRouter = middlewareWithRouter.router;
736
+ const clonedRouter = this._cloneRouter(nestedRouter);
737
+ const mountPathHasParameters = mountPath && typeof mountPath === "string" && hasPathParameters(mountPath, this.opts);
738
+ for (let routeIndex = 0; routeIndex < clonedRouter.stack.length; routeIndex++) {
739
+ const nestedLayer = clonedRouter.stack[routeIndex];
740
+ const clonedLayer = this._cloneLayer(nestedLayer);
741
+ if (mountPath && typeof mountPath === "string") {
742
+ clonedLayer.setPrefix(mountPath);
743
+ }
744
+ if (this.opts.prefix) {
745
+ clonedLayer.setPrefix(this.opts.prefix);
746
+ }
747
+ if (clonedLayer.methods.length === 0 && mountPathHasParameters) {
748
+ clonedLayer.opts.ignoreCaptures = false;
749
+ }
750
+ this.stack.push(clonedLayer);
751
+ clonedRouter.stack[routeIndex] = clonedLayer;
752
+ }
753
+ if (this.params) {
754
+ this._applyParamMiddlewareToRouter(clonedRouter);
755
+ }
756
+ }
757
+ /**
758
+ * Clone a router instance
759
+ * @private
760
+ */
761
+ _cloneRouter(sourceRouter) {
762
+ return Object.assign(
763
+ Object.create(Object.getPrototypeOf(sourceRouter)),
764
+ sourceRouter,
765
+ {
766
+ stack: [...sourceRouter.stack]
767
+ }
768
+ );
769
+ }
770
+ /**
771
+ * Clone a layer instance (deep clone to avoid shared references)
772
+ * @private
773
+ */
774
+ _cloneLayer(sourceLayer) {
775
+ const cloned = Object.assign(
776
+ Object.create(Object.getPrototypeOf(sourceLayer)),
777
+ sourceLayer,
778
+ {
779
+ // Deep clone arrays and objects to avoid shared references
780
+ stack: [...sourceLayer.stack],
781
+ methods: [...sourceLayer.methods],
782
+ paramNames: [...sourceLayer.paramNames],
783
+ opts: { ...sourceLayer.opts }
784
+ }
785
+ );
786
+ return cloned;
787
+ }
788
+ /**
789
+ * Apply this router's param middleware to a nested router
790
+ * @private
791
+ */
792
+ _applyParamMiddlewareToRouter(targetRouter) {
793
+ const parameterNames = Object.keys(this.params);
794
+ for (const parameterName of parameterNames) {
795
+ const parameterMiddleware = this.params[parameterName];
796
+ applyParameterMiddlewareToRoute(
797
+ targetRouter,
798
+ parameterName,
799
+ parameterMiddleware
800
+ );
801
+ }
802
+ }
803
+ /**
804
+ * Register regular middleware (not nested router)
805
+ * @private
806
+ */
807
+ _registerMiddleware(middleware, explicitPath, hasExplicitPath) {
808
+ const prefixHasParameters = hasPathParameters(
809
+ this.opts.prefix || "",
810
+ this.opts
811
+ );
812
+ const effectiveExplicitPath = (() => {
813
+ if (explicitPath !== void 0) return explicitPath;
814
+ if (prefixHasParameters) return "";
815
+ return;
816
+ })();
817
+ const effectiveHasExplicitPath = hasExplicitPath || explicitPath === void 0 && prefixHasParameters;
818
+ const { path: middlewarePath, pathAsRegExp } = determineMiddlewarePath(
819
+ effectiveExplicitPath,
820
+ prefixHasParameters
821
+ );
822
+ let finalPath = middlewarePath;
823
+ let usePathToRegexp = pathAsRegExp;
824
+ const isRootPath = effectiveHasExplicitPath && middlewarePath === "/";
825
+ if (effectiveHasExplicitPath && typeof middlewarePath === "string") {
826
+ finalPath = middlewarePath;
827
+ usePathToRegexp = false;
828
+ }
829
+ this.register(finalPath, [], middleware, {
830
+ end: isRootPath,
831
+ ignoreCaptures: !effectiveHasExplicitPath && !prefixHasParameters,
832
+ pathAsRegExp: usePathToRegexp
833
+ });
834
+ }
835
+ /**
836
+ * Set the path prefix for a Router instance that was already initialized.
837
+ * Note: Calling this method multiple times will replace the prefix, not stack them.
838
+ *
839
+ * @example
840
+ *
841
+ * ```javascript
842
+ * router.prefix('/things/:thing_id')
843
+ * ```
844
+ *
845
+ * @param prefixPath - Prefix string
846
+ * @returns This router instance
847
+ */
848
+ prefix(prefixPath) {
849
+ const normalizedPrefix = prefixPath.replace(/\/$/, "");
850
+ const previousPrefix = this.opts.prefix || "";
851
+ this.opts.prefix = normalizedPrefix;
852
+ for (const route of this.stack) {
853
+ if (previousPrefix && typeof route.path === "string") {
854
+ if (route.path.startsWith(previousPrefix)) {
855
+ route.path = route.path.slice(previousPrefix.length) || "/";
856
+ route.setPrefix(normalizedPrefix);
857
+ } else {
858
+ route.setPrefix(normalizedPrefix);
859
+ }
860
+ } else {
861
+ route.setPrefix(normalizedPrefix);
862
+ }
863
+ }
864
+ return this;
865
+ }
866
+ /**
867
+ * Returns router middleware which dispatches a route matching the request.
868
+ *
869
+ * @returns Router middleware
870
+ */
871
+ middleware() {
872
+ const dispatchMiddleware = function(context, next) {
873
+ debug("%s %s", context.method, context.path);
874
+ if (!this.matchHost(context.host)) {
875
+ return next();
876
+ }
877
+ const requestPath = this._getRequestPath(context);
878
+ const matchResult = this.match(requestPath, context.method);
879
+ this._storeMatchedRoutes(context, matchResult);
880
+ context.router = this;
881
+ if (!matchResult.route) {
882
+ return next();
883
+ }
884
+ const matchedLayers = matchResult.pathAndMethod;
885
+ this._setMatchedRouteInfo(context, matchedLayers);
886
+ const middlewareChain = this._buildMiddlewareChain(
887
+ matchedLayers,
888
+ requestPath
889
+ );
890
+ return (0, import_koa_compose.default)(middlewareChain)(
891
+ context,
892
+ next
893
+ );
894
+ }.bind(this);
895
+ dispatchMiddleware.router = this;
896
+ return dispatchMiddleware;
897
+ }
898
+ /**
899
+ * Get the request path to use for routing
900
+ * @private
901
+ */
902
+ _getRequestPath(context) {
903
+ const context_ = context;
904
+ return this.opts.routerPath || context_.newRouterPath || context_.path || context_.routerPath || "";
905
+ }
906
+ /**
907
+ * Store matched routes on context
908
+ * @private
909
+ */
910
+ _storeMatchedRoutes(context, matchResult) {
911
+ const context_ = context;
912
+ if (context_.matched) {
913
+ context_.matched.push(...matchResult.path);
914
+ } else {
915
+ context_.matched = matchResult.path;
916
+ }
917
+ }
918
+ /**
919
+ * Set matched route information on context
920
+ * @private
921
+ */
922
+ _setMatchedRouteInfo(context, matchedLayers) {
923
+ const context_ = context;
924
+ const routeLayer = matchedLayers.toReversed().find((layer) => layer.methods.length > 0);
925
+ if (routeLayer) {
926
+ context_._matchedRoute = routeLayer.path;
927
+ if (routeLayer.name) {
928
+ context_._matchedRouteName = routeLayer.name;
929
+ }
930
+ }
931
+ }
932
+ /**
933
+ * Build middleware chain from matched layers
934
+ * @private
935
+ */
936
+ _buildMiddlewareChain(matchedLayers, requestPath) {
937
+ const layersToExecute = this.opts.exclusive ? [matchedLayers.at(-1)].filter(
938
+ (layer) => layer !== void 0
939
+ ) : matchedLayers;
940
+ const middlewareChain = [];
941
+ for (const layer of layersToExecute) {
942
+ middlewareChain.push(
943
+ (context, next) => {
944
+ const routerContext = context;
945
+ routerContext.captures = layer.captures(requestPath);
946
+ routerContext.request.params = layer.params(
947
+ requestPath,
948
+ routerContext.captures || [],
949
+ routerContext.params
950
+ );
951
+ routerContext.params = routerContext.request.params;
952
+ routerContext.routerPath = layer.path;
953
+ routerContext.routerName = layer.name || void 0;
954
+ routerContext._matchedRoute = layer.path;
955
+ if (layer.name) {
956
+ routerContext._matchedRouteName = layer.name;
957
+ }
958
+ return next();
959
+ },
960
+ ...layer.stack
961
+ );
962
+ }
963
+ return middlewareChain;
964
+ }
965
+ routes() {
966
+ return this.middleware();
967
+ }
968
+ /**
969
+ * Returns separate middleware for responding to `OPTIONS` requests with
970
+ * an `Allow` header containing the allowed methods, as well as responding
971
+ * with `405 Method Not Allowed` and `501 Not Implemented` as appropriate.
972
+ *
973
+ * @example
974
+ *
975
+ * ```javascript
976
+ * const Koa = require('koa');
977
+ * const Router = require('@koa/router');
978
+ *
979
+ * const app = new Koa();
980
+ * const router = new Router();
981
+ *
982
+ * app.use(router.routes());
983
+ * app.use(router.allowedMethods());
984
+ * ```
985
+ *
986
+ * **Example with [Boom](https://github.com/hapijs/boom)**
987
+ *
988
+ * ```javascript
989
+ * const Koa = require('koa');
990
+ * const Router = require('@koa/router');
991
+ * const Boom = require('boom');
992
+ *
993
+ * const app = new Koa();
994
+ * const router = new Router();
995
+ *
996
+ * app.use(router.routes());
997
+ * app.use(router.allowedMethods({
998
+ * throw: true,
999
+ * notImplemented: () => new Boom.notImplemented(),
1000
+ * methodNotAllowed: () => new Boom.methodNotAllowed()
1001
+ * }));
1002
+ * ```
1003
+ *
1004
+ * @param options - Options object
1005
+ * @returns Middleware function
1006
+ */
1007
+ allowedMethods(options = {}) {
1008
+ const implementedMethods = this.methods;
1009
+ return (context, next) => {
1010
+ const routerContext = context;
1011
+ return next().then(() => {
1012
+ if (!this._shouldProcessAllowedMethods(routerContext)) {
1013
+ return;
1014
+ }
1015
+ const matchedRoutes = routerContext.matched || [];
1016
+ const allowedMethods = this._collectAllowedMethods(matchedRoutes);
1017
+ const allowedMethodsList = Object.keys(allowedMethods);
1018
+ const requestMethod = context.method.toUpperCase();
1019
+ if (!implementedMethods.includes(requestMethod)) {
1020
+ this._handleNotImplemented(
1021
+ routerContext,
1022
+ allowedMethodsList,
1023
+ options
1024
+ );
1025
+ return;
1026
+ }
1027
+ if (requestMethod === "OPTIONS" && allowedMethodsList.length > 0) {
1028
+ this._handleOptionsRequest(routerContext, allowedMethodsList);
1029
+ return;
1030
+ }
1031
+ if (allowedMethodsList.length > 0 && !allowedMethods[requestMethod]) {
1032
+ this._handleMethodNotAllowed(
1033
+ routerContext,
1034
+ allowedMethodsList,
1035
+ options
1036
+ );
1037
+ }
1038
+ });
1039
+ };
1040
+ }
1041
+ /**
1042
+ * Check if we should process allowed methods
1043
+ * @private
1044
+ */
1045
+ _shouldProcessAllowedMethods(context) {
1046
+ return !!(context.matched && (!context.status || context.status === 404));
1047
+ }
1048
+ /**
1049
+ * Collect all allowed methods from matched routes
1050
+ * @private
1051
+ */
1052
+ _collectAllowedMethods(matchedRoutes) {
1053
+ const allowedMethods = {};
1054
+ for (const route of matchedRoutes) {
1055
+ for (const method of route.methods) {
1056
+ allowedMethods[method] = method;
1057
+ }
1058
+ }
1059
+ return allowedMethods;
1060
+ }
1061
+ /**
1062
+ * Handle 501 Not Implemented response
1063
+ * @private
1064
+ */
1065
+ _handleNotImplemented(context, allowedMethodsList, options) {
1066
+ if (options.throw) {
1067
+ const error = typeof options.notImplemented === "function" ? options.notImplemented() : new import_http_errors.default.NotImplemented();
1068
+ throw error;
1069
+ }
1070
+ context.status = 501;
1071
+ context.set("Allow", allowedMethodsList.join(", "));
1072
+ }
1073
+ /**
1074
+ * Handle OPTIONS request
1075
+ * @private
1076
+ */
1077
+ _handleOptionsRequest(context, allowedMethodsList) {
1078
+ context.status = 200;
1079
+ context.body = "";
1080
+ context.set("Allow", allowedMethodsList.join(", "));
1081
+ }
1082
+ /**
1083
+ * Handle 405 Method Not Allowed response
1084
+ * @private
1085
+ */
1086
+ _handleMethodNotAllowed(context, allowedMethodsList, options) {
1087
+ if (options.throw) {
1088
+ const error = typeof options.methodNotAllowed === "function" ? options.methodNotAllowed() : new import_http_errors.default.MethodNotAllowed();
1089
+ throw error;
1090
+ }
1091
+ context.status = 405;
1092
+ context.set("Allow", allowedMethodsList.join(", "));
1093
+ }
1094
+ all(...arguments_) {
1095
+ let name;
1096
+ let path;
1097
+ let middleware;
1098
+ if (arguments_.length >= 2 && (typeof arguments_[1] === "string" || arguments_[1] instanceof RegExp)) {
1099
+ name = arguments_[0];
1100
+ path = arguments_[1];
1101
+ middleware = arguments_.slice(2);
1102
+ } else {
1103
+ name = void 0;
1104
+ path = arguments_[0];
1105
+ middleware = arguments_.slice(1);
1106
+ }
1107
+ if (typeof path !== "string" && !(path instanceof RegExp) && (!Array.isArray(path) || path.length === 0))
1108
+ throw new Error("You have to provide a path when adding an all handler");
1109
+ const routeOptions = {
1110
+ name,
1111
+ pathAsRegExp: path instanceof RegExp
1112
+ };
1113
+ this.register(path, httpMethods, middleware, {
1114
+ ...this.opts,
1115
+ ...routeOptions
1116
+ });
1117
+ return this;
1118
+ }
1119
+ /**
1120
+ * Redirect `source` to `destination` URL with optional 30x status `code`.
1121
+ *
1122
+ * Both `source` and `destination` can be route names.
1123
+ *
1124
+ * ```javascript
1125
+ * router.redirect('/login', 'sign-in');
1126
+ * ```
1127
+ *
1128
+ * This is equivalent to:
1129
+ *
1130
+ * ```javascript
1131
+ * router.all('/login', ctx => {
1132
+ * ctx.redirect('/sign-in');
1133
+ * ctx.status = 301;
1134
+ * });
1135
+ * ```
1136
+ *
1137
+ * @param source - URL or route name
1138
+ * @param destination - URL or route name
1139
+ * @param code - HTTP status code (default: 301)
1140
+ * @returns This router instance
1141
+ */
1142
+ redirect(source, destination, code) {
1143
+ let resolvedSource = source;
1144
+ let resolvedDestination = destination;
1145
+ if (typeof source === "symbol" || typeof source === "string" && source[0] !== "/") {
1146
+ const sourceUrl = this.url(source);
1147
+ if (sourceUrl instanceof Error) throw sourceUrl;
1148
+ resolvedSource = sourceUrl;
1149
+ }
1150
+ if (typeof destination === "symbol" || typeof destination === "string" && destination[0] !== "/" && !destination.includes("://")) {
1151
+ const destinationUrl = this.url(destination);
1152
+ if (destinationUrl instanceof Error) throw destinationUrl;
1153
+ resolvedDestination = destinationUrl;
1154
+ }
1155
+ const result = this.all(
1156
+ resolvedSource,
1157
+ (context) => {
1158
+ context.redirect(resolvedDestination);
1159
+ context.status = code || 301;
1160
+ }
1161
+ );
1162
+ return result;
1163
+ }
1164
+ /**
1165
+ * Create and register a route.
1166
+ *
1167
+ * @param path - Path string
1168
+ * @param methods - Array of HTTP verbs
1169
+ * @param middleware - Middleware functions
1170
+ * @param additionalOptions - Additional options
1171
+ * @returns Created layer
1172
+ * @private
1173
+ */
1174
+ register(path, methods, middleware, additionalOptions = {}) {
1175
+ const mergedOptions = { ...this.opts, ...additionalOptions };
1176
+ if (Array.isArray(path)) {
1177
+ return this._registerMultiplePaths(
1178
+ path,
1179
+ methods,
1180
+ middleware,
1181
+ mergedOptions
1182
+ );
1183
+ }
1184
+ const routeLayer = this._createRouteLayer(
1185
+ path,
1186
+ methods,
1187
+ middleware,
1188
+ mergedOptions
1189
+ );
1190
+ if (this.opts.prefix) {
1191
+ routeLayer.setPrefix(this.opts.prefix);
1192
+ }
1193
+ applyAllParameterMiddleware(routeLayer, this.params);
1194
+ this.stack.push(routeLayer);
1195
+ debug("defined route %s %s", routeLayer.methods, routeLayer.path);
1196
+ return routeLayer;
1197
+ }
1198
+ /**
1199
+ * Register multiple paths with the same configuration
1200
+ * @private
1201
+ */
1202
+ _registerMultiplePaths(pathArray, methods, middleware, options) {
1203
+ for (const singlePath of pathArray) {
1204
+ this.register.call(this, singlePath, methods, middleware, options);
1205
+ }
1206
+ return this;
1207
+ }
1208
+ /**
1209
+ * Create a route layer with given configuration
1210
+ * @private
1211
+ */
1212
+ _createRouteLayer(path, methods, middleware, options) {
1213
+ return new Layer(path, methods, middleware, {
1214
+ end: options.end === false ? options.end : true,
1215
+ name: options.name,
1216
+ sensitive: options.sensitive || false,
1217
+ strict: options.strict || false,
1218
+ prefix: options.prefix || "",
1219
+ ignoreCaptures: options.ignoreCaptures,
1220
+ pathAsRegExp: options.pathAsRegExp
1221
+ });
1222
+ }
1223
+ /**
1224
+ * Lookup route with given `name`.
1225
+ *
1226
+ * @param name - Route name
1227
+ * @returns Matched layer or false
1228
+ */
1229
+ route(name) {
1230
+ const matchingRoute = this.stack.find((route) => route.name === name);
1231
+ return matchingRoute || false;
1232
+ }
1233
+ /**
1234
+ * Generate URL for route. Takes a route name and map of named `params`.
1235
+ *
1236
+ * @example
1237
+ *
1238
+ * ```javascript
1239
+ * router.get('user', '/users/:id', (ctx, next) => {
1240
+ * // ...
1241
+ * });
1242
+ *
1243
+ * router.url('user', 3);
1244
+ * // => "/users/3"
1245
+ *
1246
+ * router.url('user', { id: 3 });
1247
+ * // => "/users/3"
1248
+ *
1249
+ * router.use((ctx, next) => {
1250
+ * // redirect to named route
1251
+ * ctx.redirect(ctx.router.url('sign-in'));
1252
+ * })
1253
+ *
1254
+ * router.url('user', { id: 3 }, { query: { limit: 1 } });
1255
+ * // => "/users/3?limit=1"
1256
+ *
1257
+ * router.url('user', { id: 3 }, { query: "limit=1" });
1258
+ * // => "/users/3?limit=1"
1259
+ * ```
1260
+ *
1261
+ * @param name - Route name
1262
+ * @param args - URL parameters
1263
+ * @returns Generated URL or Error
1264
+ */
1265
+ url(name, ...arguments_) {
1266
+ const route = this.route(name);
1267
+ if (route) return route.url(...arguments_);
1268
+ return new Error(`No route found for name: ${String(name)}`);
1269
+ }
1270
+ /**
1271
+ * Match given `path` and return corresponding routes.
1272
+ *
1273
+ * @param path - Request path
1274
+ * @param method - HTTP method
1275
+ * @returns Match result with matched layers
1276
+ * @private
1277
+ */
1278
+ match(path, method) {
1279
+ const matchResult = {
1280
+ path: [],
1281
+ pathAndMethod: [],
1282
+ route: false
1283
+ };
1284
+ const normalizedMethod = method.toUpperCase();
1285
+ for (const layer of this.stack) {
1286
+ debug("test %s %s", layer.path, layer.regexp);
1287
+ if (layer.match(path)) {
1288
+ matchResult.path.push(layer);
1289
+ const isMiddleware = layer.methods.length === 0;
1290
+ const matchesMethod = layer.methods.includes(normalizedMethod);
1291
+ if (isMiddleware || matchesMethod) {
1292
+ matchResult.pathAndMethod.push(layer);
1293
+ if (layer.methods.length > 0) {
1294
+ matchResult.route = true;
1295
+ }
1296
+ }
1297
+ }
1298
+ }
1299
+ return matchResult;
1300
+ }
1301
+ /**
1302
+ * Match given `input` to allowed host
1303
+ * @param input - Host to check
1304
+ * @returns Whether host matches
1305
+ */
1306
+ matchHost(input) {
1307
+ const { host } = this;
1308
+ if (!host) {
1309
+ return true;
1310
+ }
1311
+ if (!input) {
1312
+ return false;
1313
+ }
1314
+ if (typeof host === "string") {
1315
+ return input === host;
1316
+ }
1317
+ if (Array.isArray(host)) {
1318
+ return host.includes(input);
1319
+ }
1320
+ if (host instanceof RegExp) {
1321
+ return host.test(input);
1322
+ }
1323
+ return false;
1324
+ }
1325
+ /**
1326
+ * Run middleware for named route parameters. Useful for auto-loading or
1327
+ * validation.
1328
+ *
1329
+ * @example
1330
+ *
1331
+ * ```javascript
1332
+ * router
1333
+ * .param('user', (id, ctx, next) => {
1334
+ * ctx.user = users[id];
1335
+ * if (!ctx.user) return ctx.status = 404;
1336
+ * return next();
1337
+ * })
1338
+ * .get('/users/:user', ctx => {
1339
+ * ctx.body = ctx.user;
1340
+ * })
1341
+ * .get('/users/:user/friends', ctx => {
1342
+ * return ctx.user.getFriends().then(function(friends) {
1343
+ * ctx.body = friends;
1344
+ * });
1345
+ * })
1346
+ * // /users/3 => {"id": 3, "name": "Alex"}
1347
+ * // /users/3/friends => [{"id": 4, "name": "TJ"}]
1348
+ * ```
1349
+ *
1350
+ * @param param - Parameter name
1351
+ * @param middleware - Parameter middleware
1352
+ * @returns This router instance
1353
+ */
1354
+ param(parameter, middleware) {
1355
+ if (!this.params[parameter]) {
1356
+ this.params[parameter] = [];
1357
+ }
1358
+ if (!Array.isArray(this.params[parameter])) {
1359
+ this.params[parameter] = [
1360
+ this.params[parameter]
1361
+ ];
1362
+ }
1363
+ this.params[parameter].push(middleware);
1364
+ for (const route of this.stack) {
1365
+ route.param(parameter, middleware);
1366
+ }
1367
+ return this;
1368
+ }
1369
+ /**
1370
+ * Helper method for registering HTTP verb routes
1371
+ * @internal - Used by dynamically added HTTP methods
1372
+ */
1373
+ _registerMethod(method, ...arguments_) {
1374
+ let name;
1375
+ let path;
1376
+ let middleware;
1377
+ if (arguments_.length >= 2 && (typeof arguments_[1] === "string" || arguments_[1] instanceof RegExp)) {
1378
+ name = arguments_[0];
1379
+ path = arguments_[1];
1380
+ middleware = arguments_.slice(2);
1381
+ } else {
1382
+ name = void 0;
1383
+ path = arguments_[0];
1384
+ middleware = arguments_.slice(1);
1385
+ }
1386
+ if (typeof path !== "string" && !(path instanceof RegExp) && (!Array.isArray(path) || path.length === 0))
1387
+ throw new Error(
1388
+ `You have to provide a path when adding a ${method} handler`
1389
+ );
1390
+ const options = {
1391
+ name,
1392
+ pathAsRegExp: path instanceof RegExp
1393
+ };
1394
+ this.register(path, [method], middleware, {
1395
+ ...this.opts,
1396
+ ...options
1397
+ });
1398
+ return this;
1399
+ }
1400
+ get(...arguments_) {
1401
+ return this._registerMethod("get", ...arguments_);
1402
+ }
1403
+ post(...arguments_) {
1404
+ return this._registerMethod("post", ...arguments_);
1405
+ }
1406
+ put(...arguments_) {
1407
+ return this._registerMethod("put", ...arguments_);
1408
+ }
1409
+ patch(...arguments_) {
1410
+ return this._registerMethod("patch", ...arguments_);
1411
+ }
1412
+ delete(...arguments_) {
1413
+ return this._registerMethod("delete", ...arguments_);
1414
+ }
1415
+ del(...arguments_) {
1416
+ return this.delete.apply(
1417
+ this,
1418
+ arguments_
1419
+ );
1420
+ }
1421
+ head(...arguments_) {
1422
+ return this._registerMethod("head", ...arguments_);
1423
+ }
1424
+ options(...arguments_) {
1425
+ return this._registerMethod("options", ...arguments_);
1426
+ }
1427
+ };
1428
+ var RouterExport = Router;
1429
+ var router_default = RouterExport;
1430
+ for (const httpMethod of httpMethods) {
1431
+ const isAlreadyDefined = COMMON_HTTP_METHODS.includes(httpMethod) || httpMethod in Router.prototype;
1432
+ if (!isAlreadyDefined) {
1433
+ Object.defineProperty(Router.prototype, httpMethod, {
1434
+ value: function(...arguments_) {
1435
+ return this._registerMethod(httpMethod, ...arguments_);
1436
+ },
1437
+ writable: true,
1438
+ configurable: true,
1439
+ enumerable: false
1440
+ });
1441
+ }
1442
+ }
1443
+ // Annotate the CommonJS export names for ESM import in node:
1444
+ 0 && (module.exports = {
1445
+ Router
1446
+ });
1447
+ if (module.exports.default) {
1448
+ Object.assign(module.exports.default, module.exports);
1449
+ module.exports = module.exports.default;
1450
+ }