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