@zudojs/http 1.2.0 → 1.4.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/README.md +221 -0
- package/dist/httpAdapter/http.adapters.d.ts +24 -3
- package/dist/httpAdapter/http.adapters.js +20 -11
- package/dist/httpAdapter/node/httpNode.adapter.d.ts +2 -1
- package/dist/httpAdapter/node/httpNode.adapter.js +17 -2
- package/dist/httpAdapter/node/httpNode.request.js +7 -1
- package/dist/httpAdapter/node/httpNode.type.d.ts +14 -0
- package/dist/httpAgent/http.agent.d.ts +22 -3
- package/dist/httpAgent/http.agent.js +52 -14
- package/dist/httpCacheControl/httpCacheControl.freshness.d.ts +7 -1
- package/dist/httpCacheControl/httpCacheControl.freshness.js +30 -3
- package/dist/httpClient/httpClient.retry.d.ts +17 -12
- package/dist/httpClient/httpClient.retry.js +35 -10
- package/dist/httpClient/httpClient.type.d.ts +14 -0
- package/dist/httpErrors/httpError.base.js +2 -2
- package/dist/httpErrors/httpError.util.d.ts +8 -0
- package/dist/httpErrors/httpError.util.js +12 -0
- package/dist/httpFetchMount/httpFetchMount.core.d.ts +25 -0
- package/dist/httpFetchMount/httpFetchMount.core.js +84 -0
- package/dist/httpFetchMount/httpFetchMount.request.d.ts +21 -0
- package/dist/httpFetchMount/httpFetchMount.request.js +100 -0
- package/dist/httpFetchMount/httpFetchMount.type.d.ts +56 -0
- package/dist/httpFetchMount/httpFetchMount.type.js +5 -0
- package/dist/httpFetchMount/index.d.ts +11 -0
- package/dist/httpFetchMount/index.js +10 -0
- package/dist/httpKeepAlive/httpKeepAlive.core.js +14 -2
- package/dist/httpMiddleware/builtin/logging/httpMiddleware.logging.d.ts +18 -0
- package/dist/httpMiddleware/builtin/logging/httpMiddleware.logging.js +17 -1
- package/dist/httpMiddleware/builtin/rateLimit/httpMiddleware.rateLimit.d.ts +6 -3
- package/dist/httpMiddleware/builtin/rateLimit/httpMiddleware.rateLimit.js +34 -6
- package/dist/httpMiddleware/builtin/security/httpMiddleware.security.d.ts +11 -0
- package/dist/httpMiddleware/builtin/security/httpMiddleware.security.js +25 -12
- package/dist/httpMiddleware/httpMiddleware.type.d.ts +9 -1
- package/dist/httpMiddleware/pipeline/httpPipeline.execution.js +22 -45
- package/dist/httpMiddleware/pipeline/httpPipeline.guardResponse.d.ts +36 -0
- package/dist/httpMiddleware/pipeline/httpPipeline.guardResponse.js +57 -0
- package/dist/httpMiddleware/pipeline/httpPipeline.helper.d.ts +2 -1
- package/dist/httpMiddleware/pipeline/httpPipeline.helper.js +10 -0
- package/dist/httpMiddleware/pipeline/index.d.ts +1 -0
- package/dist/httpMiddleware/pipeline/index.js +1 -0
- package/dist/httpNegotiation/httpNegotiation.core.d.ts +13 -0
- package/dist/httpNegotiation/httpNegotiation.core.js +60 -8
- package/dist/httpOpenApi/httpOpenApi.document.d.ts +44 -0
- package/dist/httpOpenApi/httpOpenApi.document.js +61 -0
- package/dist/httpOpenApi/httpOpenApi.mount.d.ts +31 -0
- package/dist/httpOpenApi/httpOpenApi.mount.js +58 -0
- package/dist/httpOpenApi/httpOpenApi.type.d.ts +54 -0
- package/dist/httpOpenApi/httpOpenApi.type.js +5 -0
- package/dist/httpOpenApi/index.d.ts +13 -0
- package/dist/httpOpenApi/index.js +12 -0
- package/dist/httpOpenApi/routeTable/index.d.ts +11 -0
- package/dist/httpOpenApi/routeTable/index.js +11 -0
- package/dist/httpOpenApi/routeTable/routeTable.collect.d.ts +19 -0
- package/dist/httpOpenApi/routeTable/routeTable.collect.js +89 -0
- package/dist/httpOpenApi/routeTable/routeTable.merge.d.ts +15 -0
- package/dist/httpOpenApi/routeTable/routeTable.merge.js +37 -0
- package/dist/httpOpenApi/routeTable/routeTable.template.d.ts +30 -0
- package/dist/httpOpenApi/routeTable/routeTable.template.js +67 -0
- package/dist/httpProxy/http.proxy.d.ts +9 -0
- package/dist/httpProxy/http.proxy.js +35 -3
- package/dist/httpQuery/index.d.ts +11 -2
- package/dist/httpQuery/index.js +11 -2
- package/dist/httpQuery/queryParse/index.d.ts +10 -0
- package/dist/httpQuery/queryParse/index.js +10 -0
- package/dist/httpQuery/queryParse/queryParse.flat.d.ts +13 -0
- package/dist/httpQuery/queryParse/queryParse.flat.js +44 -0
- package/dist/httpQuery/queryParse/queryParse.nested.d.ts +25 -0
- package/dist/httpQuery/queryParse/queryParse.nested.js +112 -0
- package/dist/httpQuery/queryParse/queryParse.tokenizer.d.ts +37 -0
- package/dist/httpQuery/queryParse/queryParse.tokenizer.js +95 -0
- package/dist/httpQuery/queryRequest/index.d.ts +9 -0
- package/dist/httpQuery/queryRequest/index.js +9 -0
- package/dist/httpQuery/queryRequest/query.request.d.ts +43 -0
- package/dist/httpQuery/queryRequest/query.request.js +96 -0
- package/dist/httpQuery/querySerialize/index.d.ts +10 -0
- package/dist/httpQuery/querySerialize/index.js +10 -0
- package/dist/httpQuery/querySerialize/query.util.d.ts +21 -0
- package/dist/httpQuery/querySerialize/query.util.js +67 -0
- package/dist/httpQuery/querySerialize/querySerialize.core.d.ts +12 -0
- package/dist/httpQuery/querySerialize/querySerialize.core.js +97 -0
- package/dist/httpQuery/queryTypes/index.d.ts +11 -0
- package/dist/httpQuery/queryTypes/index.js +9 -0
- package/dist/httpQuery/queryTypes/query.container.d.ts +16 -0
- package/dist/httpQuery/queryTypes/query.container.js +51 -0
- package/dist/httpQuery/queryTypes/query.limit.d.ts +25 -0
- package/dist/httpQuery/queryTypes/query.limit.js +32 -0
- package/dist/httpQuery/queryTypes/query.type.d.ts +62 -0
- package/dist/httpQuery/queryTypes/query.type.js +2 -0
- package/dist/httpRedirect/http.redirect.d.ts +6 -0
- package/dist/httpRedirect/http.redirect.js +53 -2
- package/dist/httpRequest/http.request.d.ts +61 -2
- package/dist/httpRequest/http.request.js +86 -35
- package/dist/httpRequest/httpRequest.context.d.ts +8 -0
- package/dist/httpRequest/httpRequest.context.js +23 -18
- package/dist/httpRequest/index.d.ts +1 -0
- package/dist/httpRequest/index.js +1 -0
- package/dist/httpRequest/requestId/httpRequest.requestId.d.ts +25 -0
- package/dist/httpRequest/requestId/httpRequest.requestId.js +34 -0
- package/dist/httpRequest/requestId/index.d.ts +7 -0
- package/dist/httpRequest/requestId/index.js +7 -0
- package/dist/httpRequest/target/httpRequest.target.d.ts +2 -2
- package/dist/httpRequest/target/httpRequest.target.js +23 -5
- package/dist/httpResponse/httpResponse.writer.js +15 -0
- package/dist/httpRouter/core/factory/httpRoute.factory.base.d.ts +24 -4
- package/dist/httpRouter/core/factory/httpRoute.factory.base.js +73 -17
- package/dist/httpRouter/core/factory/httpRoute.factory.js +3 -3
- package/dist/httpRouter/core/group/httpRouterGroup.core.js +13 -0
- package/dist/httpRouter/core/register/httpRouter.register.js +19 -21
- package/dist/httpRouter/core/types/httpRouter.type.d.ts +37 -1
- package/dist/httpRouter/core/util/httpRoute.util.d.ts +55 -0
- package/dist/httpRouter/core/util/httpRoute.util.js +101 -4
- package/dist/httpRouter/dispatch/httpRoute.dispatcher.d.ts +11 -0
- package/dist/httpRouter/dispatch/httpRoute.dispatcher.js +25 -6
- package/dist/httpRouter/matching/httpRoute.matcher.core.js +51 -6
- package/dist/httpRouter/matching/httpRoute.matcher.d.ts +0 -1
- package/dist/httpRouter/matching/httpRoute.matcher.js +64 -26
- package/dist/httpRouter/pattern/httpRoute.pattern.parse.d.ts +16 -0
- package/dist/httpRouter/pattern/httpRoute.pattern.parse.js +40 -11
- package/dist/httpRouter/pattern/index.d.ts +1 -1
- package/dist/httpRouter/pattern/index.js +1 -1
- package/dist/httpSecurity/httpSecurity.config.js +4 -1
- package/dist/httpSecurity/httpSecurity.validator.js +16 -7
- package/dist/httpServer/factory/httpServer.factory.d.ts +10 -9
- package/dist/httpServer/factory/httpServer.factory.js +8 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +11 -8
|
@@ -3,11 +3,25 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Core types for routing, route definitions, and router context.
|
|
5
5
|
*/
|
|
6
|
+
import type { RouteOpenAPIMetadata } from "@zudojs/openapi";
|
|
6
7
|
import type { HttpRequestContext as RequestContext } from "../../../httpRequest/httpRequest.context.js";
|
|
7
8
|
import type { HttpResponseContext as ResponseContext } from "../../../httpResponse/httpResponse.context.js";
|
|
8
9
|
import type { HttpMiddleware, HttpMiddlewareContext } from "../../../httpMiddleware/httpMiddleware.type.js";
|
|
9
10
|
export type HttpMethod = "GET" | "HEAD" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "CONNECT" | "TRACE";
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* A plain, JSON-serialisable value a route handler may return: an object,
|
|
13
|
+
* array, string, number, boolean or `null`.
|
|
14
|
+
*/
|
|
15
|
+
export type RouterJsonValue = object | string | number | boolean | null;
|
|
16
|
+
/**
|
|
17
|
+
* What a route handler may return, as server handlers do:
|
|
18
|
+
* - an `HttpResponseContext` or a web `Response`: sent as built;
|
|
19
|
+
* - a plain value: sent as `200` with a JSON body
|
|
20
|
+
* (`return { id }` is `ctx.middleware.response.json({ id })`);
|
|
21
|
+
* - `undefined` / `null`: `204 No Content`.
|
|
22
|
+
*/
|
|
23
|
+
export type RouterHandlerResult = ResponseContext | Response | RouterJsonValue | void;
|
|
24
|
+
export type RouterHandler = (context: HttpRouterContext) => RouterHandlerResult | Promise<RouterHandlerResult>;
|
|
11
25
|
export type RouterHandlerLike = RouterHandler | HttpMiddleware;
|
|
12
26
|
export interface HttpRouterContext {
|
|
13
27
|
readonly request: RequestContext;
|
|
@@ -18,6 +32,14 @@ export interface HttpRouterContext {
|
|
|
18
32
|
readonly middleware: HttpMiddlewareContext;
|
|
19
33
|
readonly signal: AbortSignal;
|
|
20
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* OpenAPI documentation a route carries: summary, tags, operationId,
|
|
37
|
+
* `params` / `query` / `headers` / `body` schemas, responses, security,
|
|
38
|
+
* `deprecated`, `hidden`. It is `@zudojs/openapi`'s `RouteOpenAPIMetadata`,
|
|
39
|
+
* so the schemas it names document the route in `generateOpenAPIDocument`.
|
|
40
|
+
* `false` hides the route from the generated document.
|
|
41
|
+
*/
|
|
42
|
+
export type HttpRouteOpenAPI = RouteOpenAPIMetadata | false;
|
|
21
43
|
export interface RouteDefinition {
|
|
22
44
|
readonly method: HttpMethod | readonly HttpMethod[] | "*";
|
|
23
45
|
readonly path: string;
|
|
@@ -26,12 +48,20 @@ export interface RouteDefinition {
|
|
|
26
48
|
readonly name?: string;
|
|
27
49
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
28
50
|
readonly strictTrailingSlash?: boolean;
|
|
51
|
+
/** OpenAPI documentation; stored as `metadata.openapi`. */
|
|
52
|
+
readonly openapi?: HttpRouteOpenAPI;
|
|
29
53
|
}
|
|
30
54
|
export interface RouteOptions {
|
|
31
55
|
readonly name?: string;
|
|
32
56
|
readonly middleware?: readonly HttpMiddleware[];
|
|
33
57
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
34
58
|
readonly strictTrailingSlash?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* OpenAPI documentation; stored as `metadata.openapi`, where
|
|
61
|
+
* `generateOpenAPIDocument` reads it. Takes precedence over a
|
|
62
|
+
* `metadata.openapi` passed alongside it.
|
|
63
|
+
*/
|
|
64
|
+
readonly openapi?: HttpRouteOpenAPI;
|
|
35
65
|
}
|
|
36
66
|
export interface MatchedRoute {
|
|
37
67
|
readonly id: string;
|
|
@@ -104,6 +134,12 @@ export interface CompiledRoute {
|
|
|
104
134
|
readonly segments: readonly CompiledSegment[];
|
|
105
135
|
readonly score: number;
|
|
106
136
|
readonly strictTrailingSlash: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Whether the registered pattern ended with a slash.
|
|
139
|
+
*
|
|
140
|
+
* Only consulted when {@link CompiledRoute.strictTrailingSlash} is set.
|
|
141
|
+
*/
|
|
142
|
+
readonly expectsTrailingSlash?: boolean;
|
|
107
143
|
}
|
|
108
144
|
export { HttpRouterError, RouteConflictError, } from "../error/httpRouter.error.js";
|
|
109
145
|
//# sourceMappingURL=httpRouter.type.d.ts.map
|
|
@@ -8,6 +8,14 @@ import type { HttpRequestContext as RequestContext } from "../../../httpRequest/
|
|
|
8
8
|
export declare function getRequestMethod(request: RequestContext): string;
|
|
9
9
|
export declare function getRequestUrl(request: RequestContext): string;
|
|
10
10
|
export declare function getRequestSignal(request: RequestContext): AbortSignal | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Copies the matched route's parameters onto the request before any route
|
|
13
|
+
* middleware runs, so `request.getParam("id")` / `request.params` see them
|
|
14
|
+
* in middleware as well as in the handler. They were only on the router
|
|
15
|
+
* context, so a guard reading the request (a permissions
|
|
16
|
+
* `extractResource`, an ownership check) always saw `undefined` and denied.
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyRouteParams(request: RequestContext, params: Readonly<Record<string, string>>): void;
|
|
11
19
|
/**
|
|
12
20
|
* Parses a request-target with the canonical parser shared by the request
|
|
13
21
|
* context and path-scoped middleware.
|
|
@@ -24,8 +32,55 @@ export declare function parseUrl(value: string): URL;
|
|
|
24
32
|
* for the same parameter.
|
|
25
33
|
*/
|
|
26
34
|
export declare function parseQuery(params: URLSearchParams): Readonly<Record<string, string | string[]>>;
|
|
35
|
+
/**
|
|
36
|
+
* Normalizes a **request** path.
|
|
37
|
+
*
|
|
38
|
+
* Strips the query string, forces a leading slash, collapses repeated
|
|
39
|
+
* slashes, and trims a trailing slash.
|
|
40
|
+
*
|
|
41
|
+
* This must not be used on a route *pattern*: a pattern may legitimately
|
|
42
|
+
* contain `?` (the optional-parameter marker), which this function treats as
|
|
43
|
+
* the start of a query string. Use {@link normalizeRoutePattern} for
|
|
44
|
+
* patterns.
|
|
45
|
+
*/
|
|
27
46
|
export declare function normalizePath(path: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Normalizes a request path while preserving a single trailing slash.
|
|
49
|
+
*
|
|
50
|
+
* The trailing slash is the only information a strict-trailing-slash route
|
|
51
|
+
* needs and {@link normalizePath} destroys it, so matching runs on this
|
|
52
|
+
* spelling instead.
|
|
53
|
+
*
|
|
54
|
+
* @param path - The raw request path, possibly with a query or fragment.
|
|
55
|
+
* @returns The normalized path, keeping one trailing slash if present.
|
|
56
|
+
*/
|
|
57
|
+
export declare function normalizeMatchPath(path: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Normalizes a route **pattern**.
|
|
60
|
+
*
|
|
61
|
+
* Identical to {@link normalizePath} except that `?` is left alone, so the
|
|
62
|
+
* documented optional-parameter syntax (`/users/:id?`, `/files/{name?}`)
|
|
63
|
+
* survives registration.
|
|
64
|
+
*
|
|
65
|
+
* @param pattern - The raw route pattern.
|
|
66
|
+
* @returns The normalized pattern.
|
|
67
|
+
*/
|
|
68
|
+
export declare function normalizeRoutePattern(pattern: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* Reports whether a path carries a meaningful trailing slash.
|
|
71
|
+
*
|
|
72
|
+
* @param path - The path to inspect.
|
|
73
|
+
* @returns `true` when the path ends with `/` and is not the root path.
|
|
74
|
+
*/
|
|
75
|
+
export declare function hasTrailingSlash(path: string): boolean;
|
|
28
76
|
export declare function splitPath(path: string): string[];
|
|
77
|
+
/**
|
|
78
|
+
* Splits a route pattern into its segments, keeping `?` markers intact.
|
|
79
|
+
*
|
|
80
|
+
* @param pattern - The raw route pattern.
|
|
81
|
+
* @returns The pattern's non-empty segments.
|
|
82
|
+
*/
|
|
83
|
+
export declare function splitRoutePattern(pattern: string): string[];
|
|
29
84
|
export declare function validateParameterName(name: string, path: string): void;
|
|
30
85
|
export declare function decodeRouteValue(value: string): string;
|
|
31
86
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* router core.
|
|
6
6
|
*/
|
|
7
7
|
import { parseRequestTarget } from "../../../httpRequest/target/httpRequest.target.js";
|
|
8
|
-
import { parseQueryString } from "../../../httpQuery/
|
|
8
|
+
import { parseQueryString } from "../../../httpQuery/index.js";
|
|
9
9
|
import { InvalidRoutePatternError } from "../error/httpRouter.error.js";
|
|
10
10
|
export function getRequestMethod(request) {
|
|
11
11
|
const value = request.method;
|
|
@@ -21,6 +21,22 @@ export function getRequestUrl(request) {
|
|
|
21
21
|
export function getRequestSignal(request) {
|
|
22
22
|
return request.signal;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Copies the matched route's parameters onto the request before any route
|
|
26
|
+
* middleware runs, so `request.getParam("id")` / `request.params` see them
|
|
27
|
+
* in middleware as well as in the handler. They were only on the router
|
|
28
|
+
* context, so a guard reading the request (a permissions
|
|
29
|
+
* `extractResource`, an ownership check) always saw `undefined` and denied.
|
|
30
|
+
*/
|
|
31
|
+
export function applyRouteParams(request, params) {
|
|
32
|
+
const target = request;
|
|
33
|
+
if (typeof target.setParam !== "function") {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
for (const [name, value] of Object.entries(params)) {
|
|
37
|
+
target.setParam(name, value);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
24
40
|
/**
|
|
25
41
|
* Parses a request-target with the canonical parser shared by the request
|
|
26
42
|
* context and path-scoped middleware.
|
|
@@ -44,14 +60,82 @@ export function parseQuery(params) {
|
|
|
44
60
|
/* -------------------------------------------------------------------------- */
|
|
45
61
|
/* Path Helpers */
|
|
46
62
|
/* -------------------------------------------------------------------------- */
|
|
63
|
+
/**
|
|
64
|
+
* Normalizes a **request** path.
|
|
65
|
+
*
|
|
66
|
+
* Strips the query string, forces a leading slash, collapses repeated
|
|
67
|
+
* slashes, and trims a trailing slash.
|
|
68
|
+
*
|
|
69
|
+
* This must not be used on a route *pattern*: a pattern may legitimately
|
|
70
|
+
* contain `?` (the optional-parameter marker), which this function treats as
|
|
71
|
+
* the start of a query string. Use {@link normalizeRoutePattern} for
|
|
72
|
+
* patterns.
|
|
73
|
+
*/
|
|
47
74
|
export function normalizePath(path) {
|
|
48
75
|
if (!path || path === "") {
|
|
49
76
|
return "/";
|
|
50
77
|
}
|
|
51
78
|
const withoutQuery = path.split("?", 1)[0] ?? path;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
79
|
+
return collapsePath(withoutQuery);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Normalizes a request path while preserving a single trailing slash.
|
|
83
|
+
*
|
|
84
|
+
* The trailing slash is the only information a strict-trailing-slash route
|
|
85
|
+
* needs and {@link normalizePath} destroys it, so matching runs on this
|
|
86
|
+
* spelling instead.
|
|
87
|
+
*
|
|
88
|
+
* @param path - The raw request path, possibly with a query or fragment.
|
|
89
|
+
* @returns The normalized path, keeping one trailing slash if present.
|
|
90
|
+
*/
|
|
91
|
+
export function normalizeMatchPath(path) {
|
|
92
|
+
if (!path || path.trim() === "") {
|
|
93
|
+
return "/";
|
|
94
|
+
}
|
|
95
|
+
let target = path.trim();
|
|
96
|
+
if (target.startsWith("http://") || target.startsWith("https://")) {
|
|
97
|
+
try {
|
|
98
|
+
target = new URL(target).pathname;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
/* Keep the original value if it is not a valid absolute URL. */
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const withoutQuery = target.split("?", 1)[0] ?? target;
|
|
105
|
+
const withoutHash = withoutQuery.split("#", 1)[0] ?? withoutQuery;
|
|
106
|
+
const collapsed = collapsePath(withoutHash);
|
|
107
|
+
return hasTrailingSlash(withoutHash) && collapsed !== "/"
|
|
108
|
+
? `${collapsed}/`
|
|
109
|
+
: collapsed;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Normalizes a route **pattern**.
|
|
113
|
+
*
|
|
114
|
+
* Identical to {@link normalizePath} except that `?` is left alone, so the
|
|
115
|
+
* documented optional-parameter syntax (`/users/:id?`, `/files/{name?}`)
|
|
116
|
+
* survives registration.
|
|
117
|
+
*
|
|
118
|
+
* @param pattern - The raw route pattern.
|
|
119
|
+
* @returns The normalized pattern.
|
|
120
|
+
*/
|
|
121
|
+
export function normalizeRoutePattern(pattern) {
|
|
122
|
+
if (!pattern || pattern === "") {
|
|
123
|
+
return "/";
|
|
124
|
+
}
|
|
125
|
+
return collapsePath(pattern);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Reports whether a path carries a meaningful trailing slash.
|
|
129
|
+
*
|
|
130
|
+
* @param path - The path to inspect.
|
|
131
|
+
* @returns `true` when the path ends with `/` and is not the root path.
|
|
132
|
+
*/
|
|
133
|
+
export function hasTrailingSlash(path) {
|
|
134
|
+
const trimmed = path.replace(/\/{2,}/g, "/");
|
|
135
|
+
return trimmed.length > 1 && trimmed.endsWith("/");
|
|
136
|
+
}
|
|
137
|
+
function collapsePath(value) {
|
|
138
|
+
let normalized = value.startsWith("/") ? value : `/${value}`;
|
|
55
139
|
normalized = normalized.replace(/\/{2,}/g, "/");
|
|
56
140
|
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
57
141
|
normalized = normalized.slice(0, -1);
|
|
@@ -65,6 +149,19 @@ export function splitPath(path) {
|
|
|
65
149
|
}
|
|
66
150
|
return normalized.split("/").filter(Boolean);
|
|
67
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Splits a route pattern into its segments, keeping `?` markers intact.
|
|
154
|
+
*
|
|
155
|
+
* @param pattern - The raw route pattern.
|
|
156
|
+
* @returns The pattern's non-empty segments.
|
|
157
|
+
*/
|
|
158
|
+
export function splitRoutePattern(pattern) {
|
|
159
|
+
const normalized = normalizeRoutePattern(pattern);
|
|
160
|
+
if (normalized === "/") {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
return normalized.split("/").filter(Boolean);
|
|
164
|
+
}
|
|
68
165
|
export function validateParameterName(name, path) {
|
|
69
166
|
if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name)) {
|
|
70
167
|
throw new InvalidRoutePatternError(path, `Invalid parameter name "${name}".`);
|
|
@@ -24,6 +24,17 @@ export interface RouteDispatchContext {
|
|
|
24
24
|
export interface RouteDispatchOptions {
|
|
25
25
|
readonly onError?: RouteDispatchErrorHandler;
|
|
26
26
|
readonly onComplete?: RouteDispatchCompleteHandler;
|
|
27
|
+
/**
|
|
28
|
+
* Keeps the response the middleware chain produced.
|
|
29
|
+
*
|
|
30
|
+
* By default a response returned by a route handler is merged into the
|
|
31
|
+
* response passed to `dispatch()`, overwriting its status, headers, cookies
|
|
32
|
+
* and body. With this set, the handler's response is left alone and the
|
|
33
|
+
* dispatch response is returned exactly as the middleware chain built it.
|
|
34
|
+
*
|
|
35
|
+
* The option was previously declared and never read, so it silently did
|
|
36
|
+
* nothing.
|
|
37
|
+
*/
|
|
27
38
|
readonly preserveResponse?: boolean;
|
|
28
39
|
}
|
|
29
40
|
export type RouteDispatchErrorHandler = (error: unknown, context: RouteDispatchContext) => void | Promise<void>;
|
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
* handler pipeline. Route registration and route matching remain separate
|
|
6
6
|
* concerns.
|
|
7
7
|
*/
|
|
8
|
+
import { isGuardResponse } from "@zudojs/middleware";
|
|
9
|
+
import { applyGuardResponse } from "../../httpMiddleware/pipeline/httpPipeline.guardResponse.js";
|
|
8
10
|
import { HttpResponseContext } from "../../httpResponse/httpResponse.context.js";
|
|
9
11
|
import { createRouterContext } from "../httpRouter.context.js";
|
|
12
|
+
import { normalizeResponse } from "../core/factory/httpRoute.factory.base.js";
|
|
10
13
|
import { RouterMiddlewareState } from "../httpRouter.state.js";
|
|
11
|
-
import { getRequestSignal } from "../core/util/httpRoute.util.js";
|
|
14
|
+
import { applyRouteParams, getRequestSignal, } from "../core/util/httpRoute.util.js";
|
|
12
15
|
/* -------------------------------------------------------------------------- */
|
|
13
16
|
/* Route Dispatcher */
|
|
14
17
|
/* -------------------------------------------------------------------------- */
|
|
@@ -104,7 +107,7 @@ export class RouteDispatcher {
|
|
|
104
107
|
async execute(context) {
|
|
105
108
|
const state = new RouterMiddlewareState();
|
|
106
109
|
const middleware = context.route.middleware.map((layer) => toRouteMiddleware(layer, context.route, state));
|
|
107
|
-
const handler = toDispatchHandler(context.route.handler, context);
|
|
110
|
+
const handler = toDispatchHandler(context.route.handler, context, this.options.preserveResponse === true);
|
|
108
111
|
let index = -1;
|
|
109
112
|
const dispatchNext = async (current) => {
|
|
110
113
|
if (current <= index) {
|
|
@@ -177,6 +180,8 @@ export async function dispatchRoute(dispatcher, request, response) {
|
|
|
177
180
|
/* Context Creation */
|
|
178
181
|
/* -------------------------------------------------------------------------- */
|
|
179
182
|
function createDispatchContext(request, response, match) {
|
|
183
|
+
/* Route middleware reads params from the request, so set them first. */
|
|
184
|
+
applyRouteParams(request, match.params);
|
|
180
185
|
return Object.freeze({
|
|
181
186
|
request,
|
|
182
187
|
response,
|
|
@@ -220,15 +225,25 @@ function normalizeHandler(handler) {
|
|
|
220
225
|
* A response context returned by the handler is merged into the dispatch
|
|
221
226
|
* response so the dispatcher's response object stays authoritative.
|
|
222
227
|
*/
|
|
223
|
-
function toDispatchHandler(handler, context) {
|
|
228
|
+
function toDispatchHandler(handler, context, preserveResponse = false) {
|
|
224
229
|
return async (request, response) => {
|
|
225
|
-
const
|
|
230
|
+
const returned = await handler(createRouterContext({
|
|
226
231
|
request,
|
|
227
232
|
route: context.route,
|
|
228
233
|
params: context.params,
|
|
229
234
|
signal: getRequestSignal(request),
|
|
230
235
|
}));
|
|
231
|
-
|
|
236
|
+
/* A web Response or a plain value becomes a response context, exactly
|
|
237
|
+
* as the router treats it (a plain value is a 200 JSON body). */
|
|
238
|
+
const result = preserveResponse ||
|
|
239
|
+
returned === undefined ||
|
|
240
|
+
returned === null ||
|
|
241
|
+
returned instanceof HttpResponseContext
|
|
242
|
+
? returned
|
|
243
|
+
: await normalizeResponse(returned);
|
|
244
|
+
if (!preserveResponse &&
|
|
245
|
+
result instanceof HttpResponseContext &&
|
|
246
|
+
result !== response) {
|
|
232
247
|
/*
|
|
233
248
|
* Everything the handler put on its response must survive the merge.
|
|
234
249
|
* Copying only status, headers and body dropped every cookie the
|
|
@@ -252,7 +267,7 @@ function toDispatchHandler(handler, context) {
|
|
|
252
267
|
*/
|
|
253
268
|
function toRouteMiddleware(middleware, route, state) {
|
|
254
269
|
return async (request, response, next) => {
|
|
255
|
-
await middleware({
|
|
270
|
+
const result = await middleware({
|
|
256
271
|
request,
|
|
257
272
|
response,
|
|
258
273
|
state,
|
|
@@ -262,6 +277,10 @@ function toRouteMiddleware(middleware, route, state) {
|
|
|
262
277
|
await next();
|
|
263
278
|
return response;
|
|
264
279
|
});
|
|
280
|
+
/* A guard's refusal is written onto the dispatch response, not dropped. */
|
|
281
|
+
if (isGuardResponse(result)) {
|
|
282
|
+
applyGuardResponse(response, result);
|
|
283
|
+
}
|
|
265
284
|
};
|
|
266
285
|
}
|
|
267
286
|
function normalizeMiddleware(middleware) {
|
|
@@ -5,14 +5,28 @@
|
|
|
5
5
|
* case sensitivity, optional parameters, per-parameter regular expression
|
|
6
6
|
* constraints, and trailing wildcards.
|
|
7
7
|
*/
|
|
8
|
-
import { decodeRouteSegment, normalizePath, splitPath, } from "../core/util/httpRoute.util.js";
|
|
8
|
+
import { decodeRouteSegment, hasTrailingSlash, normalizePath, splitPath, } from "../core/util/httpRoute.util.js";
|
|
9
9
|
export function matchCompiledRoute(route, path, caseSensitive) {
|
|
10
|
+
/*
|
|
11
|
+
* `strictTrailingSlash` used to be compiled and stored but never read, so a
|
|
12
|
+
* strict router still answered `/users/` with the `/users` route. The
|
|
13
|
+
* request path reaches this function with its trailing slash intact
|
|
14
|
+
* (`normalizeMatchPath`), so the two spellings can finally be told apart.
|
|
15
|
+
*/
|
|
16
|
+
if (route.strictTrailingSlash &&
|
|
17
|
+
hasTrailingSlash(path) !== (route.expectsTrailingSlash ?? false)) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
10
20
|
const routePath = normalizePath(path);
|
|
11
21
|
const inputSegments = splitPath(routePath);
|
|
12
22
|
const output = {};
|
|
13
23
|
const routeSegments = route.segments;
|
|
14
24
|
let inputIndex = 0;
|
|
15
|
-
for (
|
|
25
|
+
for (let segmentIndex = 0; segmentIndex < routeSegments.length; segmentIndex += 1) {
|
|
26
|
+
const segment = routeSegments[segmentIndex];
|
|
27
|
+
if (segment === undefined) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
16
30
|
if (segment.type === "wildcard") {
|
|
17
31
|
/*
|
|
18
32
|
* Decode each segment separately and re-join. Decoding the joined tail
|
|
@@ -33,10 +47,18 @@ export function matchCompiledRoute(route, path, caseSensitive) {
|
|
|
33
47
|
break;
|
|
34
48
|
}
|
|
35
49
|
const input = inputSegments[inputIndex];
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
if (segment.type === "parameter" && segment.optional) {
|
|
51
|
+
/*
|
|
52
|
+
* An optional parameter only claims a segment when the segments that
|
|
53
|
+
* follow it still have enough input left. Without that check
|
|
54
|
+
* `/account/:id?/profile` swallowed `profile` as the id and then found
|
|
55
|
+
* nothing to match its literal tail.
|
|
56
|
+
*/
|
|
57
|
+
const remainingInput = inputSegments.length - inputIndex;
|
|
58
|
+
if (input === undefined ||
|
|
59
|
+
remainingInput <= requiredSegments(routeSegments, segmentIndex + 1)) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
40
62
|
}
|
|
41
63
|
if (input === undefined) {
|
|
42
64
|
return undefined;
|
|
@@ -69,4 +91,27 @@ export function matchCompiledRoute(route, path, caseSensitive) {
|
|
|
69
91
|
}
|
|
70
92
|
return Object.freeze(output);
|
|
71
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Counts the segments from `start` onwards that must consume input.
|
|
96
|
+
*
|
|
97
|
+
* Optional parameters and wildcards can match nothing, so they do not count.
|
|
98
|
+
*
|
|
99
|
+
* @param segments - The route's compiled segments.
|
|
100
|
+
* @param start - The index to count from.
|
|
101
|
+
* @returns The number of segments that require an input segment.
|
|
102
|
+
*/
|
|
103
|
+
function requiredSegments(segments, start) {
|
|
104
|
+
let required = 0;
|
|
105
|
+
for (let index = start; index < segments.length; index += 1) {
|
|
106
|
+
const segment = segments[index];
|
|
107
|
+
if (segment === undefined || segment.type === "wildcard") {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (segment.type === "parameter" && segment.optional) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
required += 1;
|
|
114
|
+
}
|
|
115
|
+
return required;
|
|
116
|
+
}
|
|
72
117
|
//# sourceMappingURL=httpRoute.matcher.core.js.map
|
|
@@ -51,7 +51,6 @@ export declare class RouteMatcher {
|
|
|
51
51
|
stats(): RouteMatcherStats;
|
|
52
52
|
resetStats(): void;
|
|
53
53
|
private collect;
|
|
54
|
-
private matchFirstAllowed;
|
|
55
54
|
}
|
|
56
55
|
export declare function createRouteMatcher(router: HttpRouter, options?: RouteMatcherOptions): RouteMatcher;
|
|
57
56
|
export declare function matchRoute(matcher: RouteMatcher, method: string, path: string): RouteMatcherResult | undefined;
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* pathname. Route registration lives in the router core; per-route segment
|
|
6
6
|
* matching lives in `httpRoute.matcher.core.ts`.
|
|
7
7
|
*/
|
|
8
|
-
import { normalizeMethod } from "../core/factory/httpRoute.factory.base.js";
|
|
8
|
+
import { createOptionsResponse, normalizeMethod, } from "../core/factory/httpRoute.factory.base.js";
|
|
9
|
+
import { normalizeMatchPath } from "../core/util/httpRoute.util.js";
|
|
9
10
|
import { formatAllowHeader } from "../../httpMethods/http.methods.js";
|
|
10
11
|
import { matchCompiledRoute } from "./httpRoute.matcher.core.js";
|
|
11
12
|
/* -------------------------------------------------------------------------- */
|
|
@@ -31,14 +32,20 @@ export class RouteMatcher {
|
|
|
31
32
|
match(request) {
|
|
32
33
|
this.requests += 1;
|
|
33
34
|
const method = normalizeMethod(request.method);
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
/*
|
|
36
|
+
* The raw target is threaded through so a strict-trailing-slash route can
|
|
37
|
+
* still tell `/users` from `/users/`; only the reported path is
|
|
38
|
+
* normalized.
|
|
39
|
+
*/
|
|
40
|
+
const target = request.path;
|
|
41
|
+
const path = normalizeRequestPath(target);
|
|
42
|
+
const direct = this.matchMethod(method, target);
|
|
36
43
|
if (direct) {
|
|
37
44
|
this.matches += 1;
|
|
38
45
|
return direct;
|
|
39
46
|
}
|
|
40
47
|
if (method === "HEAD" && this.matcherOptions.allowHeadFallback) {
|
|
41
|
-
const fallback = this.matchMethod("GET",
|
|
48
|
+
const fallback = this.matchMethod("GET", target);
|
|
42
49
|
if (fallback) {
|
|
43
50
|
this.matches += 1;
|
|
44
51
|
return {
|
|
@@ -48,13 +55,25 @@ export class RouteMatcher {
|
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
if (method === "OPTIONS" && this.matcherOptions.allowOptionsFallback) {
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
58
|
+
const allowed = this.allowedMethods(target);
|
|
59
|
+
if (allowed.allowed) {
|
|
53
60
|
this.matches += 1;
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
/*
|
|
62
|
+
* The fallback must never carry an executable route. It used to
|
|
63
|
+
* answer with the first route registered on the path under ANY
|
|
64
|
+
* method, so `OPTIONS /accounts/42` ran the `DELETE` handler — past
|
|
65
|
+
* any CSRF guard that treats `OPTIONS` as safe. The synthetic route
|
|
66
|
+
* below owns no middleware and answers `204` with `Allow`, matching
|
|
67
|
+
* what `HttpRouter.match()` already does.
|
|
68
|
+
*/
|
|
69
|
+
return Object.freeze({
|
|
70
|
+
route: createOptionsRoute(path, allowed),
|
|
71
|
+
params: Object.freeze({}),
|
|
72
|
+
method,
|
|
73
|
+
path,
|
|
74
|
+
score: 0,
|
|
56
75
|
matchedBy: "options-fallback",
|
|
57
|
-
};
|
|
76
|
+
});
|
|
58
77
|
}
|
|
59
78
|
}
|
|
60
79
|
this.misses += 1;
|
|
@@ -63,11 +82,12 @@ export class RouteMatcher {
|
|
|
63
82
|
matchMethod(method, path) {
|
|
64
83
|
const normalizedMethod = normalizeMethod(method);
|
|
65
84
|
const normalizedPath = normalizeRequestPath(path);
|
|
85
|
+
const matchPath = normalizeMatchPath(path);
|
|
66
86
|
for (const route of this.router.compiled()) {
|
|
67
87
|
if (!methodApplies(route.definition.method, normalizedMethod)) {
|
|
68
88
|
continue;
|
|
69
89
|
}
|
|
70
|
-
const params = matchCompiledRoute(route,
|
|
90
|
+
const params = matchCompiledRoute(route, matchPath, this.matcherOptions.caseSensitive);
|
|
71
91
|
if (!params) {
|
|
72
92
|
continue;
|
|
73
93
|
}
|
|
@@ -80,23 +100,22 @@ export class RouteMatcher {
|
|
|
80
100
|
/* ------------------------------------------------------------------------ */
|
|
81
101
|
matchAll(request) {
|
|
82
102
|
const method = normalizeMethod(request.method);
|
|
83
|
-
|
|
84
|
-
return this.collect(method, path);
|
|
103
|
+
return this.collect(method, request.path);
|
|
85
104
|
}
|
|
86
105
|
/* ------------------------------------------------------------------------ */
|
|
87
106
|
/* Path Matching */
|
|
88
107
|
/* ------------------------------------------------------------------------ */
|
|
89
108
|
matchPath(path) {
|
|
90
|
-
return this.collect("*",
|
|
109
|
+
return this.collect("*", path);
|
|
91
110
|
}
|
|
92
111
|
/* ------------------------------------------------------------------------ */
|
|
93
112
|
/* Allowed Methods */
|
|
94
113
|
/* ------------------------------------------------------------------------ */
|
|
95
114
|
allowedMethods(path) {
|
|
96
|
-
const
|
|
115
|
+
const matchPath = normalizeMatchPath(path);
|
|
97
116
|
const methods = new Set();
|
|
98
117
|
for (const route of this.router.compiled()) {
|
|
99
|
-
const params = matchCompiledRoute(route,
|
|
118
|
+
const params = matchCompiledRoute(route, matchPath, this.matcherOptions.caseSensitive);
|
|
100
119
|
if (params) {
|
|
101
120
|
methods.add(route.definition.method);
|
|
102
121
|
}
|
|
@@ -140,29 +159,22 @@ export class RouteMatcher {
|
|
|
140
159
|
/* ------------------------------------------------------------------------ */
|
|
141
160
|
collect(method, path) {
|
|
142
161
|
const results = [];
|
|
162
|
+
const normalizedPath = normalizeRequestPath(path);
|
|
163
|
+
const matchPath = normalizeMatchPath(path);
|
|
143
164
|
const seen = new Set();
|
|
144
165
|
for (const route of this.router.compiled()) {
|
|
145
166
|
if (!methodApplies(route.definition.method, method)) {
|
|
146
167
|
continue;
|
|
147
168
|
}
|
|
148
|
-
const params = matchCompiledRoute(route,
|
|
169
|
+
const params = matchCompiledRoute(route, matchPath, this.matcherOptions.caseSensitive);
|
|
149
170
|
if (!params || seen.has(route.definition.id)) {
|
|
150
171
|
continue;
|
|
151
172
|
}
|
|
152
173
|
seen.add(route.definition.id);
|
|
153
|
-
results.push(createResult(route, params, method,
|
|
174
|
+
results.push(createResult(route, params, method, normalizedPath));
|
|
154
175
|
}
|
|
155
176
|
return Object.freeze(results);
|
|
156
177
|
}
|
|
157
|
-
matchFirstAllowed(methods, path) {
|
|
158
|
-
for (const method of methods) {
|
|
159
|
-
const result = this.matchMethod(method, path);
|
|
160
|
-
if (result) {
|
|
161
|
-
return result;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
178
|
}
|
|
167
179
|
/* -------------------------------------------------------------------------- */
|
|
168
180
|
/* Factory */
|
|
@@ -200,6 +212,32 @@ function createResult(route, params, method, path) {
|
|
|
200
212
|
matchedBy: route.definition.method === "*" ? "wildcard" : "exact",
|
|
201
213
|
});
|
|
202
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Builds the synthetic route an `OPTIONS` fallback resolves to.
|
|
217
|
+
*
|
|
218
|
+
* It carries no middleware and a handler that only answers `204` with the
|
|
219
|
+
* path's `Allow` header, so an `OPTIONS` request can never reach a handler
|
|
220
|
+
* registered for another method.
|
|
221
|
+
*
|
|
222
|
+
* @param path - The normalized request path.
|
|
223
|
+
* @param allowed - The methods registered on that path.
|
|
224
|
+
* @returns The synthetic route definition.
|
|
225
|
+
*/
|
|
226
|
+
function createOptionsRoute(path, allowed) {
|
|
227
|
+
const methods = allowed.methods.includes("OPTIONS")
|
|
228
|
+
? allowed.methods
|
|
229
|
+
: [...allowed.methods, "OPTIONS"];
|
|
230
|
+
return Object.freeze({
|
|
231
|
+
id: "route:options-fallback",
|
|
232
|
+
method: "OPTIONS",
|
|
233
|
+
path,
|
|
234
|
+
name: undefined,
|
|
235
|
+
params: Object.freeze({}),
|
|
236
|
+
metadata: Object.freeze({}),
|
|
237
|
+
handler: () => createOptionsResponse(methods),
|
|
238
|
+
middleware: Object.freeze([]),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
203
241
|
/* -------------------------------------------------------------------------- */
|
|
204
242
|
/* Method Utilities */
|
|
205
243
|
/* -------------------------------------------------------------------------- */
|
|
@@ -20,6 +20,7 @@ export interface CompiledRoutePath {
|
|
|
20
20
|
readonly segments: readonly CompiledSegment[];
|
|
21
21
|
readonly score: number;
|
|
22
22
|
readonly strictTrailingSlash: boolean;
|
|
23
|
+
readonly expectsTrailingSlash: boolean;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
26
|
* Compiles a route path into its segments.
|
|
@@ -35,4 +36,19 @@ export declare function compileRoute(path: string, strictTrailingSlash: boolean)
|
|
|
35
36
|
* Scores compiled segments by specificity.
|
|
36
37
|
*/
|
|
37
38
|
export declare function scoreSegments(segments: readonly CompiledSegment[]): number;
|
|
39
|
+
/**
|
|
40
|
+
* Compares two compiled patterns by specificity, most specific first.
|
|
41
|
+
*
|
|
42
|
+
* Segments are compared left to right by kind (literal, then parameter, then
|
|
43
|
+
* wildcard), which is how a router is expected to rank patterns. Summing the
|
|
44
|
+
* kinds into one scalar — as this used to — let a longer but entirely
|
|
45
|
+
* parameterised pattern such as `/:p/:q/:r/:s` outrank a literal-anchored
|
|
46
|
+
* `/admin/*rest`, so a request to `/admin/a/b/c` bypassed the admin route and
|
|
47
|
+
* every guard registered on it.
|
|
48
|
+
*
|
|
49
|
+
* @param left - The first pattern's segments.
|
|
50
|
+
* @param right - The second pattern's segments.
|
|
51
|
+
* @returns A negative number when `left` is more specific.
|
|
52
|
+
*/
|
|
53
|
+
export declare function compareSegmentSpecificity(left: readonly CompiledSegment[], right: readonly CompiledSegment[]): number;
|
|
38
54
|
//# sourceMappingURL=httpRoute.pattern.parse.d.ts.map
|