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.
Files changed (58) hide show
  1. package/README.md +15 -1
  2. package/dist/Route.cjs +200 -12
  3. package/dist/Route.d.cts +125 -1
  4. package/dist/Route.d.mts +125 -1
  5. package/dist/Route.mjs +199 -12
  6. package/dist/RouteGroup.cjs +1 -0
  7. package/dist/RouteGroup.mjs +1 -0
  8. package/dist/RouteRegistrar.cjs +68 -0
  9. package/dist/RouteRegistrar.d.cts +62 -0
  10. package/dist/RouteRegistrar.d.mts +62 -0
  11. package/dist/RouteRegistrar.mjs +67 -0
  12. package/dist/core/CoreRouter.cjs +309 -0
  13. package/dist/core/CoreRouter.d.cts +168 -0
  14. package/dist/core/CoreRouter.d.mts +168 -0
  15. package/dist/core/CoreRouter.mjs +309 -0
  16. package/dist/core/index.cjs +3 -0
  17. package/dist/core/index.d.cts +2 -1
  18. package/dist/core/index.d.mts +2 -1
  19. package/dist/core/index.mjs +2 -1
  20. package/dist/decorators/index.cjs +4 -1
  21. package/dist/decorators/index.d.cts +2 -1
  22. package/dist/decorators/index.d.mts +2 -1
  23. package/dist/decorators/index.mjs +2 -1
  24. package/dist/decorators/middleware.cjs +100 -0
  25. package/dist/decorators/middleware.d.cts +51 -0
  26. package/dist/decorators/middleware.d.mts +51 -0
  27. package/dist/decorators/middleware.mjs +98 -0
  28. package/dist/decorators/setup.cjs +4 -1
  29. package/dist/decorators/setup.d.cts +2 -1
  30. package/dist/decorators/setup.d.mts +2 -2
  31. package/dist/decorators/setup.mjs +2 -1
  32. package/dist/express/router.cjs +13 -4
  33. package/dist/express/router.d.cts +1 -0
  34. package/dist/express/router.d.mts +1 -0
  35. package/dist/express/router.mjs +13 -4
  36. package/dist/fastify/router.cjs +13 -4
  37. package/dist/fastify/router.d.cts +1 -0
  38. package/dist/fastify/router.d.mts +1 -0
  39. package/dist/fastify/router.mjs +13 -4
  40. package/dist/h3/router.cjs +13 -4
  41. package/dist/h3/router.d.cts +1 -0
  42. package/dist/h3/router.d.mts +1 -0
  43. package/dist/h3/router.mjs +13 -4
  44. package/dist/hono/router.cjs +11 -4
  45. package/dist/hono/router.d.cts +1 -0
  46. package/dist/hono/router.d.mts +1 -0
  47. package/dist/hono/router.mjs +11 -4
  48. package/dist/index.cjs +6 -0
  49. package/dist/index.d.cts +4 -2
  50. package/dist/index.d.mts +4 -3
  51. package/dist/index.mjs +4 -2
  52. package/dist/koa/router.cjs +13 -4
  53. package/dist/koa/router.d.cts +1 -0
  54. package/dist/koa/router.d.mts +1 -0
  55. package/dist/koa/router.mjs +13 -4
  56. package/dist/types/basic.d.cts +2 -0
  57. package/dist/types/basic.d.mts +2 -0
  58. 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
  *
@@ -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 };
@@ -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;
@@ -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 };
@@ -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 };
@@ -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
- export { Bind, type BindDecorator, type BindFactory, type BindToken, type BindValue, Container };
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
- export { Bind, type BindDecorator, type BindFactory, type BindToken, type BindValue, Container };
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 };
@@ -1,3 +1,4 @@
1
+ import { getControllerMiddlewares, middleware } from "./middleware.mjs";
1
2
  import { Bind, Container } from "../core/bindings.mjs";
2
3
 
3
- export { Bind, Container };
4
+ export { Bind, Container, getControllerMiddlewares, middleware };