clear-router 2.8.9 → 2.9.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/README.md +15 -1
- package/dist/Route.cjs +200 -12
- package/dist/Route.d.cts +125 -1
- package/dist/Route.d.mts +125 -1
- package/dist/Route.mjs +199 -12
- package/dist/RouteGroup.cjs +1 -0
- package/dist/RouteGroup.mjs +1 -0
- package/dist/RouteRegistrar.cjs +68 -0
- package/dist/RouteRegistrar.d.cts +62 -0
- package/dist/RouteRegistrar.d.mts +62 -0
- package/dist/RouteRegistrar.mjs +67 -0
- package/dist/core/CoreRouter.cjs +309 -0
- package/dist/core/CoreRouter.d.cts +168 -0
- package/dist/core/CoreRouter.d.mts +168 -0
- package/dist/core/CoreRouter.mjs +309 -0
- package/dist/core/index.cjs +3 -0
- package/dist/core/index.d.cts +2 -1
- package/dist/core/index.d.mts +2 -1
- package/dist/core/index.mjs +2 -1
- package/dist/decorators/index.cjs +4 -1
- package/dist/decorators/index.d.cts +2 -1
- package/dist/decorators/index.d.mts +2 -1
- package/dist/decorators/index.mjs +2 -1
- package/dist/decorators/middleware.cjs +100 -0
- package/dist/decorators/middleware.d.cts +51 -0
- package/dist/decorators/middleware.d.mts +51 -0
- package/dist/decorators/middleware.mjs +98 -0
- package/dist/decorators/setup.cjs +4 -1
- package/dist/decorators/setup.d.cts +2 -1
- package/dist/decorators/setup.d.mts +2 -2
- package/dist/decorators/setup.mjs +2 -1
- package/dist/express/router.cjs +13 -4
- package/dist/express/router.d.cts +1 -0
- package/dist/express/router.d.mts +1 -0
- package/dist/express/router.mjs +13 -4
- package/dist/fastify/router.cjs +13 -4
- package/dist/fastify/router.d.cts +1 -0
- package/dist/fastify/router.d.mts +1 -0
- package/dist/fastify/router.mjs +13 -4
- package/dist/h3/router.cjs +13 -4
- package/dist/h3/router.d.cts +1 -0
- package/dist/h3/router.d.mts +1 -0
- package/dist/h3/router.mjs +13 -4
- package/dist/hono/router.cjs +11 -4
- package/dist/hono/router.d.cts +1 -0
- package/dist/hono/router.d.mts +1 -0
- package/dist/hono/router.mjs +11 -4
- package/dist/index.cjs +6 -0
- package/dist/index.d.cts +4 -2
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +4 -2
- package/dist/koa/router.cjs +13 -4
- package/dist/koa/router.d.cts +1 -0
- package/dist/koa/router.d.mts +1 -0
- package/dist/koa/router.mjs +13 -4
- package/dist/types/basic.d.cts +2 -0
- package/dist/types/basic.d.mts +2 -0
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { ClearRouterPluginArgumentsContext, ClearRouterPluginInput, ClearRouterP
|
|
|
6
6
|
import { Controller } from "../Controller.mjs";
|
|
7
7
|
import { ResourceRoutes } from "../ResourceRoutes.mjs";
|
|
8
8
|
import { RouteGroup } from "../RouteGroup.mjs";
|
|
9
|
+
import { RouteRegistrar } from "../RouteRegistrar.mjs";
|
|
9
10
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
11
|
|
|
11
12
|
//#region src/core/CoreRouter.d.ts
|
|
@@ -26,6 +27,9 @@ declare abstract class CoreRouter {
|
|
|
26
27
|
private static readonly pluginArgumentResolversKey;
|
|
27
28
|
private static requestProvider?;
|
|
28
29
|
private static responseProvider?;
|
|
30
|
+
private static readonly domainMatcherCache;
|
|
31
|
+
private static readonly constraintRegexCache;
|
|
32
|
+
static routePatterns: Map<string, string | RegExp>;
|
|
29
33
|
static config: RouterConfig;
|
|
30
34
|
protected static groupContext: AsyncLocalStorage<RouteGroupContext>;
|
|
31
35
|
protected static pluginRequestContext: AsyncLocalStorage<ClearRouterPluginRequestContext<any>>;
|
|
@@ -111,6 +115,149 @@ declare abstract class CoreRouter {
|
|
|
111
115
|
}>;
|
|
112
116
|
protected static expandRoutePath(path: string): string[];
|
|
113
117
|
protected static routeRegistrationPaths(path: string): string[];
|
|
118
|
+
/**
|
|
119
|
+
* Compile a host pattern such as `{account}.example.com` into a matcher and
|
|
120
|
+
* the ordered list of placeholder names it captures. Results are memoized.
|
|
121
|
+
*
|
|
122
|
+
* @param pattern
|
|
123
|
+
* @returns
|
|
124
|
+
*/
|
|
125
|
+
protected static compileDomain(pattern: string): {
|
|
126
|
+
regex: RegExp;
|
|
127
|
+
params: string[];
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Match a host against a domain pattern, returning the captured parameters or
|
|
131
|
+
* `null` when the host does not match.
|
|
132
|
+
*
|
|
133
|
+
* @param pattern
|
|
134
|
+
* @param host
|
|
135
|
+
* @returns
|
|
136
|
+
*/
|
|
137
|
+
static matchDomain(pattern: string, host?: string | null): Record<string, string> | null;
|
|
138
|
+
/**
|
|
139
|
+
* Best-effort extraction of the request host across every supported adapter
|
|
140
|
+
* context shape (Express/Fastify plain headers, H3 `Headers`, Hono accessor,
|
|
141
|
+
* Koa context).
|
|
142
|
+
*
|
|
143
|
+
* @param ctx
|
|
144
|
+
* @returns
|
|
145
|
+
*/
|
|
146
|
+
protected static extractHost(ctx: any): string;
|
|
147
|
+
/**
|
|
148
|
+
* Resolve the domain parameters for a route given the active request context.
|
|
149
|
+
* Returns `null` when the route is not domain-constrained, `false` when it is
|
|
150
|
+
* but the host does not match, or the captured parameters on a match.
|
|
151
|
+
*
|
|
152
|
+
* @param route
|
|
153
|
+
* @param ctx
|
|
154
|
+
* @returns
|
|
155
|
+
*/
|
|
156
|
+
protected static matchRouteDomain(route: Route<any, any, any>, ctx: any): Record<string, string> | null | false;
|
|
157
|
+
/**
|
|
158
|
+
* Register a global pattern applied to every route parameter sharing the
|
|
159
|
+
* given name (equivalent to Laravel's `Route::pattern`).
|
|
160
|
+
*
|
|
161
|
+
* @param name
|
|
162
|
+
* @param pattern
|
|
163
|
+
*/
|
|
164
|
+
static pattern(name: string, pattern: string | RegExp): void;
|
|
165
|
+
/**
|
|
166
|
+
* Register multiple global parameter patterns at once.
|
|
167
|
+
*
|
|
168
|
+
* @param patterns
|
|
169
|
+
*/
|
|
170
|
+
static patterns(patterns: Record<string, string | RegExp>): void;
|
|
171
|
+
/**
|
|
172
|
+
* Merge the global parameter patterns with the route's own constraints. Route
|
|
173
|
+
* level constraints take precedence over global patterns.
|
|
174
|
+
*
|
|
175
|
+
* @param route
|
|
176
|
+
* @returns
|
|
177
|
+
*/
|
|
178
|
+
protected static resolveConstraints(route: Route<any, any, any>): Record<string, string | RegExp>;
|
|
179
|
+
/**
|
|
180
|
+
* Compile a constraint pattern into a fully-anchored regular expression.
|
|
181
|
+
*
|
|
182
|
+
* @param pattern
|
|
183
|
+
* @returns
|
|
184
|
+
*/
|
|
185
|
+
protected static toConstraintRegex(pattern: string | RegExp): RegExp;
|
|
186
|
+
/**
|
|
187
|
+
* Determine whether the resolved parameters satisfy the route's constraints.
|
|
188
|
+
* Absent parameters (e.g. optional ones) are ignored.
|
|
189
|
+
*
|
|
190
|
+
* @param route
|
|
191
|
+
* @param params
|
|
192
|
+
* @returns
|
|
193
|
+
*/
|
|
194
|
+
protected static satisfiesConstraints(route: Route<any, any, any>, params: Record<string, any>): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Determine which of a route's parameters are allowed to span multiple path
|
|
197
|
+
* segments (i.e. their constraint matches an encoded forward slash). These are
|
|
198
|
+
* registered with the adapter's catch-all syntax.
|
|
199
|
+
*
|
|
200
|
+
* @param route
|
|
201
|
+
* @returns
|
|
202
|
+
*/
|
|
203
|
+
protected static wildcardParameters(route: Route<any, any, any>): Set<string>;
|
|
204
|
+
/**
|
|
205
|
+
* Render a single wildcard (slash-spanning) parameter for the underlying
|
|
206
|
+
* router's registration path. Overridden per adapter; the base form keeps the
|
|
207
|
+
* plain `:name` placeholder.
|
|
208
|
+
*
|
|
209
|
+
* @param name
|
|
210
|
+
* @returns
|
|
211
|
+
*/
|
|
212
|
+
protected static formatWildcardParam(name: string): string;
|
|
213
|
+
/**
|
|
214
|
+
* Rewrite a route's registration paths so any wildcard parameters use the
|
|
215
|
+
* adapter's catch-all syntax. Non-wildcard routes are returned unchanged.
|
|
216
|
+
*
|
|
217
|
+
* @param route
|
|
218
|
+
* @returns
|
|
219
|
+
*/
|
|
220
|
+
protected static resolveRegistrationPaths(route: Route<any, any, any>): string[];
|
|
221
|
+
/**
|
|
222
|
+
* Resolve the final parameters for a dispatched route, applying domain
|
|
223
|
+
* matching and constraint validation. Returns the merged parameters, or
|
|
224
|
+
* `false` when the route should not handle the request (host mismatch or a
|
|
225
|
+
* constraint failure) so the adapter can fall through.
|
|
226
|
+
*
|
|
227
|
+
* @param route
|
|
228
|
+
* @param ctx
|
|
229
|
+
* @param baseParams
|
|
230
|
+
* @returns
|
|
231
|
+
*/
|
|
232
|
+
protected static matchRoute(route: Route<any, any, any>, ctx: any, baseParams?: Record<string, any>): Record<string, any> | false;
|
|
233
|
+
/**
|
|
234
|
+
* Normalize wildcard (slash-spanning) parameters into a single string keyed by
|
|
235
|
+
* the declared parameter name, smoothing over the differing shapes adapters
|
|
236
|
+
* return (Express yields an array of segments, Fastify keys it under `*`).
|
|
237
|
+
*
|
|
238
|
+
* @param route
|
|
239
|
+
* @param params
|
|
240
|
+
*/
|
|
241
|
+
protected static normalizeWildcardParams(route: Route<any, any, any>, params: Record<string, any>): void;
|
|
242
|
+
/**
|
|
243
|
+
* Get the route currently being dispatched, if any.
|
|
244
|
+
*
|
|
245
|
+
* @returns
|
|
246
|
+
*/
|
|
247
|
+
static current(): Route<any, any, any> | undefined;
|
|
248
|
+
/**
|
|
249
|
+
* Get the name of the route currently being dispatched.
|
|
250
|
+
*
|
|
251
|
+
* @returns
|
|
252
|
+
*/
|
|
253
|
+
static currentRouteName(): string;
|
|
254
|
+
/**
|
|
255
|
+
* Get the action (`Controller@method` or `Closure`) of the route currently
|
|
256
|
+
* being dispatched.
|
|
257
|
+
*
|
|
258
|
+
* @returns
|
|
259
|
+
*/
|
|
260
|
+
static currentRouteAction(): string;
|
|
114
261
|
/**
|
|
115
262
|
* Configures the router with the given options, such as method override settings.
|
|
116
263
|
*
|
|
@@ -215,6 +362,27 @@ declare abstract class CoreRouter {
|
|
|
215
362
|
* @param middlewares
|
|
216
363
|
*/
|
|
217
364
|
static group<S extends RouteGroupSource>(prefix: string, source: S, middlewares?: any[]): RouteGroup<any, any, any, S>;
|
|
365
|
+
/**
|
|
366
|
+
* Build a route group, optionally constrained to a host pattern. Shared by
|
|
367
|
+
* `group` and the `domain` registrar.
|
|
368
|
+
*
|
|
369
|
+
* @param prefix
|
|
370
|
+
* @param source
|
|
371
|
+
* @param middlewares
|
|
372
|
+
* @param extra
|
|
373
|
+
*/
|
|
374
|
+
protected static makeGroup<S extends RouteGroupSource>(prefix: string, source: S, middlewares?: any[], extra?: {
|
|
375
|
+
domain?: string;
|
|
376
|
+
}): RouteGroup<any, any, any, S>;
|
|
377
|
+
/**
|
|
378
|
+
* Begin a route registration constrained to a host pattern such as
|
|
379
|
+
* `{account}.example.com`. Returns a registrar whose `.group()` registers the
|
|
380
|
+
* routes under that domain (matched parameters become route parameters).
|
|
381
|
+
*
|
|
382
|
+
* @param pattern
|
|
383
|
+
* @returns
|
|
384
|
+
*/
|
|
385
|
+
static domain(pattern: string): RouteRegistrar;
|
|
218
386
|
/**
|
|
219
387
|
* Adds global middlewares to the router, which will be applied to all routes.
|
|
220
388
|
*
|
package/dist/core/CoreRouter.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Route } from "../Route.mjs";
|
|
2
2
|
import { RouteGroup } from "../RouteGroup.mjs";
|
|
3
|
+
import { RouteRegistrar } from "../RouteRegistrar.mjs";
|
|
3
4
|
import { Request } from "./Request.mjs";
|
|
4
5
|
import { Response } from "./Response.mjs";
|
|
6
|
+
import { getControllerMiddlewares } from "../decorators/middleware.mjs";
|
|
5
7
|
import { Container, getBindingMetadataFromTargets, getDesignParamTypes, getStandardMetadata, isClass } from "./bindings.mjs";
|
|
6
8
|
import { ResourceRoutes } from "../ResourceRoutes.mjs";
|
|
7
9
|
import { createRequire } from "node:module";
|
|
@@ -25,6 +27,9 @@ var CoreRouter = class {
|
|
|
25
27
|
static pluginArgumentResolversKey = Symbol.for("clear-router:plugin-argument-resolvers");
|
|
26
28
|
static requestProvider;
|
|
27
29
|
static responseProvider;
|
|
30
|
+
static domainMatcherCache = /* @__PURE__ */ new Map();
|
|
31
|
+
static constraintRegexCache = /* @__PURE__ */ new Map();
|
|
32
|
+
static routePatterns = /* @__PURE__ */ new Map();
|
|
28
33
|
static config = {
|
|
29
34
|
inferParamName: false,
|
|
30
35
|
methodOverride: {
|
|
@@ -111,6 +116,7 @@ var CoreRouter = class {
|
|
|
111
116
|
this.routesByPathMethod.clear();
|
|
112
117
|
this.routesByMethod.clear();
|
|
113
118
|
this.routesByName.clear();
|
|
119
|
+
this.routePatterns.clear();
|
|
114
120
|
return this;
|
|
115
121
|
}
|
|
116
122
|
static createBaseConfig() {
|
|
@@ -436,6 +442,271 @@ var CoreRouter = class {
|
|
|
436
442
|
return this.expandRoutePath(path);
|
|
437
443
|
}
|
|
438
444
|
/**
|
|
445
|
+
* Compile a host pattern such as `{account}.example.com` into a matcher and
|
|
446
|
+
* the ordered list of placeholder names it captures. Results are memoized.
|
|
447
|
+
*
|
|
448
|
+
* @param pattern
|
|
449
|
+
* @returns
|
|
450
|
+
*/
|
|
451
|
+
static compileDomain(pattern) {
|
|
452
|
+
const cached = this.domainMatcherCache.get(pattern);
|
|
453
|
+
if (cached) return cached;
|
|
454
|
+
const cleanPattern = pattern.split(":", 1)[0].trim();
|
|
455
|
+
const escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, (match) => `\\${match}`);
|
|
456
|
+
const placeholder = /\{([^{}]+)\}/g;
|
|
457
|
+
const params = [];
|
|
458
|
+
let source = "^";
|
|
459
|
+
let lastIndex = 0;
|
|
460
|
+
let match;
|
|
461
|
+
while ((match = placeholder.exec(cleanPattern)) !== null) {
|
|
462
|
+
source += escape(cleanPattern.slice(lastIndex, match.index));
|
|
463
|
+
const raw = match[1].trim();
|
|
464
|
+
const optional = raw.endsWith("?");
|
|
465
|
+
const name = (optional ? raw.slice(0, -1) : raw).split(":", 1)[0].trim();
|
|
466
|
+
params.push(name);
|
|
467
|
+
source += optional ? "([^.]*)" : "([^.]+)";
|
|
468
|
+
lastIndex = match.index + match[0].length;
|
|
469
|
+
}
|
|
470
|
+
source += escape(cleanPattern.slice(lastIndex));
|
|
471
|
+
source += "$";
|
|
472
|
+
const compiled = {
|
|
473
|
+
regex: new RegExp(source, "i"),
|
|
474
|
+
params
|
|
475
|
+
};
|
|
476
|
+
this.domainMatcherCache.set(pattern, compiled);
|
|
477
|
+
return compiled;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Match a host against a domain pattern, returning the captured parameters or
|
|
481
|
+
* `null` when the host does not match.
|
|
482
|
+
*
|
|
483
|
+
* @param pattern
|
|
484
|
+
* @param host
|
|
485
|
+
* @returns
|
|
486
|
+
*/
|
|
487
|
+
static matchDomain(pattern, host) {
|
|
488
|
+
if (!pattern) return null;
|
|
489
|
+
const cleanHost = String(host ?? "").split(":", 1)[0].trim().toLowerCase();
|
|
490
|
+
const { regex, params } = this.compileDomain(pattern);
|
|
491
|
+
const match = regex.exec(cleanHost);
|
|
492
|
+
if (!match) return null;
|
|
493
|
+
const result = {};
|
|
494
|
+
params.forEach((name, index) => {
|
|
495
|
+
const value = match[index + 1];
|
|
496
|
+
if (typeof value !== "undefined") result[name] = decodeURIComponent(value);
|
|
497
|
+
});
|
|
498
|
+
return result;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Best-effort extraction of the request host across every supported adapter
|
|
502
|
+
* context shape (Express/Fastify plain headers, H3 `Headers`, Hono accessor,
|
|
503
|
+
* Koa context).
|
|
504
|
+
*
|
|
505
|
+
* @param ctx
|
|
506
|
+
* @returns
|
|
507
|
+
*/
|
|
508
|
+
static extractHost(ctx) {
|
|
509
|
+
const headers = ctx?.req?.headers ?? ctx?.headers;
|
|
510
|
+
let host;
|
|
511
|
+
if (headers) host = typeof headers.get === "function" ? headers.get("host") ?? headers.get(":authority") : headers.host ?? headers[":authority"];
|
|
512
|
+
if (!host && typeof ctx?.req?.header === "function") host = ctx.req.header("host");
|
|
513
|
+
if (!host && typeof ctx?.host === "string") host = ctx.host;
|
|
514
|
+
if (Array.isArray(host)) host = host[0];
|
|
515
|
+
return String(host ?? "").split(",", 1)[0].trim();
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Resolve the domain parameters for a route given the active request context.
|
|
519
|
+
* Returns `null` when the route is not domain-constrained, `false` when it is
|
|
520
|
+
* but the host does not match, or the captured parameters on a match.
|
|
521
|
+
*
|
|
522
|
+
* @param route
|
|
523
|
+
* @param ctx
|
|
524
|
+
* @returns
|
|
525
|
+
*/
|
|
526
|
+
static matchRouteDomain(route, ctx) {
|
|
527
|
+
if (!route.domainPattern) return null;
|
|
528
|
+
return this.matchDomain(route.domainPattern, this.extractHost(ctx)) ?? false;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Register a global pattern applied to every route parameter sharing the
|
|
532
|
+
* given name (equivalent to Laravel's `Route::pattern`).
|
|
533
|
+
*
|
|
534
|
+
* @param name
|
|
535
|
+
* @param pattern
|
|
536
|
+
*/
|
|
537
|
+
static pattern(name, pattern) {
|
|
538
|
+
this.ensureState();
|
|
539
|
+
this.routePatterns.set(name, pattern);
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Register multiple global parameter patterns at once.
|
|
543
|
+
*
|
|
544
|
+
* @param patterns
|
|
545
|
+
*/
|
|
546
|
+
static patterns(patterns) {
|
|
547
|
+
for (const [name, pattern] of Object.entries(patterns)) this.pattern(name, pattern);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Merge the global parameter patterns with the route's own constraints. Route
|
|
551
|
+
* level constraints take precedence over global patterns.
|
|
552
|
+
*
|
|
553
|
+
* @param route
|
|
554
|
+
* @returns
|
|
555
|
+
*/
|
|
556
|
+
static resolveConstraints(route) {
|
|
557
|
+
if (!this.routePatterns.size && !Object.keys(route.constraints).length) return route.constraints;
|
|
558
|
+
return {
|
|
559
|
+
...Object.fromEntries(this.routePatterns),
|
|
560
|
+
...route.constraints
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Compile a constraint pattern into a fully-anchored regular expression.
|
|
565
|
+
*
|
|
566
|
+
* @param pattern
|
|
567
|
+
* @returns
|
|
568
|
+
*/
|
|
569
|
+
static toConstraintRegex(pattern) {
|
|
570
|
+
if (pattern instanceof RegExp) return new RegExp(`^(?:${pattern.source})$`, pattern.flags.replace("g", ""));
|
|
571
|
+
const cached = this.constraintRegexCache.get(pattern);
|
|
572
|
+
if (cached) return cached;
|
|
573
|
+
const regex = new RegExp(`^(?:${pattern})$`);
|
|
574
|
+
this.constraintRegexCache.set(pattern, regex);
|
|
575
|
+
return regex;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Determine whether the resolved parameters satisfy the route's constraints.
|
|
579
|
+
* Absent parameters (e.g. optional ones) are ignored.
|
|
580
|
+
*
|
|
581
|
+
* @param route
|
|
582
|
+
* @param params
|
|
583
|
+
* @returns
|
|
584
|
+
*/
|
|
585
|
+
static satisfiesConstraints(route, params) {
|
|
586
|
+
const constraints = this.resolveConstraints(route);
|
|
587
|
+
for (const name of Object.keys(constraints)) {
|
|
588
|
+
const value = params[name];
|
|
589
|
+
if (typeof value === "undefined" || value === null) continue;
|
|
590
|
+
const values = Array.isArray(value) ? value : [value];
|
|
591
|
+
const regex = this.toConstraintRegex(constraints[name]);
|
|
592
|
+
if (!values.every((entry) => regex.test(String(entry)))) return false;
|
|
593
|
+
}
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Determine which of a route's parameters are allowed to span multiple path
|
|
598
|
+
* segments (i.e. their constraint matches an encoded forward slash). These are
|
|
599
|
+
* registered with the adapter's catch-all syntax.
|
|
600
|
+
*
|
|
601
|
+
* @param route
|
|
602
|
+
* @returns
|
|
603
|
+
*/
|
|
604
|
+
static wildcardParameters(route) {
|
|
605
|
+
const wildcards = /* @__PURE__ */ new Set();
|
|
606
|
+
const constraints = this.resolveConstraints(route);
|
|
607
|
+
const declared = new Set(route.parameters.map((parameter) => parameter.name));
|
|
608
|
+
for (const name of Object.keys(constraints)) {
|
|
609
|
+
if (!declared.has(name)) continue;
|
|
610
|
+
if (this.toConstraintRegex(constraints[name]).test("a/b")) wildcards.add(name);
|
|
611
|
+
}
|
|
612
|
+
return wildcards;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Render a single wildcard (slash-spanning) parameter for the underlying
|
|
616
|
+
* router's registration path. Overridden per adapter; the base form keeps the
|
|
617
|
+
* plain `:name` placeholder.
|
|
618
|
+
*
|
|
619
|
+
* @param name
|
|
620
|
+
* @returns
|
|
621
|
+
*/
|
|
622
|
+
static formatWildcardParam(name) {
|
|
623
|
+
return `:${name}`;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Rewrite a route's registration paths so any wildcard parameters use the
|
|
627
|
+
* adapter's catch-all syntax. Non-wildcard routes are returned unchanged.
|
|
628
|
+
*
|
|
629
|
+
* @param route
|
|
630
|
+
* @returns
|
|
631
|
+
*/
|
|
632
|
+
static resolveRegistrationPaths(route) {
|
|
633
|
+
const wildcards = this.wildcardParameters(route);
|
|
634
|
+
if (!wildcards.size) return route.registrationPaths;
|
|
635
|
+
return route.registrationPaths.map((path) => path.split("/").map((segment) => {
|
|
636
|
+
const name = segment.startsWith(":") ? segment.slice(1) : "";
|
|
637
|
+
return name && wildcards.has(name) ? this.formatWildcardParam(name) : segment;
|
|
638
|
+
}).join("/"));
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Resolve the final parameters for a dispatched route, applying domain
|
|
642
|
+
* matching and constraint validation. Returns the merged parameters, or
|
|
643
|
+
* `false` when the route should not handle the request (host mismatch or a
|
|
644
|
+
* constraint failure) so the adapter can fall through.
|
|
645
|
+
*
|
|
646
|
+
* @param route
|
|
647
|
+
* @param ctx
|
|
648
|
+
* @param baseParams
|
|
649
|
+
* @returns
|
|
650
|
+
*/
|
|
651
|
+
static matchRoute(route, ctx, baseParams = {}) {
|
|
652
|
+
const params = { ...baseParams ?? {} };
|
|
653
|
+
if (route.domainPattern) {
|
|
654
|
+
const domainParams = this.matchDomain(route.domainPattern, this.extractHost(ctx));
|
|
655
|
+
if (!domainParams) return false;
|
|
656
|
+
Object.assign(params, domainParams);
|
|
657
|
+
}
|
|
658
|
+
this.normalizeWildcardParams(route, params);
|
|
659
|
+
if (!this.satisfiesConstraints(route, params)) return false;
|
|
660
|
+
return params;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Normalize wildcard (slash-spanning) parameters into a single string keyed by
|
|
664
|
+
* the declared parameter name, smoothing over the differing shapes adapters
|
|
665
|
+
* return (Express yields an array of segments, Fastify keys it under `*`).
|
|
666
|
+
*
|
|
667
|
+
* @param route
|
|
668
|
+
* @param params
|
|
669
|
+
*/
|
|
670
|
+
static normalizeWildcardParams(route, params) {
|
|
671
|
+
const wildcards = this.wildcardParameters(route);
|
|
672
|
+
if (!wildcards.size) return;
|
|
673
|
+
for (const name of wildcards) {
|
|
674
|
+
let value = params[name];
|
|
675
|
+
if (typeof value === "undefined" && typeof params["*"] !== "undefined") {
|
|
676
|
+
value = params["*"];
|
|
677
|
+
delete params["*"];
|
|
678
|
+
}
|
|
679
|
+
if (Array.isArray(value)) value = value.join("/");
|
|
680
|
+
if (typeof value !== "undefined") params[name] = value;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Get the route currently being dispatched, if any.
|
|
685
|
+
*
|
|
686
|
+
* @returns
|
|
687
|
+
*/
|
|
688
|
+
static current() {
|
|
689
|
+
const store = this.pluginRequestContext.getStore();
|
|
690
|
+
return store?.request?.route ?? store?.ctx?.clearRequest?.route;
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Get the name of the route currently being dispatched.
|
|
694
|
+
*
|
|
695
|
+
* @returns
|
|
696
|
+
*/
|
|
697
|
+
static currentRouteName() {
|
|
698
|
+
return this.current()?.routeName ?? "";
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Get the action (`Controller@method` or `Closure`) of the route currently
|
|
702
|
+
* being dispatched.
|
|
703
|
+
*
|
|
704
|
+
* @returns
|
|
705
|
+
*/
|
|
706
|
+
static currentRouteAction() {
|
|
707
|
+
return this.current()?.action ?? "";
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
439
710
|
* Configures the router with the given options, such as method override settings.
|
|
440
711
|
*
|
|
441
712
|
* @param this
|
|
@@ -507,6 +778,7 @@ var CoreRouter = class {
|
|
|
507
778
|
const context = this.groupContext.getStore();
|
|
508
779
|
const activePrefix = context?.prefix ?? this.prefix;
|
|
509
780
|
const activeGroupMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
|
|
781
|
+
const activeDomain = context?.domain;
|
|
510
782
|
methods = Array.isArray(methods) ? methods : [methods];
|
|
511
783
|
middlewares = middlewares ? Array.isArray(middlewares) ? middlewares : [middlewares] : void 0;
|
|
512
784
|
const fullPath = this.normalizePath(`${activePrefix}/${path}`);
|
|
@@ -519,10 +791,12 @@ var CoreRouter = class {
|
|
|
519
791
|
const route = new Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, this.resolveMiddlewares([
|
|
520
792
|
...this.globalMiddlewares,
|
|
521
793
|
...activeGroupMiddlewares,
|
|
794
|
+
...getControllerMiddlewares(handler),
|
|
522
795
|
...middlewares || []
|
|
523
796
|
]), {
|
|
524
797
|
registrationPaths,
|
|
525
798
|
parameters,
|
|
799
|
+
domain: activeDomain,
|
|
526
800
|
onName: (name, route, previousName) => {
|
|
527
801
|
if (previousName && this.routesByName.get(previousName) === route) this.routesByName.delete(previousName);
|
|
528
802
|
this.routesByName.set(name, route);
|
|
@@ -643,11 +917,24 @@ var CoreRouter = class {
|
|
|
643
917
|
* @param middlewares
|
|
644
918
|
*/
|
|
645
919
|
static group(prefix, source, middlewares) {
|
|
920
|
+
return this.makeGroup(prefix, source, middlewares);
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Build a route group, optionally constrained to a host pattern. Shared by
|
|
924
|
+
* `group` and the `domain` registrar.
|
|
925
|
+
*
|
|
926
|
+
* @param prefix
|
|
927
|
+
* @param source
|
|
928
|
+
* @param middlewares
|
|
929
|
+
* @param extra
|
|
930
|
+
*/
|
|
931
|
+
static makeGroup(prefix, source, middlewares, extra) {
|
|
646
932
|
this.ensureState();
|
|
647
933
|
return new RouteGroup({
|
|
648
934
|
prefix,
|
|
649
935
|
source,
|
|
650
936
|
middlewares,
|
|
937
|
+
domain: extra?.domain,
|
|
651
938
|
context: this.groupContext,
|
|
652
939
|
defaultPrefix: this.prefix,
|
|
653
940
|
defaultMiddlewares: this.groupMiddlewares,
|
|
@@ -656,6 +943,18 @@ var CoreRouter = class {
|
|
|
656
943
|
});
|
|
657
944
|
}
|
|
658
945
|
/**
|
|
946
|
+
* Begin a route registration constrained to a host pattern such as
|
|
947
|
+
* `{account}.example.com`. Returns a registrar whose `.group()` registers the
|
|
948
|
+
* routes under that domain (matched parameters become route parameters).
|
|
949
|
+
*
|
|
950
|
+
* @param pattern
|
|
951
|
+
* @returns
|
|
952
|
+
*/
|
|
953
|
+
static domain(pattern) {
|
|
954
|
+
this.ensureState();
|
|
955
|
+
return new RouteRegistrar((prefix, source, middlewares, extra) => this.makeGroup(prefix, source, middlewares, extra), { domain: pattern });
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
659
958
|
* Adds global middlewares to the router, which will be applied to all routes.
|
|
660
959
|
*
|
|
661
960
|
* @param this
|
|
@@ -831,6 +1130,16 @@ var CoreRouter = class {
|
|
|
831
1130
|
instance.clearRequest = clearRequest;
|
|
832
1131
|
}
|
|
833
1132
|
};
|
|
1133
|
+
/**
|
|
1134
|
+
* Expose the active request's route through the `Route` facade (`Route.current()`,
|
|
1135
|
+
* `Route.currentRouteName()`, `Route.currentRouteAction()`) by delegating to the
|
|
1136
|
+
* shared router request context.
|
|
1137
|
+
*/
|
|
1138
|
+
Route.bindCurrentResolvers({
|
|
1139
|
+
current: () => CoreRouter.current(),
|
|
1140
|
+
currentRouteName: () => CoreRouter.currentRouteName(),
|
|
1141
|
+
currentRouteAction: () => CoreRouter.currentRouteAction()
|
|
1142
|
+
});
|
|
834
1143
|
|
|
835
1144
|
//#endregion
|
|
836
1145
|
export { CoreRouter };
|
package/dist/core/index.cjs
CHANGED
|
@@ -4,6 +4,7 @@ const require_RouteGroup = require('../RouteGroup.cjs');
|
|
|
4
4
|
const require_Request = require('./Request.cjs');
|
|
5
5
|
const require_Response = require('./Response.cjs');
|
|
6
6
|
const require_plugins = require('./plugins.cjs');
|
|
7
|
+
const require_middleware = require('../decorators/middleware.cjs');
|
|
7
8
|
const require_CoreRouter = require('./CoreRouter.cjs');
|
|
8
9
|
|
|
9
10
|
exports.CoreRouter = require_CoreRouter.CoreRouter;
|
|
@@ -11,5 +12,7 @@ exports.Request = require_Request.Request;
|
|
|
11
12
|
exports.Response = require_Response.Response;
|
|
12
13
|
exports.RouteGroup = require_RouteGroup.RouteGroup;
|
|
13
14
|
exports.definePlugin = require_plugins.definePlugin;
|
|
15
|
+
exports.getControllerMiddlewares = require_middleware.getControllerMiddlewares;
|
|
14
16
|
exports.importFile = require_helpers.importFile;
|
|
17
|
+
exports.middleware = require_middleware.middleware;
|
|
15
18
|
exports.wrap = require_helpers.wrap;
|
package/dist/core/index.d.cts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Response } from "./Response.cjs";
|
|
2
2
|
import { Request } from "./Request.cjs";
|
|
3
3
|
import { ClearRouterPlugin, ClearRouterPluginArgumentsContext, ClearRouterPluginContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, PluginArgumentsResolver, PluginBind, PluginBindFactory, PluginBindValue, PluginSetupResult, definePlugin } from "./plugins.cjs";
|
|
4
|
+
import { MiddlewareDecorator, MiddlewareInput, getControllerMiddlewares, middleware } from "../decorators/middleware.cjs";
|
|
4
5
|
import { RouteGroup } from "../RouteGroup.cjs";
|
|
5
6
|
import { CoreRouter } from "./CoreRouter.cjs";
|
|
6
7
|
import { importFile, wrap } from "./helpers.cjs";
|
|
7
|
-
export { ClearRouterPlugin, ClearRouterPluginArgumentsContext, ClearRouterPluginContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, CoreRouter, PluginArgumentsResolver, PluginBind, PluginBindFactory, PluginBindValue, PluginSetupResult, Request, Response, RouteGroup, definePlugin, importFile, wrap };
|
|
8
|
+
export { ClearRouterPlugin, ClearRouterPluginArgumentsContext, ClearRouterPluginContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, CoreRouter, MiddlewareDecorator, MiddlewareInput, PluginArgumentsResolver, PluginBind, PluginBindFactory, PluginBindValue, PluginSetupResult, Request, Response, RouteGroup, definePlugin, getControllerMiddlewares, importFile, middleware, wrap };
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Response } from "./Response.mjs";
|
|
2
2
|
import { Request } from "./Request.mjs";
|
|
3
3
|
import { ClearRouterPlugin, ClearRouterPluginArgumentsContext, ClearRouterPluginContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, PluginArgumentsResolver, PluginBind, PluginBindFactory, PluginBindValue, PluginSetupResult, definePlugin } from "./plugins.mjs";
|
|
4
|
+
import { MiddlewareDecorator, MiddlewareInput, getControllerMiddlewares, middleware } from "../decorators/middleware.mjs";
|
|
4
5
|
import { RouteGroup } from "../RouteGroup.mjs";
|
|
5
6
|
import { CoreRouter } from "./CoreRouter.mjs";
|
|
6
7
|
import { importFile, wrap } from "./helpers.mjs";
|
|
7
|
-
export { ClearRouterPlugin, ClearRouterPluginArgumentsContext, ClearRouterPluginContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, CoreRouter, PluginArgumentsResolver, PluginBind, PluginBindFactory, PluginBindValue, PluginSetupResult, Request, Response, RouteGroup, definePlugin, importFile, wrap };
|
|
8
|
+
export { ClearRouterPlugin, ClearRouterPluginArgumentsContext, ClearRouterPluginContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, CoreRouter, MiddlewareDecorator, MiddlewareInput, PluginArgumentsResolver, PluginBind, PluginBindFactory, PluginBindValue, PluginSetupResult, Request, Response, RouteGroup, definePlugin, getControllerMiddlewares, importFile, middleware, wrap };
|
package/dist/core/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { RouteGroup } from "../RouteGroup.mjs";
|
|
|
3
3
|
import { Request } from "./Request.mjs";
|
|
4
4
|
import { Response } from "./Response.mjs";
|
|
5
5
|
import { definePlugin } from "./plugins.mjs";
|
|
6
|
+
import { getControllerMiddlewares, middleware } from "../decorators/middleware.mjs";
|
|
6
7
|
import { CoreRouter } from "./CoreRouter.mjs";
|
|
7
8
|
|
|
8
|
-
export { CoreRouter, Request, Response, RouteGroup, definePlugin, importFile, wrap };
|
|
9
|
+
export { CoreRouter, Request, Response, RouteGroup, definePlugin, getControllerMiddlewares, importFile, middleware, wrap };
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_middleware = require('./middleware.cjs');
|
|
2
3
|
const require_bindings = require('../core/bindings.cjs');
|
|
3
4
|
|
|
4
5
|
exports.Bind = require_bindings.Bind;
|
|
5
|
-
exports.Container = require_bindings.Container;
|
|
6
|
+
exports.Container = require_bindings.Container;
|
|
7
|
+
exports.getControllerMiddlewares = require_middleware.getControllerMiddlewares;
|
|
8
|
+
exports.middleware = require_middleware.middleware;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { Bind, BindDecorator, BindFactory, BindToken, BindValue, Container } from "../core/bindings.cjs";
|
|
2
|
-
|
|
2
|
+
import { MiddlewareDecorator, MiddlewareInput, getControllerMiddlewares, middleware } from "./middleware.cjs";
|
|
3
|
+
export { Bind, type BindDecorator, type BindFactory, type BindToken, type BindValue, Container, type MiddlewareDecorator, type MiddlewareInput, getControllerMiddlewares, middleware };
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { Bind, BindDecorator, BindFactory, BindToken, BindValue, Container } from "../core/bindings.mjs";
|
|
2
|
-
|
|
2
|
+
import { MiddlewareDecorator, MiddlewareInput, getControllerMiddlewares, middleware } from "./middleware.mjs";
|
|
3
|
+
export { Bind, type BindDecorator, type BindFactory, type BindToken, type BindValue, Container, type MiddlewareDecorator, type MiddlewareInput, getControllerMiddlewares, middleware };
|