@koa/router 15.7.0 → 15.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +90 -85
- package/dist/index.mjs +90 -85
- package/package.json +17 -19
package/dist/index.d.mts
CHANGED
|
@@ -375,6 +375,12 @@ declare class RouterImplementation<StateT = koa.DefaultState, ContextT = koa.Def
|
|
|
375
375
|
* @returns This router instance
|
|
376
376
|
*/
|
|
377
377
|
prefix(prefixPath: string): Router<StateT, ContextT>;
|
|
378
|
+
private _determineMiddlewarePath;
|
|
379
|
+
private _hasPathParameters;
|
|
380
|
+
private _updatePrefix;
|
|
381
|
+
private _getUnprefixedPath;
|
|
382
|
+
private _updateRoutePrefix;
|
|
383
|
+
private _updateRouteCaptureOptions;
|
|
378
384
|
/**
|
|
379
385
|
* Returns router middleware which dispatches a route matching the request.
|
|
380
386
|
*
|
|
@@ -943,7 +949,7 @@ type RouterContext<StateT = DefaultState, ContextT = DefaultContext, BodyT = unk
|
|
|
943
949
|
/**
|
|
944
950
|
* Track param middleware execution (internal)
|
|
945
951
|
*/
|
|
946
|
-
_matchedParams?: WeakMap<Function,
|
|
952
|
+
_matchedParams?: WeakMap<Function, Map<string, string>>;
|
|
947
953
|
};
|
|
948
954
|
/**
|
|
949
955
|
* Router middleware function type
|
package/dist/index.d.ts
CHANGED
|
@@ -375,6 +375,12 @@ declare class RouterImplementation<StateT = koa.DefaultState, ContextT = koa.Def
|
|
|
375
375
|
* @returns This router instance
|
|
376
376
|
*/
|
|
377
377
|
prefix(prefixPath: string): Router<StateT, ContextT>;
|
|
378
|
+
private _determineMiddlewarePath;
|
|
379
|
+
private _hasPathParameters;
|
|
380
|
+
private _updatePrefix;
|
|
381
|
+
private _getUnprefixedPath;
|
|
382
|
+
private _updateRoutePrefix;
|
|
383
|
+
private _updateRouteCaptureOptions;
|
|
378
384
|
/**
|
|
379
385
|
* Returns router middleware which dispatches a route matching the request.
|
|
380
386
|
*
|
|
@@ -943,7 +949,7 @@ type RouterContext<StateT = DefaultState, ContextT = DefaultContext, BodyT = unk
|
|
|
943
949
|
/**
|
|
944
950
|
* Track param middleware execution (internal)
|
|
945
951
|
*/
|
|
946
|
-
_matchedParams?: WeakMap<Function,
|
|
952
|
+
_matchedParams?: WeakMap<Function, Map<string, string>>;
|
|
947
953
|
};
|
|
948
954
|
/**
|
|
949
955
|
* Router middleware function type
|
package/dist/index.js
CHANGED
|
@@ -271,7 +271,7 @@ var Layer = class {
|
|
|
271
271
|
_configurePathMatching() {
|
|
272
272
|
if (this.opts.pathAsRegExp === true) {
|
|
273
273
|
this.regexp = this.path instanceof RegExp ? this.path : new RegExp(this.path);
|
|
274
|
-
} else if (this.path) {
|
|
274
|
+
} else if (this.path || this.path === "") {
|
|
275
275
|
this._configurePathToRegexp();
|
|
276
276
|
}
|
|
277
277
|
}
|
|
@@ -504,11 +504,17 @@ var Layer = class {
|
|
|
504
504
|
if (!context._matchedParams) {
|
|
505
505
|
context._matchedParams = /* @__PURE__ */ new WeakMap();
|
|
506
506
|
}
|
|
507
|
-
|
|
507
|
+
const parameterValue = context.params[parameterName];
|
|
508
|
+
const matchedParameters = context._matchedParams.get(parameterHandler) ?? /* @__PURE__ */ new Map();
|
|
509
|
+
const hasMatchedParameter = matchedParameters.has(parameterName);
|
|
510
|
+
const matchedValue = matchedParameters.get(parameterName);
|
|
511
|
+
const hasMatchedValue = hasMatchedParameter && matchedValue === parameterValue;
|
|
512
|
+
if (hasMatchedValue) {
|
|
508
513
|
return next();
|
|
509
514
|
}
|
|
510
|
-
|
|
511
|
-
|
|
515
|
+
matchedParameters.set(parameterName, parameterValue);
|
|
516
|
+
context._matchedParams.set(parameterHandler, matchedParameters);
|
|
517
|
+
return parameterHandler(parameterValue, context, next);
|
|
512
518
|
});
|
|
513
519
|
middleware.param = parameterName;
|
|
514
520
|
middleware._originalFn = parameterHandler;
|
|
@@ -548,10 +554,7 @@ var Layer = class {
|
|
|
548
554
|
* @private
|
|
549
555
|
*/
|
|
550
556
|
setPrefix(prefixPath) {
|
|
551
|
-
if (
|
|
552
|
-
return this;
|
|
553
|
-
}
|
|
554
|
-
if (this.path instanceof RegExp) {
|
|
557
|
+
if (typeof this.path !== "string") {
|
|
555
558
|
return this;
|
|
556
559
|
}
|
|
557
560
|
this.path = this._applyPrefix(prefixPath);
|
|
@@ -569,9 +572,14 @@ var Layer = class {
|
|
|
569
572
|
const isPathIsRawRegex = this.opts.pathAsRegExp === true && typeof this.path === "string";
|
|
570
573
|
if (isPrefixHasParameters && isPathIsRawRegex) {
|
|
571
574
|
const currentPath = this.path;
|
|
572
|
-
|
|
573
|
-
|
|
575
|
+
const middlewareBoundary = [
|
|
576
|
+
String.raw`(?:\/|$)`,
|
|
577
|
+
String.raw`(?:\\\/|$)`
|
|
578
|
+
].find((boundary) => currentPath.endsWith(boundary));
|
|
579
|
+
if (middlewareBoundary) {
|
|
580
|
+
this.path = `${currentPath.slice(0, -middlewareBoundary.length)}{/*rest}`;
|
|
574
581
|
this.opts.pathAsRegExp = false;
|
|
582
|
+
this.opts.ignoreWildcardParameter = "rest";
|
|
575
583
|
}
|
|
576
584
|
}
|
|
577
585
|
if (isRootPath && !isStrictMode) {
|
|
@@ -598,7 +606,7 @@ var Layer = class {
|
|
|
598
606
|
} else if (isTreatAsRegExp) {
|
|
599
607
|
const pathString = this.path;
|
|
600
608
|
const anchoredPattern = pathString.startsWith("^") ? pathString : `^${pathString}`;
|
|
601
|
-
this.regexp = this.path instanceof RegExp ? this.path : new RegExp(anchoredPattern);
|
|
609
|
+
this.regexp = this.path instanceof RegExp ? this.path : new RegExp(anchoredPattern, this.opts.sensitive ? "" : "i");
|
|
602
610
|
} else {
|
|
603
611
|
const options = normalizeLayerOptionsToPathToRegexp(this.opts);
|
|
604
612
|
const { regexp, keys } = compilePathToRegexp(
|
|
@@ -657,57 +665,14 @@ function applyAllParameterMiddleware(route, parametersObject) {
|
|
|
657
665
|
}
|
|
658
666
|
}
|
|
659
667
|
|
|
660
|
-
// src/utils/path-helpers.ts
|
|
661
|
-
function hasPathParameters(path, options = {}) {
|
|
662
|
-
if (!path) {
|
|
663
|
-
return false;
|
|
664
|
-
}
|
|
665
|
-
const { keys } = compilePathToRegexp(path, options);
|
|
666
|
-
return keys.length > 0;
|
|
667
|
-
}
|
|
668
|
-
function determineMiddlewarePath(explicitPath, hasPrefixParameters) {
|
|
669
|
-
if (explicitPath !== void 0) {
|
|
670
|
-
if (typeof explicitPath === "string") {
|
|
671
|
-
if (explicitPath === "") {
|
|
672
|
-
return {
|
|
673
|
-
path: "{/*rest}",
|
|
674
|
-
pathAsRegExp: false
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
if (explicitPath === "/") {
|
|
678
|
-
return {
|
|
679
|
-
path: "/",
|
|
680
|
-
pathAsRegExp: false
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
return {
|
|
684
|
-
path: explicitPath,
|
|
685
|
-
pathAsRegExp: false
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
|
-
return {
|
|
689
|
-
path: explicitPath,
|
|
690
|
-
pathAsRegExp: true
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
if (hasPrefixParameters) {
|
|
694
|
-
return {
|
|
695
|
-
path: "{/*rest}",
|
|
696
|
-
pathAsRegExp: false
|
|
697
|
-
};
|
|
698
|
-
}
|
|
699
|
-
return {
|
|
700
|
-
path: String.raw`(?:\/|$)`,
|
|
701
|
-
pathAsRegExp: true
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
|
|
705
668
|
// src/utils/debug.ts
|
|
706
669
|
var import_debug = __toESM(require("debug"));
|
|
707
670
|
var debug = (0, import_debug.default)("koa-router");
|
|
708
671
|
|
|
709
672
|
// src/router.ts
|
|
710
673
|
var httpMethods = getAllHttpMethods();
|
|
674
|
+
var MIDDLEWARE_BOUNDARY_PATH = String.raw`(?:\/|$)`;
|
|
675
|
+
var MIDDLEWARE_WILDCARD_PATH = "{/*rest}";
|
|
711
676
|
var RouterImplementation = class {
|
|
712
677
|
opts;
|
|
713
678
|
methods;
|
|
@@ -853,7 +818,10 @@ var RouterImplementation = class {
|
|
|
853
818
|
_mountNestedRouter(middlewareWithRouter, mountPath) {
|
|
854
819
|
const nestedRouter = middlewareWithRouter.router;
|
|
855
820
|
const clonedRouter = this._cloneRouter(nestedRouter);
|
|
856
|
-
const
|
|
821
|
+
const isMountPathHasParameters = this._hasPathParameters(mountPath);
|
|
822
|
+
const isRouterPrefixHasParameters = this._hasPathParameters(
|
|
823
|
+
this.opts.prefix
|
|
824
|
+
);
|
|
857
825
|
for (let routeIndex = 0; routeIndex < clonedRouter.stack.length; routeIndex++) {
|
|
858
826
|
const nestedLayer = clonedRouter.stack[routeIndex];
|
|
859
827
|
const clonedLayer = this._cloneLayer(nestedLayer);
|
|
@@ -863,7 +831,7 @@ var RouterImplementation = class {
|
|
|
863
831
|
if (this.opts.prefix) {
|
|
864
832
|
clonedLayer.setPrefix(this.opts.prefix);
|
|
865
833
|
}
|
|
866
|
-
if (clonedLayer.methods.length === 0 &&
|
|
834
|
+
if (clonedLayer.methods.length === 0 && (isMountPathHasParameters || isRouterPrefixHasParameters)) {
|
|
867
835
|
clonedLayer.opts.ignoreCaptures = false;
|
|
868
836
|
}
|
|
869
837
|
this.stack.push(clonedLayer);
|
|
@@ -924,20 +892,14 @@ var RouterImplementation = class {
|
|
|
924
892
|
* @private
|
|
925
893
|
*/
|
|
926
894
|
_registerMiddleware(middleware, explicitPath, hasExplicitPath) {
|
|
927
|
-
const isPrefixHasParameters =
|
|
928
|
-
this.opts.prefix || "",
|
|
929
|
-
this.opts
|
|
930
|
-
);
|
|
895
|
+
const isPrefixHasParameters = this._hasPathParameters(this.opts.prefix);
|
|
931
896
|
const effectiveExplicitPath = (() => {
|
|
932
897
|
if (explicitPath !== void 0) return explicitPath;
|
|
933
898
|
if (isPrefixHasParameters) return "";
|
|
934
899
|
return;
|
|
935
900
|
})();
|
|
936
901
|
const isEffectiveHasExplicitPath = hasExplicitPath || explicitPath === void 0 && isPrefixHasParameters;
|
|
937
|
-
const { path: middlewarePath, pathAsRegExp } =
|
|
938
|
-
effectiveExplicitPath,
|
|
939
|
-
isPrefixHasParameters
|
|
940
|
-
);
|
|
902
|
+
const { path: middlewarePath, pathAsRegExp } = this._determineMiddlewarePath(effectiveExplicitPath);
|
|
941
903
|
let finalPath = middlewarePath;
|
|
942
904
|
let isUsePathToRegexp = pathAsRegExp;
|
|
943
905
|
const isRootPath = isEffectiveHasExplicitPath && middlewarePath === "/";
|
|
@@ -945,7 +907,7 @@ var RouterImplementation = class {
|
|
|
945
907
|
finalPath = middlewarePath;
|
|
946
908
|
isUsePathToRegexp = false;
|
|
947
909
|
}
|
|
948
|
-
const ignoreWildcardParameter = effectiveExplicitPath === "" && middlewarePath ===
|
|
910
|
+
const ignoreWildcardParameter = effectiveExplicitPath === "" && middlewarePath === MIDDLEWARE_WILDCARD_PATH ? "rest" : void 0;
|
|
949
911
|
this.register(finalPath, [], middleware, {
|
|
950
912
|
end: isRootPath,
|
|
951
913
|
ignoreCaptures: !isEffectiveHasExplicitPath && !isPrefixHasParameters,
|
|
@@ -967,19 +929,56 @@ var RouterImplementation = class {
|
|
|
967
929
|
* @returns This router instance
|
|
968
930
|
*/
|
|
969
931
|
prefix(prefixPath) {
|
|
970
|
-
|
|
932
|
+
this._updatePrefix(prefixPath);
|
|
933
|
+
return this;
|
|
934
|
+
}
|
|
935
|
+
_determineMiddlewarePath(explicitPath) {
|
|
936
|
+
if (explicitPath === "") {
|
|
937
|
+
return { path: MIDDLEWARE_WILDCARD_PATH, pathAsRegExp: false };
|
|
938
|
+
}
|
|
939
|
+
if (explicitPath === void 0) {
|
|
940
|
+
return { path: MIDDLEWARE_BOUNDARY_PATH, pathAsRegExp: true };
|
|
941
|
+
}
|
|
942
|
+
const isPathAsRegExp = typeof explicitPath !== "string";
|
|
943
|
+
return { path: explicitPath, pathAsRegExp: isPathAsRegExp };
|
|
944
|
+
}
|
|
945
|
+
_hasPathParameters(path = "") {
|
|
946
|
+
if (typeof path !== "string" || !path) return false;
|
|
947
|
+
const { keys } = compilePathToRegexp(path, this.opts);
|
|
948
|
+
return keys.length > 0;
|
|
949
|
+
}
|
|
950
|
+
_updatePrefix(currentPrefix) {
|
|
971
951
|
const previousPrefix = this.opts.prefix || "";
|
|
952
|
+
const normalizedPrefix = currentPrefix.replace(/\/$/, "");
|
|
953
|
+
const isPrefixHasParameters = this._hasPathParameters(normalizedPrefix);
|
|
972
954
|
this.opts.prefix = normalizedPrefix;
|
|
973
955
|
for (const route of this.stack) {
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
956
|
+
this._updateRoutePrefix(route, previousPrefix);
|
|
957
|
+
this._updateRouteCaptureOptions(route, isPrefixHasParameters);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
_getUnprefixedPath(route, previousPrefix) {
|
|
961
|
+
if (typeof route.path !== "string" || !previousPrefix) return route.path;
|
|
962
|
+
const prefixedMiddlewarePaths = /* @__PURE__ */ new Set([
|
|
963
|
+
previousPrefix + MIDDLEWARE_BOUNDARY_PATH,
|
|
964
|
+
previousPrefix + MIDDLEWARE_WILDCARD_PATH
|
|
965
|
+
]);
|
|
966
|
+
const stripBoundary = previousPrefix + "/";
|
|
967
|
+
const isExactPrefix = route.path === previousPrefix;
|
|
968
|
+
const hasPrefixBoundary = route.path.startsWith(stripBoundary);
|
|
969
|
+
const isPathlessMiddleware = route.methods.length === 0 && prefixedMiddlewarePaths.has(route.path);
|
|
970
|
+
const hasReplaceablePrefix = isExactPrefix || hasPrefixBoundary || isPathlessMiddleware;
|
|
971
|
+
if (!hasReplaceablePrefix) return route.path;
|
|
972
|
+
return route.path.slice(previousPrefix.length) || "/";
|
|
973
|
+
}
|
|
974
|
+
_updateRoutePrefix(route, previousPrefix) {
|
|
975
|
+
route.path = this._getUnprefixedPath(route, previousPrefix);
|
|
976
|
+
route.setPrefix(this.opts.prefix || "");
|
|
977
|
+
}
|
|
978
|
+
_updateRouteCaptureOptions(route, isPrefixHasParameters) {
|
|
979
|
+
if (route.methods.length === 0 && isPrefixHasParameters) {
|
|
980
|
+
route.opts.ignoreCaptures = false;
|
|
981
981
|
}
|
|
982
|
-
return this;
|
|
983
982
|
}
|
|
984
983
|
/**
|
|
985
984
|
* Returns router middleware which dispatches a route matching the request.
|
|
@@ -1058,23 +1057,29 @@ var RouterImplementation = class {
|
|
|
1058
1057
|
*/
|
|
1059
1058
|
_buildMiddlewareChain(matchedLayers, requestPath) {
|
|
1060
1059
|
let layersToExecute;
|
|
1060
|
+
let chosenLayer;
|
|
1061
1061
|
if (this.exclusive) {
|
|
1062
|
-
|
|
1062
|
+
const routeLayers = matchedLayers.filter(
|
|
1063
|
+
(layer) => layer.methods.length > 0
|
|
1064
|
+
);
|
|
1063
1065
|
if (this.opts.exclusive === "specificity") {
|
|
1064
|
-
for (const layer of
|
|
1066
|
+
for (const layer of routeLayers) {
|
|
1065
1067
|
if (!chosenLayer || layer.paramNames.length < chosenLayer.paramNames.length) {
|
|
1066
1068
|
chosenLayer = layer;
|
|
1067
1069
|
}
|
|
1068
1070
|
}
|
|
1069
1071
|
} else {
|
|
1070
|
-
chosenLayer =
|
|
1072
|
+
chosenLayer = routeLayers.at(-1);
|
|
1071
1073
|
}
|
|
1072
|
-
layersToExecute =
|
|
1074
|
+
layersToExecute = matchedLayers.filter(
|
|
1075
|
+
(layer) => layer.methods.length === 0 || layer === chosenLayer
|
|
1076
|
+
);
|
|
1073
1077
|
} else {
|
|
1074
1078
|
layersToExecute = matchedLayers;
|
|
1075
1079
|
}
|
|
1076
1080
|
const middlewareChain = [];
|
|
1077
1081
|
for (const layer of layersToExecute) {
|
|
1082
|
+
const metadataLayer = chosenLayer ?? layer;
|
|
1078
1083
|
middlewareChain.push(
|
|
1079
1084
|
(context, next) => {
|
|
1080
1085
|
const routerContext = context;
|
|
@@ -1085,11 +1090,11 @@ var RouterImplementation = class {
|
|
|
1085
1090
|
routerContext.params
|
|
1086
1091
|
);
|
|
1087
1092
|
routerContext.params = routerContext.request.params;
|
|
1088
|
-
routerContext.routerPath =
|
|
1089
|
-
routerContext.routerName =
|
|
1090
|
-
routerContext._matchedRoute =
|
|
1091
|
-
if (
|
|
1092
|
-
routerContext._matchedRouteName =
|
|
1093
|
+
routerContext.routerPath = metadataLayer.path;
|
|
1094
|
+
routerContext.routerName = metadataLayer.name || void 0;
|
|
1095
|
+
routerContext._matchedRoute = metadataLayer.path;
|
|
1096
|
+
if (this.exclusive || metadataLayer.name) {
|
|
1097
|
+
routerContext._matchedRouteName = metadataLayer.name || void 0;
|
|
1093
1098
|
}
|
|
1094
1099
|
return next();
|
|
1095
1100
|
},
|
package/dist/index.mjs
CHANGED
|
@@ -232,7 +232,7 @@ var Layer = class {
|
|
|
232
232
|
_configurePathMatching() {
|
|
233
233
|
if (this.opts.pathAsRegExp === true) {
|
|
234
234
|
this.regexp = this.path instanceof RegExp ? this.path : new RegExp(this.path);
|
|
235
|
-
} else if (this.path) {
|
|
235
|
+
} else if (this.path || this.path === "") {
|
|
236
236
|
this._configurePathToRegexp();
|
|
237
237
|
}
|
|
238
238
|
}
|
|
@@ -465,11 +465,17 @@ var Layer = class {
|
|
|
465
465
|
if (!context._matchedParams) {
|
|
466
466
|
context._matchedParams = /* @__PURE__ */ new WeakMap();
|
|
467
467
|
}
|
|
468
|
-
|
|
468
|
+
const parameterValue = context.params[parameterName];
|
|
469
|
+
const matchedParameters = context._matchedParams.get(parameterHandler) ?? /* @__PURE__ */ new Map();
|
|
470
|
+
const hasMatchedParameter = matchedParameters.has(parameterName);
|
|
471
|
+
const matchedValue = matchedParameters.get(parameterName);
|
|
472
|
+
const hasMatchedValue = hasMatchedParameter && matchedValue === parameterValue;
|
|
473
|
+
if (hasMatchedValue) {
|
|
469
474
|
return next();
|
|
470
475
|
}
|
|
471
|
-
|
|
472
|
-
|
|
476
|
+
matchedParameters.set(parameterName, parameterValue);
|
|
477
|
+
context._matchedParams.set(parameterHandler, matchedParameters);
|
|
478
|
+
return parameterHandler(parameterValue, context, next);
|
|
473
479
|
});
|
|
474
480
|
middleware.param = parameterName;
|
|
475
481
|
middleware._originalFn = parameterHandler;
|
|
@@ -509,10 +515,7 @@ var Layer = class {
|
|
|
509
515
|
* @private
|
|
510
516
|
*/
|
|
511
517
|
setPrefix(prefixPath) {
|
|
512
|
-
if (
|
|
513
|
-
return this;
|
|
514
|
-
}
|
|
515
|
-
if (this.path instanceof RegExp) {
|
|
518
|
+
if (typeof this.path !== "string") {
|
|
516
519
|
return this;
|
|
517
520
|
}
|
|
518
521
|
this.path = this._applyPrefix(prefixPath);
|
|
@@ -530,9 +533,14 @@ var Layer = class {
|
|
|
530
533
|
const isPathIsRawRegex = this.opts.pathAsRegExp === true && typeof this.path === "string";
|
|
531
534
|
if (isPrefixHasParameters && isPathIsRawRegex) {
|
|
532
535
|
const currentPath = this.path;
|
|
533
|
-
|
|
534
|
-
|
|
536
|
+
const middlewareBoundary = [
|
|
537
|
+
String.raw`(?:\/|$)`,
|
|
538
|
+
String.raw`(?:\\\/|$)`
|
|
539
|
+
].find((boundary) => currentPath.endsWith(boundary));
|
|
540
|
+
if (middlewareBoundary) {
|
|
541
|
+
this.path = `${currentPath.slice(0, -middlewareBoundary.length)}{/*rest}`;
|
|
535
542
|
this.opts.pathAsRegExp = false;
|
|
543
|
+
this.opts.ignoreWildcardParameter = "rest";
|
|
536
544
|
}
|
|
537
545
|
}
|
|
538
546
|
if (isRootPath && !isStrictMode) {
|
|
@@ -559,7 +567,7 @@ var Layer = class {
|
|
|
559
567
|
} else if (isTreatAsRegExp) {
|
|
560
568
|
const pathString = this.path;
|
|
561
569
|
const anchoredPattern = pathString.startsWith("^") ? pathString : `^${pathString}`;
|
|
562
|
-
this.regexp = this.path instanceof RegExp ? this.path : new RegExp(anchoredPattern);
|
|
570
|
+
this.regexp = this.path instanceof RegExp ? this.path : new RegExp(anchoredPattern, this.opts.sensitive ? "" : "i");
|
|
563
571
|
} else {
|
|
564
572
|
const options = normalizeLayerOptionsToPathToRegexp(this.opts);
|
|
565
573
|
const { regexp, keys } = compilePathToRegexp(
|
|
@@ -618,57 +626,14 @@ function applyAllParameterMiddleware(route, parametersObject) {
|
|
|
618
626
|
}
|
|
619
627
|
}
|
|
620
628
|
|
|
621
|
-
// src/utils/path-helpers.ts
|
|
622
|
-
function hasPathParameters(path, options = {}) {
|
|
623
|
-
if (!path) {
|
|
624
|
-
return false;
|
|
625
|
-
}
|
|
626
|
-
const { keys } = compilePathToRegexp(path, options);
|
|
627
|
-
return keys.length > 0;
|
|
628
|
-
}
|
|
629
|
-
function determineMiddlewarePath(explicitPath, hasPrefixParameters) {
|
|
630
|
-
if (explicitPath !== void 0) {
|
|
631
|
-
if (typeof explicitPath === "string") {
|
|
632
|
-
if (explicitPath === "") {
|
|
633
|
-
return {
|
|
634
|
-
path: "{/*rest}",
|
|
635
|
-
pathAsRegExp: false
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
if (explicitPath === "/") {
|
|
639
|
-
return {
|
|
640
|
-
path: "/",
|
|
641
|
-
pathAsRegExp: false
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
return {
|
|
645
|
-
path: explicitPath,
|
|
646
|
-
pathAsRegExp: false
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
return {
|
|
650
|
-
path: explicitPath,
|
|
651
|
-
pathAsRegExp: true
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
if (hasPrefixParameters) {
|
|
655
|
-
return {
|
|
656
|
-
path: "{/*rest}",
|
|
657
|
-
pathAsRegExp: false
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
return {
|
|
661
|
-
path: String.raw`(?:\/|$)`,
|
|
662
|
-
pathAsRegExp: true
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
|
|
666
629
|
// src/utils/debug.ts
|
|
667
630
|
import debugModule from "debug";
|
|
668
631
|
var debug = debugModule("koa-router");
|
|
669
632
|
|
|
670
633
|
// src/router.ts
|
|
671
634
|
var httpMethods = getAllHttpMethods();
|
|
635
|
+
var MIDDLEWARE_BOUNDARY_PATH = String.raw`(?:\/|$)`;
|
|
636
|
+
var MIDDLEWARE_WILDCARD_PATH = "{/*rest}";
|
|
672
637
|
var RouterImplementation = class {
|
|
673
638
|
opts;
|
|
674
639
|
methods;
|
|
@@ -814,7 +779,10 @@ var RouterImplementation = class {
|
|
|
814
779
|
_mountNestedRouter(middlewareWithRouter, mountPath) {
|
|
815
780
|
const nestedRouter = middlewareWithRouter.router;
|
|
816
781
|
const clonedRouter = this._cloneRouter(nestedRouter);
|
|
817
|
-
const
|
|
782
|
+
const isMountPathHasParameters = this._hasPathParameters(mountPath);
|
|
783
|
+
const isRouterPrefixHasParameters = this._hasPathParameters(
|
|
784
|
+
this.opts.prefix
|
|
785
|
+
);
|
|
818
786
|
for (let routeIndex = 0; routeIndex < clonedRouter.stack.length; routeIndex++) {
|
|
819
787
|
const nestedLayer = clonedRouter.stack[routeIndex];
|
|
820
788
|
const clonedLayer = this._cloneLayer(nestedLayer);
|
|
@@ -824,7 +792,7 @@ var RouterImplementation = class {
|
|
|
824
792
|
if (this.opts.prefix) {
|
|
825
793
|
clonedLayer.setPrefix(this.opts.prefix);
|
|
826
794
|
}
|
|
827
|
-
if (clonedLayer.methods.length === 0 &&
|
|
795
|
+
if (clonedLayer.methods.length === 0 && (isMountPathHasParameters || isRouterPrefixHasParameters)) {
|
|
828
796
|
clonedLayer.opts.ignoreCaptures = false;
|
|
829
797
|
}
|
|
830
798
|
this.stack.push(clonedLayer);
|
|
@@ -885,20 +853,14 @@ var RouterImplementation = class {
|
|
|
885
853
|
* @private
|
|
886
854
|
*/
|
|
887
855
|
_registerMiddleware(middleware, explicitPath, hasExplicitPath) {
|
|
888
|
-
const isPrefixHasParameters =
|
|
889
|
-
this.opts.prefix || "",
|
|
890
|
-
this.opts
|
|
891
|
-
);
|
|
856
|
+
const isPrefixHasParameters = this._hasPathParameters(this.opts.prefix);
|
|
892
857
|
const effectiveExplicitPath = (() => {
|
|
893
858
|
if (explicitPath !== void 0) return explicitPath;
|
|
894
859
|
if (isPrefixHasParameters) return "";
|
|
895
860
|
return;
|
|
896
861
|
})();
|
|
897
862
|
const isEffectiveHasExplicitPath = hasExplicitPath || explicitPath === void 0 && isPrefixHasParameters;
|
|
898
|
-
const { path: middlewarePath, pathAsRegExp } =
|
|
899
|
-
effectiveExplicitPath,
|
|
900
|
-
isPrefixHasParameters
|
|
901
|
-
);
|
|
863
|
+
const { path: middlewarePath, pathAsRegExp } = this._determineMiddlewarePath(effectiveExplicitPath);
|
|
902
864
|
let finalPath = middlewarePath;
|
|
903
865
|
let isUsePathToRegexp = pathAsRegExp;
|
|
904
866
|
const isRootPath = isEffectiveHasExplicitPath && middlewarePath === "/";
|
|
@@ -906,7 +868,7 @@ var RouterImplementation = class {
|
|
|
906
868
|
finalPath = middlewarePath;
|
|
907
869
|
isUsePathToRegexp = false;
|
|
908
870
|
}
|
|
909
|
-
const ignoreWildcardParameter = effectiveExplicitPath === "" && middlewarePath ===
|
|
871
|
+
const ignoreWildcardParameter = effectiveExplicitPath === "" && middlewarePath === MIDDLEWARE_WILDCARD_PATH ? "rest" : void 0;
|
|
910
872
|
this.register(finalPath, [], middleware, {
|
|
911
873
|
end: isRootPath,
|
|
912
874
|
ignoreCaptures: !isEffectiveHasExplicitPath && !isPrefixHasParameters,
|
|
@@ -928,19 +890,56 @@ var RouterImplementation = class {
|
|
|
928
890
|
* @returns This router instance
|
|
929
891
|
*/
|
|
930
892
|
prefix(prefixPath) {
|
|
931
|
-
|
|
893
|
+
this._updatePrefix(prefixPath);
|
|
894
|
+
return this;
|
|
895
|
+
}
|
|
896
|
+
_determineMiddlewarePath(explicitPath) {
|
|
897
|
+
if (explicitPath === "") {
|
|
898
|
+
return { path: MIDDLEWARE_WILDCARD_PATH, pathAsRegExp: false };
|
|
899
|
+
}
|
|
900
|
+
if (explicitPath === void 0) {
|
|
901
|
+
return { path: MIDDLEWARE_BOUNDARY_PATH, pathAsRegExp: true };
|
|
902
|
+
}
|
|
903
|
+
const isPathAsRegExp = typeof explicitPath !== "string";
|
|
904
|
+
return { path: explicitPath, pathAsRegExp: isPathAsRegExp };
|
|
905
|
+
}
|
|
906
|
+
_hasPathParameters(path = "") {
|
|
907
|
+
if (typeof path !== "string" || !path) return false;
|
|
908
|
+
const { keys } = compilePathToRegexp(path, this.opts);
|
|
909
|
+
return keys.length > 0;
|
|
910
|
+
}
|
|
911
|
+
_updatePrefix(currentPrefix) {
|
|
932
912
|
const previousPrefix = this.opts.prefix || "";
|
|
913
|
+
const normalizedPrefix = currentPrefix.replace(/\/$/, "");
|
|
914
|
+
const isPrefixHasParameters = this._hasPathParameters(normalizedPrefix);
|
|
933
915
|
this.opts.prefix = normalizedPrefix;
|
|
934
916
|
for (const route of this.stack) {
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
917
|
+
this._updateRoutePrefix(route, previousPrefix);
|
|
918
|
+
this._updateRouteCaptureOptions(route, isPrefixHasParameters);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
_getUnprefixedPath(route, previousPrefix) {
|
|
922
|
+
if (typeof route.path !== "string" || !previousPrefix) return route.path;
|
|
923
|
+
const prefixedMiddlewarePaths = /* @__PURE__ */ new Set([
|
|
924
|
+
previousPrefix + MIDDLEWARE_BOUNDARY_PATH,
|
|
925
|
+
previousPrefix + MIDDLEWARE_WILDCARD_PATH
|
|
926
|
+
]);
|
|
927
|
+
const stripBoundary = previousPrefix + "/";
|
|
928
|
+
const isExactPrefix = route.path === previousPrefix;
|
|
929
|
+
const hasPrefixBoundary = route.path.startsWith(stripBoundary);
|
|
930
|
+
const isPathlessMiddleware = route.methods.length === 0 && prefixedMiddlewarePaths.has(route.path);
|
|
931
|
+
const hasReplaceablePrefix = isExactPrefix || hasPrefixBoundary || isPathlessMiddleware;
|
|
932
|
+
if (!hasReplaceablePrefix) return route.path;
|
|
933
|
+
return route.path.slice(previousPrefix.length) || "/";
|
|
934
|
+
}
|
|
935
|
+
_updateRoutePrefix(route, previousPrefix) {
|
|
936
|
+
route.path = this._getUnprefixedPath(route, previousPrefix);
|
|
937
|
+
route.setPrefix(this.opts.prefix || "");
|
|
938
|
+
}
|
|
939
|
+
_updateRouteCaptureOptions(route, isPrefixHasParameters) {
|
|
940
|
+
if (route.methods.length === 0 && isPrefixHasParameters) {
|
|
941
|
+
route.opts.ignoreCaptures = false;
|
|
942
942
|
}
|
|
943
|
-
return this;
|
|
944
943
|
}
|
|
945
944
|
/**
|
|
946
945
|
* Returns router middleware which dispatches a route matching the request.
|
|
@@ -1019,23 +1018,29 @@ var RouterImplementation = class {
|
|
|
1019
1018
|
*/
|
|
1020
1019
|
_buildMiddlewareChain(matchedLayers, requestPath) {
|
|
1021
1020
|
let layersToExecute;
|
|
1021
|
+
let chosenLayer;
|
|
1022
1022
|
if (this.exclusive) {
|
|
1023
|
-
|
|
1023
|
+
const routeLayers = matchedLayers.filter(
|
|
1024
|
+
(layer) => layer.methods.length > 0
|
|
1025
|
+
);
|
|
1024
1026
|
if (this.opts.exclusive === "specificity") {
|
|
1025
|
-
for (const layer of
|
|
1027
|
+
for (const layer of routeLayers) {
|
|
1026
1028
|
if (!chosenLayer || layer.paramNames.length < chosenLayer.paramNames.length) {
|
|
1027
1029
|
chosenLayer = layer;
|
|
1028
1030
|
}
|
|
1029
1031
|
}
|
|
1030
1032
|
} else {
|
|
1031
|
-
chosenLayer =
|
|
1033
|
+
chosenLayer = routeLayers.at(-1);
|
|
1032
1034
|
}
|
|
1033
|
-
layersToExecute =
|
|
1035
|
+
layersToExecute = matchedLayers.filter(
|
|
1036
|
+
(layer) => layer.methods.length === 0 || layer === chosenLayer
|
|
1037
|
+
);
|
|
1034
1038
|
} else {
|
|
1035
1039
|
layersToExecute = matchedLayers;
|
|
1036
1040
|
}
|
|
1037
1041
|
const middlewareChain = [];
|
|
1038
1042
|
for (const layer of layersToExecute) {
|
|
1043
|
+
const metadataLayer = chosenLayer ?? layer;
|
|
1039
1044
|
middlewareChain.push(
|
|
1040
1045
|
(context, next) => {
|
|
1041
1046
|
const routerContext = context;
|
|
@@ -1046,11 +1051,11 @@ var RouterImplementation = class {
|
|
|
1046
1051
|
routerContext.params
|
|
1047
1052
|
);
|
|
1048
1053
|
routerContext.params = routerContext.request.params;
|
|
1049
|
-
routerContext.routerPath =
|
|
1050
|
-
routerContext.routerName =
|
|
1051
|
-
routerContext._matchedRoute =
|
|
1052
|
-
if (
|
|
1053
|
-
routerContext._matchedRouteName =
|
|
1054
|
+
routerContext.routerPath = metadataLayer.path;
|
|
1055
|
+
routerContext.routerName = metadataLayer.name || void 0;
|
|
1056
|
+
routerContext._matchedRoute = metadataLayer.path;
|
|
1057
|
+
if (this.exclusive || metadataLayer.name) {
|
|
1058
|
+
routerContext._matchedRouteName = metadataLayer.name || void 0;
|
|
1054
1059
|
}
|
|
1055
1060
|
return next();
|
|
1056
1061
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koa/router",
|
|
3
3
|
"description": "Router middleware for Koa.",
|
|
4
|
-
"version": "15.7.
|
|
4
|
+
"version": "15.7.1",
|
|
5
5
|
"author": "Koa.js",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/koajs/router/issues",
|
|
@@ -31,30 +31,30 @@
|
|
|
31
31
|
"path-to-regexp": "^8.4.2"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@commitlint/cli": "^21.2.
|
|
35
|
-
"@commitlint/config-conventional": "^21.2.
|
|
34
|
+
"@commitlint/cli": "^21.2.2",
|
|
35
|
+
"@commitlint/config-conventional": "^21.2.2",
|
|
36
36
|
"@eslint/js": "^10.0.1",
|
|
37
37
|
"@koa/bodyparser": "^6.1.0",
|
|
38
38
|
"@types/debug": "^4.1.13",
|
|
39
39
|
"@types/jsonwebtoken": "^9.0.10",
|
|
40
40
|
"@types/koa": "^3.0.3",
|
|
41
|
-
"@types/node": "^26.
|
|
42
|
-
"@types/supertest": "^7.2.
|
|
43
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
44
|
-
"@typescript-eslint/parser": "^8.
|
|
45
|
-
"c8": "^
|
|
46
|
-
"chalk": "^
|
|
47
|
-
"eslint": "^10.
|
|
48
|
-
"eslint-plugin-unicorn": "^
|
|
41
|
+
"@types/node": "^26.6.2",
|
|
42
|
+
"@types/supertest": "^7.2.1",
|
|
43
|
+
"@typescript-eslint/eslint-plugin": "^8.70.0",
|
|
44
|
+
"@typescript-eslint/parser": "^8.70.0",
|
|
45
|
+
"c8": "^12.0.0",
|
|
46
|
+
"chalk": "^6.0.0",
|
|
47
|
+
"eslint": "^10.11.0",
|
|
48
|
+
"eslint-plugin-unicorn": "^73.0.0",
|
|
49
49
|
"husky": "^9.1.7",
|
|
50
|
-
"joi": "^18.2.
|
|
50
|
+
"joi": "^18.2.9",
|
|
51
51
|
"jsonwebtoken": "^9.0.3",
|
|
52
52
|
"koa": "^3.2.1",
|
|
53
|
-
"lint-staged": "^17.
|
|
54
|
-
"np": "^
|
|
55
|
-
"prettier": "^3.9.
|
|
53
|
+
"lint-staged": "^17.5.1",
|
|
54
|
+
"np": "^12.1.1",
|
|
55
|
+
"prettier": "^3.9.8",
|
|
56
56
|
"rimraf": "^6.1.3",
|
|
57
|
-
"supertest": "^7.
|
|
57
|
+
"supertest": "^7.3.1",
|
|
58
58
|
"ts-node": "^10.9.2",
|
|
59
59
|
"tsup": "^8.5.1",
|
|
60
60
|
"typescript": "^5.9.3"
|
|
@@ -104,6 +104,7 @@
|
|
|
104
104
|
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
|
|
105
105
|
"lint:ts": "eslint src test --fix",
|
|
106
106
|
"lint": "npm run lint:ts",
|
|
107
|
+
"lint:ci": "eslint src test",
|
|
107
108
|
"test:core": "TS_NODE_PROJECT=tsconfig.ts-node.json node --require ts-node/register --test test/*.test.ts test/**/*.test.ts",
|
|
108
109
|
"test:recipes": "TS_NODE_PROJECT=tsconfig.recipes.json node --require ts-node/register --test recipes/**/*.test.ts",
|
|
109
110
|
"pretest:all": "npm run lint && npm run build",
|
|
@@ -115,8 +116,5 @@
|
|
|
115
116
|
"build": "tsup",
|
|
116
117
|
"prepublishOnly": "npm run build",
|
|
117
118
|
"release": "np --test-script test:all"
|
|
118
|
-
},
|
|
119
|
-
"resolutions": {
|
|
120
|
-
"c8/yargs": "^18.0.0"
|
|
121
119
|
}
|
|
122
120
|
}
|