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