clear-router 2.7.7 → 2.8.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 (63) hide show
  1. package/dist/{Request-BhTJDR_5.d.mts → Request-Ci0UQ-Vl.d.mts} +27 -0
  2. package/dist/ResourceRouteSelection.cjs +26 -0
  3. package/dist/ResourceRouteSelection.d.cts +20 -0
  4. package/dist/ResourceRouteSelection.d.mts +20 -0
  5. package/dist/ResourceRouteSelection.mjs +25 -0
  6. package/dist/ResourceRoutes.cjs +150 -0
  7. package/dist/ResourceRoutes.d.cts +96 -0
  8. package/dist/ResourceRoutes.d.mts +96 -0
  9. package/dist/ResourceRoutes.mjs +150 -0
  10. package/dist/Route.cjs +32 -0
  11. package/dist/Route.d.cts +27 -0
  12. package/dist/Route.d.mts +27 -0
  13. package/dist/Route.mjs +32 -0
  14. package/dist/core/helpers.cjs +9 -0
  15. package/dist/core/helpers.d.cts +4 -0
  16. package/dist/core/helpers.d.mts +4 -0
  17. package/dist/core/helpers.mjs +8 -0
  18. package/dist/core/index.cjs +8 -0
  19. package/dist/core/index.d.cts +2 -1
  20. package/dist/core/index.d.mts +2 -1
  21. package/dist/core/index.mjs +2 -1
  22. package/dist/core/router.cjs +66 -37
  23. package/dist/core/router.d.cts +17 -4
  24. package/dist/core/router.d.mts +17 -4
  25. package/dist/core/router.mjs +66 -37
  26. package/dist/decorators/setup.d.mts +0 -1
  27. package/dist/express/router.cjs +2 -2
  28. package/dist/express/router.d.cts +5 -4
  29. package/dist/express/router.d.mts +5 -4
  30. package/dist/express/router.mjs +2 -2
  31. package/dist/fastify/router.cjs +2 -2
  32. package/dist/fastify/router.d.cts +5 -4
  33. package/dist/fastify/router.d.mts +5 -4
  34. package/dist/fastify/router.mjs +2 -2
  35. package/dist/h3/router.cjs +2 -2
  36. package/dist/h3/router.d.cts +22 -21
  37. package/dist/h3/router.d.mts +22 -21
  38. package/dist/h3/router.mjs +2 -2
  39. package/dist/hono/router.cjs +2 -2
  40. package/dist/hono/router.d.cts +5 -4
  41. package/dist/hono/router.d.mts +5 -4
  42. package/dist/hono/router.mjs +2 -2
  43. package/dist/index.cjs +3 -1
  44. package/dist/index.d.cts +2 -1
  45. package/dist/index.d.mts +2 -1
  46. package/dist/index.mjs +2 -1
  47. package/dist/koa/router.cjs +2 -2
  48. package/dist/koa/router.d.cts +5 -4
  49. package/dist/koa/router.d.mts +5 -4
  50. package/dist/koa/router.mjs +2 -2
  51. package/dist/types/basic.d.cts +13 -4
  52. package/dist/types/basic.d.mts +13 -4
  53. package/dist/types/express.d.cts +6 -2
  54. package/dist/types/express.d.mts +6 -2
  55. package/dist/types/fastify.d.cts +3 -2
  56. package/dist/types/fastify.d.mts +3 -2
  57. package/dist/types/h3.d.cts +7 -2
  58. package/dist/types/h3.d.mts +7 -2
  59. package/dist/types/hono.d.cts +4 -3
  60. package/dist/types/hono.d.mts +4 -3
  61. package/dist/types/koa.d.cts +3 -2
  62. package/dist/types/koa.d.mts +3 -2
  63. package/package.json +1 -1
package/dist/Route.d.cts CHANGED
@@ -5,6 +5,12 @@ import { Middleware as Middleware$1 } from "./types/h3.cjs";
5
5
  import { ClearRequest } from "./ClearRequest.cjs";
6
6
 
7
7
  //#region src/Route.d.ts
8
+ /**
9
+ * @class clear-router Route
10
+ * @description A route describes a single enpoint on clear-router
11
+ * @author 3m1n3nc3
12
+ * @repository https://github.com/arkstack-tmp/clear-router
13
+ */
8
14
  declare class Route<X = any, M = Middleware$1 | Middleware, H = any> {
9
15
  ctx: X;
10
16
  body: RequestData;
@@ -26,9 +32,30 @@ declare class Route<X = any, M = Middleware$1 | Middleware, H = any> {
26
32
  registrationPaths?: string[];
27
33
  parameters?: RouteParameter[];
28
34
  onName?: (name: string, route: Route<X, M, H>, previousName?: string) => void;
35
+ normalizeMiddleware?: (middleware: M) => M;
29
36
  });
30
37
  private onName?;
38
+ private normalizeMiddleware?;
39
+ /**
40
+ * Set the route name
41
+ *
42
+ * @param name
43
+ * @returns
44
+ */
31
45
  name(name: string): this;
46
+ /**
47
+ * Register one or more middleware that will be executed before the route.
48
+ *
49
+ * @param middlewares
50
+ * @returns
51
+ */
52
+ middleware(middlewares: M[] | M): this;
53
+ /**
54
+ * Get the path generated and accessible by this route
55
+ *
56
+ * @param params
57
+ * @returns
58
+ */
32
59
  toPath(params?: RequestData): string;
33
60
  }
34
61
  //#endregion
package/dist/Route.d.mts CHANGED
@@ -5,6 +5,12 @@ import { Middleware as Middleware$1 } from "./types/h3.mjs";
5
5
  import { ClearRequest } from "./ClearRequest.mjs";
6
6
 
7
7
  //#region src/Route.d.ts
8
+ /**
9
+ * @class clear-router Route
10
+ * @description A route describes a single enpoint on clear-router
11
+ * @author 3m1n3nc3
12
+ * @repository https://github.com/arkstack-tmp/clear-router
13
+ */
8
14
  declare class Route<X = any, M = Middleware$1 | Middleware, H = any> {
9
15
  ctx: X;
10
16
  body: RequestData;
@@ -26,9 +32,30 @@ declare class Route<X = any, M = Middleware$1 | Middleware, H = any> {
26
32
  registrationPaths?: string[];
27
33
  parameters?: RouteParameter[];
28
34
  onName?: (name: string, route: Route<X, M, H>, previousName?: string) => void;
35
+ normalizeMiddleware?: (middleware: M) => M;
29
36
  });
30
37
  private onName?;
38
+ private normalizeMiddleware?;
39
+ /**
40
+ * Set the route name
41
+ *
42
+ * @param name
43
+ * @returns
44
+ */
31
45
  name(name: string): this;
46
+ /**
47
+ * Register one or more middleware that will be executed before the route.
48
+ *
49
+ * @param middlewares
50
+ * @returns
51
+ */
52
+ middleware(middlewares: M[] | M): this;
53
+ /**
54
+ * Get the path generated and accessible by this route
55
+ *
56
+ * @param params
57
+ * @returns
58
+ */
32
59
  toPath(params?: RequestData): string;
33
60
  }
34
61
  //#endregion
package/dist/Route.mjs CHANGED
@@ -1,4 +1,10 @@
1
1
  //#region src/Route.ts
2
+ /**
3
+ * @class clear-router Route
4
+ * @description A route describes a single enpoint on clear-router
5
+ * @author 3m1n3nc3
6
+ * @repository https://github.com/arkstack-tmp/clear-router
7
+ */
2
8
  var Route = class {
3
9
  ctx;
4
10
  body = {};
@@ -28,14 +34,40 @@ var Route = class {
28
34
  this.controllerName = Array.isArray(handler) ? handler[0]?.name : void 0;
29
35
  this.actionName = Array.isArray(handler) ? handler[1] : typeof handler === "function" ? handler.constructor.name ?? handler.name : void 0;
30
36
  this.onName = options.onName;
37
+ this.normalizeMiddleware = options.normalizeMiddleware;
31
38
  }
32
39
  onName;
40
+ normalizeMiddleware;
41
+ /**
42
+ * Set the route name
43
+ *
44
+ * @param name
45
+ * @returns
46
+ */
33
47
  name(name) {
34
48
  const previousName = this.routeName;
35
49
  this.routeName = name;
36
50
  this.onName?.(name, this, previousName);
37
51
  return this;
38
52
  }
53
+ /**
54
+ * Register one or more middleware that will be executed before the route.
55
+ *
56
+ * @param middlewares
57
+ * @returns
58
+ */
59
+ middleware(middlewares) {
60
+ const normalized = (Array.isArray(middlewares) ? middlewares : [middlewares]).map((middleware) => this.normalizeMiddleware?.(middleware) ?? middleware);
61
+ this.middlewares.push(...normalized);
62
+ this.middlewareCount = this.middlewares.length;
63
+ return this;
64
+ }
65
+ /**
66
+ * Get the path generated and accessible by this route
67
+ *
68
+ * @param params
69
+ * @returns
70
+ */
39
71
  toPath(params = {}) {
40
72
  return this.path.replace(/\/?\{([^{}]+)\}/g, (segment, raw) => {
41
73
  const optional = raw.endsWith("?");
@@ -0,0 +1,9 @@
1
+
2
+ //#region src/core/helpers.ts
3
+ const wrap = (value) => {
4
+ if (value === null || value === void 0) return [];
5
+ return Array.isArray(value) ? value : [value];
6
+ };
7
+
8
+ //#endregion
9
+ exports.wrap = wrap;
@@ -0,0 +1,4 @@
1
+ //#region src/core/helpers.d.ts
2
+ declare const wrap: <T>(value: T | T[] | null | undefined) => T[];
3
+ //#endregion
4
+ export { wrap };
@@ -0,0 +1,4 @@
1
+ //#region src/core/helpers.d.ts
2
+ declare const wrap: <T>(value: T | T[] | null | undefined) => T[];
3
+ //#endregion
4
+ export { wrap };
@@ -0,0 +1,8 @@
1
+ //#region src/core/helpers.ts
2
+ const wrap = (value) => {
3
+ if (value === null || value === void 0) return [];
4
+ return Array.isArray(value) ? value : [value];
5
+ };
6
+
7
+ //#endregion
8
+ export { wrap };
@@ -1,4 +1,12 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
1
2
  const require_Request = require('./Request.cjs');
2
3
  const require_Response = require('./Response.cjs');
3
4
  const require_plugins = require('./plugins.cjs');
4
5
  const require_router = require('./router.cjs');
6
+ const require_helpers = require('./helpers.cjs');
7
+
8
+ exports.CoreRouter = require_router.CoreRouter;
9
+ exports.Request = require_Request.Request;
10
+ exports.Response = require_Response.Response;
11
+ exports.definePlugin = require_plugins.definePlugin;
12
+ exports.wrap = require_helpers.wrap;
@@ -2,4 +2,5 @@ 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
4
  import { CoreRouter } from "./router.cjs";
5
- export { ClearRouterPlugin, ClearRouterPluginArgumentsContext, ClearRouterPluginContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, CoreRouter, PluginArgumentsResolver, PluginBind, PluginBindFactory, PluginBindValue, PluginSetupResult, Request, Response, definePlugin };
5
+ import { wrap } from "./helpers.cjs";
6
+ export { ClearRouterPlugin, ClearRouterPluginArgumentsContext, ClearRouterPluginContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, CoreRouter, PluginArgumentsResolver, PluginBind, PluginBindFactory, PluginBindValue, PluginSetupResult, Request, Response, definePlugin, wrap };
@@ -2,4 +2,5 @@ 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
4
  import { CoreRouter } from "./router.mjs";
5
- export { ClearRouterPlugin, ClearRouterPluginArgumentsContext, ClearRouterPluginContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, CoreRouter, PluginArgumentsResolver, PluginBind, PluginBindFactory, PluginBindValue, PluginSetupResult, Request, Response, definePlugin };
5
+ import { wrap } from "./helpers.mjs";
6
+ export { ClearRouterPlugin, ClearRouterPluginArgumentsContext, ClearRouterPluginContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, CoreRouter, PluginArgumentsResolver, PluginBind, PluginBindFactory, PluginBindValue, PluginSetupResult, Request, Response, definePlugin, wrap };
@@ -2,5 +2,6 @@ import { Request } from "./Request.mjs";
2
2
  import { Response } from "./Response.mjs";
3
3
  import { definePlugin } from "./plugins.mjs";
4
4
  import { CoreRouter } from "./router.mjs";
5
+ import { wrap } from "./helpers.mjs";
5
6
 
6
- export { CoreRouter, Request, Response, definePlugin };
7
+ export { CoreRouter, Request, Response, definePlugin, wrap };
@@ -1,6 +1,7 @@
1
1
  const require_Request = require('./Request.cjs');
2
2
  const require_Response = require('./Response.cjs');
3
3
  const require_bindings = require('./bindings.cjs');
4
+ const require_ResourceRoutes = require('../ResourceRoutes.cjs');
4
5
  const require_Route = require('../Route.cjs');
5
6
  let node_async_hooks = require("node:async_hooks");
6
7
  let node_module = require("node:module");
@@ -45,6 +46,60 @@ var CoreRouter = class {
45
46
  static groupMiddlewares = [];
46
47
  static globalMiddlewares = [];
47
48
  /**
49
+ * Resolve middlewares before assigning to adapter
50
+ *
51
+ * @param middleware
52
+ * @returns
53
+ */
54
+ static resolveMiddleware(middleware) {
55
+ if (!middleware || typeof middleware === "function" && !require_bindings.isClass(middleware)) return middleware;
56
+ const instance = require_bindings.isClass(middleware) ? new middleware() : middleware;
57
+ if (instance && typeof instance.handle === "function") return instance.handle.bind(instance);
58
+ return middleware;
59
+ }
60
+ static resolveMiddlewares(middlewares = []) {
61
+ return middlewares.map((middleware) => this.resolveMiddleware(middleware));
62
+ }
63
+ static routeSpecificity(route) {
64
+ const path = route.registrationPaths.slice().sort((left, right) => right.length - left.length)[0] ?? route.path;
65
+ const segments = this.normalizePath(path).split("/").filter(Boolean);
66
+ return [
67
+ segments.filter((segment) => !segment.startsWith(":")).length,
68
+ segments.length,
69
+ path.length
70
+ ];
71
+ }
72
+ static orderedRoutes() {
73
+ return Array.from(this.routes).sort((left, right) => {
74
+ const leftScore = this.routeSpecificity(left);
75
+ const rightScore = this.routeSpecificity(right);
76
+ for (let index = 0; index < leftScore.length; index++) {
77
+ const difference = rightScore[index] - leftScore[index];
78
+ if (difference !== 0) return difference;
79
+ }
80
+ return 0;
81
+ });
82
+ }
83
+ static removeRouteMethod(route, method, path) {
84
+ route.methods = route.methods.filter((existingMethod) => existingMethod !== method);
85
+ this.routesByPathMethod.delete(`${method.toUpperCase()} ${path}`);
86
+ const methodKey = method.toUpperCase();
87
+ this.routesByMethod.set(methodKey, (this.routesByMethod.get(methodKey) ?? []).filter((existingRoute) => existingRoute !== route));
88
+ if (!route.methods.some((existingMethod) => existingMethod !== "options")) {
89
+ this.routes.delete(route);
90
+ if (route.routeName && this.routesByName.get(route.routeName) === route) this.routesByName.delete(route.routeName);
91
+ }
92
+ }
93
+ static removeRoute(route) {
94
+ this.routes.delete(route);
95
+ if (route.routeName && this.routesByName.get(route.routeName) === route) this.routesByName.delete(route.routeName);
96
+ for (const method of route.methods) {
97
+ const methodKey = method.toUpperCase();
98
+ this.routesByPathMethod.delete(`${methodKey} ${route.path}`);
99
+ this.routesByMethod.set(methodKey, (this.routesByMethod.get(methodKey) ?? []).filter((existingRoute) => existingRoute !== route));
100
+ }
101
+ }
102
+ /**
48
103
  * Resets the router to it's default state
49
104
  */
50
105
  static reset() {
@@ -456,17 +511,22 @@ var CoreRouter = class {
456
511
  const fullPath = this.normalizePath(`${activePrefix}/${path}`);
457
512
  const registrationPaths = this.routeRegistrationPaths(fullPath);
458
513
  const parameters = this.parseRouteParameters(fullPath);
459
- const route = new require_Route.Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, [
514
+ for (const method of methods) {
515
+ const existing = this.routesByPathMethod.get(`${method.toUpperCase()} ${fullPath}`);
516
+ if (existing) this.removeRouteMethod(existing, method, fullPath);
517
+ }
518
+ const route = new require_Route.Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, this.resolveMiddlewares([
460
519
  ...this.globalMiddlewares,
461
520
  ...activeGroupMiddlewares,
462
521
  ...middlewares || []
463
- ], {
522
+ ]), {
464
523
  registrationPaths,
465
524
  parameters,
466
525
  onName: (name, route, previousName) => {
467
526
  if (previousName && this.routesByName.get(previousName) === route) this.routesByName.delete(previousName);
468
527
  this.routesByName.set(name, route);
469
- }
528
+ },
529
+ normalizeMiddleware: (middleware) => this.resolveMiddleware(middleware)
470
530
  });
471
531
  if (!methods.includes("options") && !this.routesByPathMethod.get(`OPTIONS ${fullPath}`)) this.options(path, this.createDefaultOptionsHandler());
472
532
  this.routes.add(route);
@@ -491,40 +551,9 @@ var CoreRouter = class {
491
551
  const { str } = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("@h3ravel/support");
492
552
  paramName = str(basePath).singular().afterLast("/").toString();
493
553
  }
494
- const actions = {
495
- index: {
496
- method: "get",
497
- path: "/"
498
- },
499
- show: {
500
- method: "get",
501
- path: `/:${paramName}`
502
- },
503
- create: {
504
- method: "post",
505
- path: "/"
506
- },
507
- update: {
508
- method: "put",
509
- path: `/:${paramName}`
510
- },
511
- destroy: {
512
- method: "delete",
513
- path: `/:${paramName}`
514
- }
515
- };
516
- const only = options?.only || Object.keys(actions);
517
- const except = options?.except || [];
518
- const preController = typeof controller === "function" ? new controller() : controller;
519
- for (const action of only) {
520
- if (except.includes(action)) continue;
521
- if (typeof preController[action] === "function") {
522
- const { method, path } = actions[action];
523
- const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
524
- const name = `${basePath}${path}`.replace(/\/:[^/]+|\/\{[^}]+\}/g, "").replace(/\{(\w+):[^}]+\}/g, "$1").replace(/\/|:|[{}]/g, ".").replace(/\.{2,}/g, ".").replace(/^\.|\.$/g, "");
525
- this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0).name(name + "." + action.toLowerCase());
526
- }
527
- }
554
+ return new require_ResourceRoutes.ResourceRoutes(basePath, controller, paramName, options, ({ method, path, handler, middlewares, name }) => {
555
+ return this.add(method, path, handler, middlewares).name(name);
556
+ }, (route) => this.removeRoute(route)).register();
528
557
  }
529
558
  /**
530
559
  * Adds a new GET route to the router.
@@ -1,9 +1,10 @@
1
- import { ApiResourceMiddleware, ControllerAction, HttpMethod, RouterConfig } from "../types/basic.cjs";
1
+ import { ApiResourceMiddleware, HttpMethod, ResourceAction, RouterConfig } from "../types/basic.cjs";
2
2
  import { Response } from "./Response.cjs";
3
3
  import { Route } from "../Route.cjs";
4
4
  import { Request } from "./Request.cjs";
5
5
  import { ClearRouterPluginArgumentsContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, PluginArgumentsResolver, PluginBind } from "./plugins.cjs";
6
6
  import { Controller } from "../Controller.cjs";
7
+ import { ResourceRoutes } from "../ResourceRoutes.cjs";
7
8
  import { AsyncLocalStorage } from "node:async_hooks";
8
9
 
9
10
  //#region src/core/router.d.ts
@@ -37,6 +38,18 @@ declare abstract class CoreRouter {
37
38
  static prefix: string;
38
39
  static groupMiddlewares: any[];
39
40
  static globalMiddlewares: any[];
41
+ /**
42
+ * Resolve middlewares before assigning to adapter
43
+ *
44
+ * @param middleware
45
+ * @returns
46
+ */
47
+ protected static resolveMiddleware(middleware: any): any;
48
+ protected static resolveMiddlewares(middlewares?: any[]): any[];
49
+ protected static routeSpecificity(route: Route<any, any, any>): [number, number, number];
50
+ protected static orderedRoutes(): Array<Route<any, any, any>>;
51
+ protected static removeRouteMethod(route: Route<any, any, any>, method: HttpMethod, path: string): void;
52
+ protected static removeRoute(route: Route<any, any, any>): void;
40
53
  /**
41
54
  * Resets the router to it's default state
42
55
  */
@@ -131,10 +144,10 @@ declare abstract class CoreRouter {
131
144
  * @param options
132
145
  */
133
146
  static apiResource(basePath: string, controller: any, options?: {
134
- only?: ControllerAction[];
135
- except?: ControllerAction[];
147
+ only?: ResourceAction[];
148
+ except?: ResourceAction[];
136
149
  middlewares?: ApiResourceMiddleware<any>;
137
- }): void;
150
+ }): ResourceRoutes<any, any, any>;
138
151
  /**
139
152
  * Adds a new GET route to the router.
140
153
  *
@@ -1,9 +1,10 @@
1
- import { ApiResourceMiddleware, ControllerAction, HttpMethod, RouterConfig } from "../types/basic.mjs";
1
+ import { ApiResourceMiddleware, HttpMethod, ResourceAction, RouterConfig } from "../types/basic.mjs";
2
2
  import { Response } from "./Response.mjs";
3
3
  import { Route } from "../Route.mjs";
4
4
  import { Request } from "./Request.mjs";
5
5
  import { ClearRouterPluginArgumentsContext, ClearRouterPluginInput, ClearRouterPluginRequestContext, PluginArgumentsResolver, PluginBind } from "./plugins.mjs";
6
6
  import { Controller } from "../Controller.mjs";
7
+ import { ResourceRoutes } from "../ResourceRoutes.mjs";
7
8
  import { AsyncLocalStorage } from "node:async_hooks";
8
9
 
9
10
  //#region src/core/router.d.ts
@@ -37,6 +38,18 @@ declare abstract class CoreRouter {
37
38
  static prefix: string;
38
39
  static groupMiddlewares: any[];
39
40
  static globalMiddlewares: any[];
41
+ /**
42
+ * Resolve middlewares before assigning to adapter
43
+ *
44
+ * @param middleware
45
+ * @returns
46
+ */
47
+ protected static resolveMiddleware(middleware: any): any;
48
+ protected static resolveMiddlewares(middlewares?: any[]): any[];
49
+ protected static routeSpecificity(route: Route<any, any, any>): [number, number, number];
50
+ protected static orderedRoutes(): Array<Route<any, any, any>>;
51
+ protected static removeRouteMethod(route: Route<any, any, any>, method: HttpMethod, path: string): void;
52
+ protected static removeRoute(route: Route<any, any, any>): void;
40
53
  /**
41
54
  * Resets the router to it's default state
42
55
  */
@@ -131,10 +144,10 @@ declare abstract class CoreRouter {
131
144
  * @param options
132
145
  */
133
146
  static apiResource(basePath: string, controller: any, options?: {
134
- only?: ControllerAction[];
135
- except?: ControllerAction[];
147
+ only?: ResourceAction[];
148
+ except?: ResourceAction[];
136
149
  middlewares?: ApiResourceMiddleware<any>;
137
- }): void;
150
+ }): ResourceRoutes<any, any, any>;
138
151
  /**
139
152
  * Adds a new GET route to the router.
140
153
  *
@@ -1,6 +1,7 @@
1
1
  import { Request } from "./Request.mjs";
2
2
  import { Response } from "./Response.mjs";
3
3
  import { Container, getBindingMetadataFromTargets, getDesignParamTypes, getStandardMetadata, isClass } from "./bindings.mjs";
4
+ import { ResourceRoutes } from "../ResourceRoutes.mjs";
4
5
  import { Route } from "../Route.mjs";
5
6
  import { createRequire } from "node:module";
6
7
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -45,6 +46,60 @@ var CoreRouter = class {
45
46
  static groupMiddlewares = [];
46
47
  static globalMiddlewares = [];
47
48
  /**
49
+ * Resolve middlewares before assigning to adapter
50
+ *
51
+ * @param middleware
52
+ * @returns
53
+ */
54
+ static resolveMiddleware(middleware) {
55
+ if (!middleware || typeof middleware === "function" && !isClass(middleware)) return middleware;
56
+ const instance = isClass(middleware) ? new middleware() : middleware;
57
+ if (instance && typeof instance.handle === "function") return instance.handle.bind(instance);
58
+ return middleware;
59
+ }
60
+ static resolveMiddlewares(middlewares = []) {
61
+ return middlewares.map((middleware) => this.resolveMiddleware(middleware));
62
+ }
63
+ static routeSpecificity(route) {
64
+ const path = route.registrationPaths.slice().sort((left, right) => right.length - left.length)[0] ?? route.path;
65
+ const segments = this.normalizePath(path).split("/").filter(Boolean);
66
+ return [
67
+ segments.filter((segment) => !segment.startsWith(":")).length,
68
+ segments.length,
69
+ path.length
70
+ ];
71
+ }
72
+ static orderedRoutes() {
73
+ return Array.from(this.routes).sort((left, right) => {
74
+ const leftScore = this.routeSpecificity(left);
75
+ const rightScore = this.routeSpecificity(right);
76
+ for (let index = 0; index < leftScore.length; index++) {
77
+ const difference = rightScore[index] - leftScore[index];
78
+ if (difference !== 0) return difference;
79
+ }
80
+ return 0;
81
+ });
82
+ }
83
+ static removeRouteMethod(route, method, path) {
84
+ route.methods = route.methods.filter((existingMethod) => existingMethod !== method);
85
+ this.routesByPathMethod.delete(`${method.toUpperCase()} ${path}`);
86
+ const methodKey = method.toUpperCase();
87
+ this.routesByMethod.set(methodKey, (this.routesByMethod.get(methodKey) ?? []).filter((existingRoute) => existingRoute !== route));
88
+ if (!route.methods.some((existingMethod) => existingMethod !== "options")) {
89
+ this.routes.delete(route);
90
+ if (route.routeName && this.routesByName.get(route.routeName) === route) this.routesByName.delete(route.routeName);
91
+ }
92
+ }
93
+ static removeRoute(route) {
94
+ this.routes.delete(route);
95
+ if (route.routeName && this.routesByName.get(route.routeName) === route) this.routesByName.delete(route.routeName);
96
+ for (const method of route.methods) {
97
+ const methodKey = method.toUpperCase();
98
+ this.routesByPathMethod.delete(`${methodKey} ${route.path}`);
99
+ this.routesByMethod.set(methodKey, (this.routesByMethod.get(methodKey) ?? []).filter((existingRoute) => existingRoute !== route));
100
+ }
101
+ }
102
+ /**
48
103
  * Resets the router to it's default state
49
104
  */
50
105
  static reset() {
@@ -456,17 +511,22 @@ var CoreRouter = class {
456
511
  const fullPath = this.normalizePath(`${activePrefix}/${path}`);
457
512
  const registrationPaths = this.routeRegistrationPaths(fullPath);
458
513
  const parameters = this.parseRouteParameters(fullPath);
459
- const route = new Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, [
514
+ for (const method of methods) {
515
+ const existing = this.routesByPathMethod.get(`${method.toUpperCase()} ${fullPath}`);
516
+ if (existing) this.removeRouteMethod(existing, method, fullPath);
517
+ }
518
+ const route = new Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, this.resolveMiddlewares([
460
519
  ...this.globalMiddlewares,
461
520
  ...activeGroupMiddlewares,
462
521
  ...middlewares || []
463
- ], {
522
+ ]), {
464
523
  registrationPaths,
465
524
  parameters,
466
525
  onName: (name, route, previousName) => {
467
526
  if (previousName && this.routesByName.get(previousName) === route) this.routesByName.delete(previousName);
468
527
  this.routesByName.set(name, route);
469
- }
528
+ },
529
+ normalizeMiddleware: (middleware) => this.resolveMiddleware(middleware)
470
530
  });
471
531
  if (!methods.includes("options") && !this.routesByPathMethod.get(`OPTIONS ${fullPath}`)) this.options(path, this.createDefaultOptionsHandler());
472
532
  this.routes.add(route);
@@ -491,40 +551,9 @@ var CoreRouter = class {
491
551
  const { str } = createRequire(import.meta.url)("@h3ravel/support");
492
552
  paramName = str(basePath).singular().afterLast("/").toString();
493
553
  }
494
- const actions = {
495
- index: {
496
- method: "get",
497
- path: "/"
498
- },
499
- show: {
500
- method: "get",
501
- path: `/:${paramName}`
502
- },
503
- create: {
504
- method: "post",
505
- path: "/"
506
- },
507
- update: {
508
- method: "put",
509
- path: `/:${paramName}`
510
- },
511
- destroy: {
512
- method: "delete",
513
- path: `/:${paramName}`
514
- }
515
- };
516
- const only = options?.only || Object.keys(actions);
517
- const except = options?.except || [];
518
- const preController = typeof controller === "function" ? new controller() : controller;
519
- for (const action of only) {
520
- if (except.includes(action)) continue;
521
- if (typeof preController[action] === "function") {
522
- const { method, path } = actions[action];
523
- const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
524
- const name = `${basePath}${path}`.replace(/\/:[^/]+|\/\{[^}]+\}/g, "").replace(/\{(\w+):[^}]+\}/g, "$1").replace(/\/|:|[{}]/g, ".").replace(/\.{2,}/g, ".").replace(/^\.|\.$/g, "");
525
- this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0).name(name + "." + action.toLowerCase());
526
- }
527
- }
554
+ return new ResourceRoutes(basePath, controller, paramName, options, ({ method, path, handler, middlewares, name }) => {
555
+ return this.add(method, path, handler, middlewares).name(name);
556
+ }, (route) => this.removeRoute(route)).register();
528
557
  }
529
558
  /**
530
559
  * Adds a new GET route to the router.
@@ -1,3 +1,2 @@
1
1
  import { Bind, BindDecorator, BindFactory, BindToken, BindValue, Container } from "../core/bindings.mjs";
2
- import "reflect-metadata";
3
2
  export { Bind, BindDecorator, BindFactory, BindToken, BindValue, Container };
@@ -62,7 +62,7 @@ var Router = class Router extends require_router.CoreRouter {
62
62
  * @param options
63
63
  */
64
64
  static apiResource(basePath, controller, options) {
65
- super.apiResource(basePath, controller, options);
65
+ return super.apiResource(basePath, controller, options);
66
66
  }
67
67
  /**
68
68
  * Adds a new GET route to the router.
@@ -160,7 +160,7 @@ var Router = class Router extends require_router.CoreRouter {
160
160
  return super.route(name);
161
161
  }
162
162
  static async apply(router) {
163
- for (const route of Array.from(this.routes)) {
163
+ for (const route of this.orderedRoutes()) {
164
164
  let handlerFunction = null;
165
165
  let instance = null;
166
166
  let bindingTarget;
@@ -1,6 +1,7 @@
1
- import { ApiResourceMiddleware, ControllerAction, HttpMethod } from "../types/basic.cjs";
1
+ import { ApiResourceMiddleware, HttpMethod, ResourceAction } from "../types/basic.cjs";
2
2
  import { Handler, HttpContext, Middleware } from "../types/express.cjs";
3
3
  import { Route } from "../Route.cjs";
4
+ import { ResourceRoutes } from "../ResourceRoutes.cjs";
4
5
  import { CoreRouter } from "../core/router.cjs";
5
6
  import { Router } from "express";
6
7
 
@@ -33,10 +34,10 @@ declare class Router$1 extends CoreRouter {
33
34
  * @param options
34
35
  */
35
36
  static apiResource(basePath: string, controller: any, options?: {
36
- only?: ControllerAction[];
37
- except?: ControllerAction[];
37
+ only?: ResourceAction[];
38
+ except?: ResourceAction[];
38
39
  middlewares?: ApiResourceMiddleware<Middleware>;
39
- }): void;
40
+ }): ResourceRoutes<HttpContext, Middleware, Handler>;
40
41
  /**
41
42
  * Adds a new GET route to the router.
42
43
  *
@@ -1,6 +1,7 @@
1
- import { ApiResourceMiddleware, ControllerAction, HttpMethod } from "../types/basic.mjs";
1
+ import { ApiResourceMiddleware, HttpMethod, ResourceAction } from "../types/basic.mjs";
2
2
  import { Handler, HttpContext, Middleware } from "../types/express.mjs";
3
3
  import { Route } from "../Route.mjs";
4
+ import { ResourceRoutes } from "../ResourceRoutes.mjs";
4
5
  import { CoreRouter } from "../core/router.mjs";
5
6
  import { Router } from "express";
6
7
 
@@ -33,10 +34,10 @@ declare class Router$1 extends CoreRouter {
33
34
  * @param options
34
35
  */
35
36
  static apiResource(basePath: string, controller: any, options?: {
36
- only?: ControllerAction[];
37
- except?: ControllerAction[];
37
+ only?: ResourceAction[];
38
+ except?: ResourceAction[];
38
39
  middlewares?: ApiResourceMiddleware<Middleware>;
39
- }): void;
40
+ }): ResourceRoutes<HttpContext, Middleware, Handler>;
40
41
  /**
41
42
  * Adds a new GET route to the router.
42
43
  *