clear-router 2.6.2 → 2.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -20,6 +20,11 @@ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
20
20
  type RequestData = Record<string, any>;
21
21
  type ApiResourceMiddleware<M = any> = M | M[] | { [K in ControllerAction]?: M | M[] };
22
22
  interface RouterConfig {
23
+ /**
24
+ * When enabled, API param name will be infered from the route path.
25
+ * So instead of getting /api/users/:id, we will now get /api/users/:user
26
+ */
27
+ inferParamName?: boolean;
23
28
  /**
24
29
  * Configuration for method override functionality, allowing clients to use a
25
30
  * specific header or body parameter to override the HTTP method.
@@ -239,6 +244,19 @@ declare abstract class CoreRouter {
239
244
  private static readonly pluginArgumentResolversKey;
240
245
  private static requestProvider?;
241
246
  private static responseProvider?;
247
+ static config: RouterConfig;
248
+ protected static groupContext: AsyncLocalStorage<{
249
+ prefix: string;
250
+ groupMiddlewares: any[];
251
+ }>;
252
+ protected static pluginRequestContext: AsyncLocalStorage<ClearRouterPluginRequestContext>;
253
+ static routes: Set<Route<any, any, any>>;
254
+ static routesByPathMethod: Map<string, Route<any, any, any>>;
255
+ static routesByMethod: Map<"GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD", Route<any, any, any>[]>;
256
+ static routesByName: Map<string, Route<any, any, any>>;
257
+ static prefix: string;
258
+ static groupMiddlewares: any[];
259
+ static globalMiddlewares: any[];
242
260
  /**
243
261
  * Resets the router to it's default state
244
262
  */
@@ -257,17 +275,16 @@ declare abstract class CoreRouter {
257
275
  prefix: string;
258
276
  groupMiddlewares: any[];
259
277
  }>;
260
- routes: Array<Route<any, any, any>>;
261
- routesByPathMethod: Record<string, Route<any, any, any>>;
262
- routesByMethod: { [method in Uppercase<HttpMethod>]?: Array<Route<any, any, any>> };
263
- routesByName: Record<string, Route<any, any, any>>;
278
+ routes: Set<never>;
279
+ routesByPathMethod: Map<any, any>;
280
+ routesByMethod: Map<any, any>;
281
+ routesByName: Map<any, any>;
264
282
  prefix: string;
265
283
  groupMiddlewares: any[];
266
284
  globalMiddlewares: any[];
267
285
  };
268
286
  protected static bindStateAccessors(): void;
269
287
  protected static createDefaultOptionsHandler(): any;
270
- static config: RouterConfig;
271
288
  static configureDefaults(options?: RouterConfig): void;
272
289
  /**
273
290
  * Use a registered plugin
@@ -279,18 +296,6 @@ declare abstract class CoreRouter {
279
296
  */
280
297
  static use<Options = any>(plugin: ClearRouterPluginInput<Options>, options?: Options): Promise<void>;
281
298
  protected static pluginsReady(): Promise<void>;
282
- protected static groupContext: AsyncLocalStorage<{
283
- prefix: string;
284
- groupMiddlewares: any[];
285
- }>;
286
- protected static pluginRequestContext: AsyncLocalStorage<ClearRouterPluginRequestContext>;
287
- static routes: Array<Route<any, any, any>>;
288
- static routesByPathMethod: Record<string, Route<any, any, any>>;
289
- static routesByMethod: { [method in Uppercase<HttpMethod>]?: Array<Route<any, any, any>> };
290
- static routesByName: Record<string, Route<any, any, any>>;
291
- static prefix: string;
292
- static groupMiddlewares: any[];
293
- static globalMiddlewares: any[];
294
299
  protected static getCurrentPluginRequestContext(): ClearRouterPluginRequestContext | undefined;
295
300
  protected static createPluginRequestContext(ctx: any): ClearRouterPluginRequestContext;
296
301
  protected static createPluginBind(): PluginBind;
@@ -455,6 +460,7 @@ declare abstract class CoreRouter {
455
460
  * @param provider
456
461
  */
457
462
  static setResponseProvider(provider: typeof Response$1): void;
463
+ private static hasPackageInstalled;
458
464
  /**
459
465
  * Provide a class that will overide the base Response instance
460
466
  *
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { createRequire } from "node:module";
1
2
  import { AsyncLocalStorage } from "node:async_hooks";
2
3
 
3
4
  //#region src/ClearRequest.ts
@@ -300,21 +301,43 @@ var CoreRouter = class {
300
301
  static pluginArgumentResolversKey = Symbol.for("clear-router:plugin-argument-resolvers");
301
302
  static requestProvider;
302
303
  static responseProvider;
304
+ static config = {
305
+ inferParamName: false,
306
+ methodOverride: {
307
+ enabled: true,
308
+ bodyKeys: ["_method"],
309
+ headerKeys: ["x-http-method"]
310
+ },
311
+ container: {
312
+ enabled: false,
313
+ autoDiscover: false
314
+ }
315
+ };
316
+ static groupContext = new AsyncLocalStorage();
317
+ static pluginRequestContext = new AsyncLocalStorage();
318
+ static routes = /* @__PURE__ */ new Set([]);
319
+ static routesByPathMethod = /* @__PURE__ */ new Map();
320
+ static routesByMethod = /* @__PURE__ */ new Map();
321
+ static routesByName = /* @__PURE__ */ new Map();
322
+ static prefix = "";
323
+ static groupMiddlewares = [];
324
+ static globalMiddlewares = [];
303
325
  /**
304
326
  * Resets the router to it's default state
305
327
  */
306
328
  static reset() {
307
- this.routes = [];
329
+ this.routes.clear();
308
330
  this.prefix = "";
309
331
  this.groupMiddlewares = [];
310
332
  this.globalMiddlewares = [];
311
- this.routesByPathMethod = {};
312
- this.routesByMethod = {};
313
- this.routesByName = {};
333
+ this.routesByPathMethod.clear();
334
+ this.routesByMethod.clear();
335
+ this.routesByName.clear();
314
336
  return this;
315
337
  }
316
338
  static createBaseConfig() {
317
339
  return {
340
+ inferParamName: false,
318
341
  methodOverride: {
319
342
  enabled: true,
320
343
  bodyKeys: ["_method"],
@@ -342,6 +365,7 @@ var CoreRouter = class {
342
365
  const g = globalThis;
343
366
  if (!g[this.defaultConfigKey]) g[this.defaultConfigKey] = this.createBaseConfig();
344
367
  return {
368
+ inferParamName: g[this.defaultConfigKey].inferParamName,
345
369
  methodOverride: { ...g[this.defaultConfigKey].methodOverride },
346
370
  container: { ...g[this.defaultConfigKey].container }
347
371
  };
@@ -373,10 +397,10 @@ var CoreRouter = class {
373
397
  return {
374
398
  config: this.getDefaultConfig(),
375
399
  groupContext: new AsyncLocalStorage(),
376
- routes: [],
377
- routesByPathMethod: {},
378
- routesByMethod: {},
379
- routesByName: {},
400
+ routes: /* @__PURE__ */ new Set([]),
401
+ routesByPathMethod: /* @__PURE__ */ new Map(),
402
+ routesByMethod: /* @__PURE__ */ new Map(),
403
+ routesByName: /* @__PURE__ */ new Map(),
380
404
  prefix: "",
381
405
  groupMiddlewares: [],
382
406
  globalMiddlewares: []
@@ -450,17 +474,6 @@ var CoreRouter = class {
450
474
  }
451
475
  };
452
476
  }
453
- static config = {
454
- methodOverride: {
455
- enabled: true,
456
- bodyKeys: ["_method"],
457
- headerKeys: ["x-http-method"]
458
- },
459
- container: {
460
- enabled: false,
461
- autoDiscover: false
462
- }
463
- };
464
477
  static configureDefaults(options) {
465
478
  const g = globalThis;
466
479
  const defaults = this.mergeConfig(g[this.defaultConfigKey] || this.createBaseConfig(), options);
@@ -521,15 +534,6 @@ var CoreRouter = class {
521
534
  if (!pending.length) return;
522
535
  await Promise.all(pending);
523
536
  }
524
- static groupContext = new AsyncLocalStorage();
525
- static pluginRequestContext = new AsyncLocalStorage();
526
- static routes = [];
527
- static routesByPathMethod = {};
528
- static routesByMethod = {};
529
- static routesByName = {};
530
- static prefix = "";
531
- static groupMiddlewares = [];
532
- static globalMiddlewares = [];
533
537
  static getCurrentPluginRequestContext() {
534
538
  return this.pluginRequestContext.getStore();
535
539
  }
@@ -575,10 +579,10 @@ var CoreRouter = class {
575
579
  headerKeys: ["x-http-method"]
576
580
  } };
577
581
  if (!this.groupContext) this.groupContext = new AsyncLocalStorage();
578
- if (!Array.isArray(this.routes)) this.routes = [];
579
- if (!this.routesByPathMethod || typeof this.routesByPathMethod !== "object") this.routesByPathMethod = {};
580
- if (!this.routesByMethod || typeof this.routesByMethod !== "object") this.routesByMethod = {};
581
- if (!this.routesByName || typeof this.routesByName !== "object") this.routesByName = {};
582
+ if (!this.routes || Array.isArray(this.routes)) this.routes = new Set(this.routes ?? []);
583
+ if (!this.routesByPathMethod) this.routesByPathMethod = /* @__PURE__ */ new Map();
584
+ if (!this.routesByMethod) this.routesByMethod = /* @__PURE__ */ new Map();
585
+ if (!this.routesByName) this.routesByName = /* @__PURE__ */ new Map();
582
586
  if (typeof this.prefix !== "string") this.prefix = "";
583
587
  if (!Array.isArray(this.groupMiddlewares)) this.groupMiddlewares = [];
584
588
  if (!Array.isArray(this.globalMiddlewares)) this.globalMiddlewares = [];
@@ -649,13 +653,15 @@ var CoreRouter = class {
649
653
  if (typeof container.enabled === "boolean") this.config.container.enabled = container.enabled;
650
654
  if (typeof container.autoDiscover === "boolean") this.config.container.autoDiscover = container.autoDiscover;
651
655
  }
656
+ if (options?.inferParamName) this.config.inferParamName = options?.inferParamName;
652
657
  const override = options?.methodOverride;
653
- if (!override) return;
654
- if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
655
- const bodyKeys = override.bodyKeys;
656
- if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
657
- const headerKeys = override.headerKeys;
658
- if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
658
+ if (override) {
659
+ if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
660
+ const bodyKeys = override.bodyKeys;
661
+ if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
662
+ const headerKeys = override.headerKeys;
663
+ if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
664
+ }
659
665
  }
660
666
  static resolveMethodOverride(method, headers, body) {
661
667
  this.ensureState();
@@ -717,16 +723,16 @@ var CoreRouter = class {
717
723
  registrationPaths,
718
724
  parameters,
719
725
  onName: (name, route, previousName) => {
720
- if (previousName && this.routesByName[previousName] === route) delete this.routesByName[previousName];
721
- this.routesByName[name] = route;
726
+ if (previousName && this.routesByName.get(previousName) === route) this.routesByName.delete(previousName);
727
+ this.routesByName.set(name, route);
722
728
  }
723
729
  });
724
- if (!methods.includes("options") && !this.routesByPathMethod[`OPTIONS ${fullPath}`]) this.options(path, this.createDefaultOptionsHandler());
725
- this.routes.push(route);
730
+ if (!methods.includes("options") && !this.routesByPathMethod.get(`OPTIONS ${fullPath}`)) this.options(path, this.createDefaultOptionsHandler());
731
+ this.routes.add(route);
726
732
  for (const method of methods.map((m) => m.toUpperCase())) {
727
- this.routesByPathMethod[`${method} ${fullPath}`] = route;
728
- if (!this.routesByMethod[method]) this.routesByMethod[method] = [];
729
- this.routesByMethod[method].push(route);
733
+ this.routesByPathMethod.set(`${method} ${fullPath}`, route);
734
+ if (!this.routesByMethod.has(method)) this.routesByMethod.set(method, []);
735
+ this.routesByMethod.get(method)?.push(route);
730
736
  }
731
737
  return route;
732
738
  }
@@ -739,6 +745,11 @@ var CoreRouter = class {
739
745
  * @param options
740
746
  */
741
747
  static apiResource(basePath, controller, options) {
748
+ let paramName = "id";
749
+ if (!!this.config.inferParamName && this.hasPackageInstalled("@h3ravel/support")) {
750
+ const { str } = createRequire(import.meta.url)("@h3ravel/support");
751
+ paramName = str(basePath).singular().afterLast("/").toString();
752
+ }
742
753
  const actions = {
743
754
  index: {
744
755
  method: "get",
@@ -746,7 +757,7 @@ var CoreRouter = class {
746
757
  },
747
758
  show: {
748
759
  method: "get",
749
- path: "/:id"
760
+ path: `/:${paramName}`
750
761
  },
751
762
  create: {
752
763
  method: "post",
@@ -754,11 +765,11 @@ var CoreRouter = class {
754
765
  },
755
766
  update: {
756
767
  method: "put",
757
- path: "/:id"
768
+ path: `/:${paramName}`
758
769
  },
759
770
  destroy: {
760
771
  method: "delete",
761
- path: "/:id"
772
+ path: `/:${paramName}`
762
773
  }
763
774
  };
764
775
  const only = options?.only || Object.keys(actions);
@@ -769,7 +780,8 @@ var CoreRouter = class {
769
780
  if (typeof preController[action] === "function") {
770
781
  const { method, path } = actions[action];
771
782
  const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
772
- this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0);
783
+ const name = `${basePath}${path}`.replace(/\{(\w+):[^}]+\}/g, "$1").replace(/\/|:|[{}]/g, ".").replace(/\.{2,}/g, ".").replace(/^\.|\.$/g, "");
784
+ this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0).name(name + "." + action.toLowerCase());
773
785
  }
774
786
  }
775
787
  }
@@ -888,14 +900,14 @@ var CoreRouter = class {
888
900
  }
889
901
  static allRoutes(type) {
890
902
  this.ensureState();
891
- if (type === "method") return this.routesByMethod;
892
- if (type === "path") return this.routesByPathMethod;
893
- if (type === "name") return this.routesByName;
894
- return this.routes.filter((e) => e.methods.length > 1 || e.methods[0] !== "options");
903
+ if (type === "method") return Object.fromEntries(this.routesByMethod.entries());
904
+ if (type === "path") return Object.fromEntries(this.routesByPathMethod.entries());
905
+ if (type === "name") return Object.fromEntries(this.routesByName.entries());
906
+ return Array.from(this.routes).filter((e) => e.methods.length > 1 || e.methods[0] !== "options");
895
907
  }
896
908
  static route(name) {
897
909
  this.ensureState();
898
- return this.routesByName[name];
910
+ return this.routesByName.get(name);
899
911
  }
900
912
  static url(name, params) {
901
913
  return this.route(name)?.toPath(params);
@@ -916,6 +928,14 @@ var CoreRouter = class {
916
928
  static setResponseProvider(provider) {
917
929
  this.responseProvider = provider;
918
930
  }
931
+ static hasPackageInstalled(name) {
932
+ try {
933
+ createRequire(import.meta.url).resolve(name, { paths: [process.cwd()] });
934
+ return true;
935
+ } catch {
936
+ return false;
937
+ }
938
+ }
919
939
  static initializeInstance(provider, args) {
920
940
  const isRequest = [
921
941
  "CoreRequest",
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  require('../bindings-CLsZjOEy.cjs');
3
- const require_router = require('../router--8gWGXv-.cjs');
3
+ const require_router = require('../router-DJvP-A5M.cjs');
4
4
  const require_responses = require('../responses-Bvnk0uvc.cjs');
5
5
 
6
6
  //#region src/koa/router.ts
@@ -184,7 +184,7 @@ var Router = class Router extends require_router.CoreRouter {
184
184
  * @returns The @koa/router instance with the applied routes
185
185
  */
186
186
  static apply(router) {
187
- for (const route of this.routes) {
187
+ for (const route of Array.from(this.routes)) {
188
188
  let handlerFunction = null;
189
189
  let instance = null;
190
190
  let bindingTarget;
@@ -1,4 +1,4 @@
1
- import { F as ControllerAction, I as HttpMethod, P as ApiResourceMiddleware, _ as KoaRouterApp, g as HttpContext, h as Handler, m as Route, t as CoreRouter, v as Middleware } from "../router-Bu4kNHUo.cjs";
1
+ import { F as ControllerAction, I as HttpMethod, P as ApiResourceMiddleware, _ as KoaRouterApp, g as HttpContext, h as Handler, m as Route, t as CoreRouter, v as Middleware } from "../router-BITqScD_.cjs";
2
2
 
3
3
  //#region src/koa/router.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { F as ControllerAction, I as HttpMethod, P as ApiResourceMiddleware, _ as KoaRouterApp, g as HttpContext, h as Handler, m as Route, t as CoreRouter, v as Middleware } from "../router-awXi28kb.mjs";
1
+ import { F as ControllerAction, I as HttpMethod, P as ApiResourceMiddleware, _ as KoaRouterApp, g as HttpContext, h as Handler, m as Route, t as CoreRouter, v as Middleware } from "../router-Cs8cC5zd.mjs";
2
2
 
3
3
  //#region src/koa/router.d.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  import "../bindings-XLDXFpHZ.mjs";
2
- import { t as CoreRouter } from "../router-DgZmT-17.mjs";
2
+ import { t as CoreRouter } from "../router-9HWz4Tlp.mjs";
3
3
  import { n as resolveResponseMeta, t as isFetchResponse } from "../responses-BvETUeDL.mjs";
4
4
 
5
5
  //#region src/koa/router.ts
@@ -183,7 +183,7 @@ var Router = class Router extends CoreRouter {
183
183
  * @returns The @koa/router instance with the applied routes
184
184
  */
185
185
  static apply(router) {
186
- for (const route of this.routes) {
186
+ for (const route of Array.from(this.routes)) {
187
187
  let handlerFunction = null;
188
188
  let instance = null;
189
189
  let bindingTarget;
@@ -1,4 +1,5 @@
1
1
  import { a as getStandardMetadata, c as Request, i as getDesignParamTypes, n as Container, o as isClass, r as getBindingMetadataFromTargets, s as Response } from "./bindings-XLDXFpHZ.mjs";
2
+ import { createRequire } from "node:module";
2
3
  import { AsyncLocalStorage } from "node:async_hooks";
3
4
 
4
5
  //#region src/Route.ts
@@ -74,21 +75,43 @@ var CoreRouter = class {
74
75
  static pluginArgumentResolversKey = Symbol.for("clear-router:plugin-argument-resolvers");
75
76
  static requestProvider;
76
77
  static responseProvider;
78
+ static config = {
79
+ inferParamName: false,
80
+ methodOverride: {
81
+ enabled: true,
82
+ bodyKeys: ["_method"],
83
+ headerKeys: ["x-http-method"]
84
+ },
85
+ container: {
86
+ enabled: false,
87
+ autoDiscover: false
88
+ }
89
+ };
90
+ static groupContext = new AsyncLocalStorage();
91
+ static pluginRequestContext = new AsyncLocalStorage();
92
+ static routes = /* @__PURE__ */ new Set([]);
93
+ static routesByPathMethod = /* @__PURE__ */ new Map();
94
+ static routesByMethod = /* @__PURE__ */ new Map();
95
+ static routesByName = /* @__PURE__ */ new Map();
96
+ static prefix = "";
97
+ static groupMiddlewares = [];
98
+ static globalMiddlewares = [];
77
99
  /**
78
100
  * Resets the router to it's default state
79
101
  */
80
102
  static reset() {
81
- this.routes = [];
103
+ this.routes.clear();
82
104
  this.prefix = "";
83
105
  this.groupMiddlewares = [];
84
106
  this.globalMiddlewares = [];
85
- this.routesByPathMethod = {};
86
- this.routesByMethod = {};
87
- this.routesByName = {};
107
+ this.routesByPathMethod.clear();
108
+ this.routesByMethod.clear();
109
+ this.routesByName.clear();
88
110
  return this;
89
111
  }
90
112
  static createBaseConfig() {
91
113
  return {
114
+ inferParamName: false,
92
115
  methodOverride: {
93
116
  enabled: true,
94
117
  bodyKeys: ["_method"],
@@ -116,6 +139,7 @@ var CoreRouter = class {
116
139
  const g = globalThis;
117
140
  if (!g[this.defaultConfigKey]) g[this.defaultConfigKey] = this.createBaseConfig();
118
141
  return {
142
+ inferParamName: g[this.defaultConfigKey].inferParamName,
119
143
  methodOverride: { ...g[this.defaultConfigKey].methodOverride },
120
144
  container: { ...g[this.defaultConfigKey].container }
121
145
  };
@@ -147,10 +171,10 @@ var CoreRouter = class {
147
171
  return {
148
172
  config: this.getDefaultConfig(),
149
173
  groupContext: new AsyncLocalStorage(),
150
- routes: [],
151
- routesByPathMethod: {},
152
- routesByMethod: {},
153
- routesByName: {},
174
+ routes: /* @__PURE__ */ new Set([]),
175
+ routesByPathMethod: /* @__PURE__ */ new Map(),
176
+ routesByMethod: /* @__PURE__ */ new Map(),
177
+ routesByName: /* @__PURE__ */ new Map(),
154
178
  prefix: "",
155
179
  groupMiddlewares: [],
156
180
  globalMiddlewares: []
@@ -224,17 +248,6 @@ var CoreRouter = class {
224
248
  }
225
249
  };
226
250
  }
227
- static config = {
228
- methodOverride: {
229
- enabled: true,
230
- bodyKeys: ["_method"],
231
- headerKeys: ["x-http-method"]
232
- },
233
- container: {
234
- enabled: false,
235
- autoDiscover: false
236
- }
237
- };
238
251
  static configureDefaults(options) {
239
252
  const g = globalThis;
240
253
  const defaults = this.mergeConfig(g[this.defaultConfigKey] || this.createBaseConfig(), options);
@@ -295,15 +308,6 @@ var CoreRouter = class {
295
308
  if (!pending.length) return;
296
309
  await Promise.all(pending);
297
310
  }
298
- static groupContext = new AsyncLocalStorage();
299
- static pluginRequestContext = new AsyncLocalStorage();
300
- static routes = [];
301
- static routesByPathMethod = {};
302
- static routesByMethod = {};
303
- static routesByName = {};
304
- static prefix = "";
305
- static groupMiddlewares = [];
306
- static globalMiddlewares = [];
307
311
  static getCurrentPluginRequestContext() {
308
312
  return this.pluginRequestContext.getStore();
309
313
  }
@@ -349,10 +353,10 @@ var CoreRouter = class {
349
353
  headerKeys: ["x-http-method"]
350
354
  } };
351
355
  if (!this.groupContext) this.groupContext = new AsyncLocalStorage();
352
- if (!Array.isArray(this.routes)) this.routes = [];
353
- if (!this.routesByPathMethod || typeof this.routesByPathMethod !== "object") this.routesByPathMethod = {};
354
- if (!this.routesByMethod || typeof this.routesByMethod !== "object") this.routesByMethod = {};
355
- if (!this.routesByName || typeof this.routesByName !== "object") this.routesByName = {};
356
+ if (!this.routes || Array.isArray(this.routes)) this.routes = new Set(this.routes ?? []);
357
+ if (!this.routesByPathMethod) this.routesByPathMethod = /* @__PURE__ */ new Map();
358
+ if (!this.routesByMethod) this.routesByMethod = /* @__PURE__ */ new Map();
359
+ if (!this.routesByName) this.routesByName = /* @__PURE__ */ new Map();
356
360
  if (typeof this.prefix !== "string") this.prefix = "";
357
361
  if (!Array.isArray(this.groupMiddlewares)) this.groupMiddlewares = [];
358
362
  if (!Array.isArray(this.globalMiddlewares)) this.globalMiddlewares = [];
@@ -423,13 +427,15 @@ var CoreRouter = class {
423
427
  if (typeof container.enabled === "boolean") this.config.container.enabled = container.enabled;
424
428
  if (typeof container.autoDiscover === "boolean") this.config.container.autoDiscover = container.autoDiscover;
425
429
  }
430
+ if (options?.inferParamName) this.config.inferParamName = options?.inferParamName;
426
431
  const override = options?.methodOverride;
427
- if (!override) return;
428
- if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
429
- const bodyKeys = override.bodyKeys;
430
- if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
431
- const headerKeys = override.headerKeys;
432
- if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
432
+ if (override) {
433
+ if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
434
+ const bodyKeys = override.bodyKeys;
435
+ if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
436
+ const headerKeys = override.headerKeys;
437
+ if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
438
+ }
433
439
  }
434
440
  static resolveMethodOverride(method, headers, body) {
435
441
  this.ensureState();
@@ -491,16 +497,16 @@ var CoreRouter = class {
491
497
  registrationPaths,
492
498
  parameters,
493
499
  onName: (name, route, previousName) => {
494
- if (previousName && this.routesByName[previousName] === route) delete this.routesByName[previousName];
495
- this.routesByName[name] = route;
500
+ if (previousName && this.routesByName.get(previousName) === route) this.routesByName.delete(previousName);
501
+ this.routesByName.set(name, route);
496
502
  }
497
503
  });
498
- if (!methods.includes("options") && !this.routesByPathMethod[`OPTIONS ${fullPath}`]) this.options(path, this.createDefaultOptionsHandler());
499
- this.routes.push(route);
504
+ if (!methods.includes("options") && !this.routesByPathMethod.get(`OPTIONS ${fullPath}`)) this.options(path, this.createDefaultOptionsHandler());
505
+ this.routes.add(route);
500
506
  for (const method of methods.map((m) => m.toUpperCase())) {
501
- this.routesByPathMethod[`${method} ${fullPath}`] = route;
502
- if (!this.routesByMethod[method]) this.routesByMethod[method] = [];
503
- this.routesByMethod[method].push(route);
507
+ this.routesByPathMethod.set(`${method} ${fullPath}`, route);
508
+ if (!this.routesByMethod.has(method)) this.routesByMethod.set(method, []);
509
+ this.routesByMethod.get(method)?.push(route);
504
510
  }
505
511
  return route;
506
512
  }
@@ -513,6 +519,11 @@ var CoreRouter = class {
513
519
  * @param options
514
520
  */
515
521
  static apiResource(basePath, controller, options) {
522
+ let paramName = "id";
523
+ if (!!this.config.inferParamName && this.hasPackageInstalled("@h3ravel/support")) {
524
+ const { str } = createRequire(import.meta.url)("@h3ravel/support");
525
+ paramName = str(basePath).singular().afterLast("/").toString();
526
+ }
516
527
  const actions = {
517
528
  index: {
518
529
  method: "get",
@@ -520,7 +531,7 @@ var CoreRouter = class {
520
531
  },
521
532
  show: {
522
533
  method: "get",
523
- path: "/:id"
534
+ path: `/:${paramName}`
524
535
  },
525
536
  create: {
526
537
  method: "post",
@@ -528,11 +539,11 @@ var CoreRouter = class {
528
539
  },
529
540
  update: {
530
541
  method: "put",
531
- path: "/:id"
542
+ path: `/:${paramName}`
532
543
  },
533
544
  destroy: {
534
545
  method: "delete",
535
- path: "/:id"
546
+ path: `/:${paramName}`
536
547
  }
537
548
  };
538
549
  const only = options?.only || Object.keys(actions);
@@ -543,7 +554,8 @@ var CoreRouter = class {
543
554
  if (typeof preController[action] === "function") {
544
555
  const { method, path } = actions[action];
545
556
  const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
546
- this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0);
557
+ const name = `${basePath}${path}`.replace(/\{(\w+):[^}]+\}/g, "$1").replace(/\/|:|[{}]/g, ".").replace(/\.{2,}/g, ".").replace(/^\.|\.$/g, "");
558
+ this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0).name(name + "." + action.toLowerCase());
547
559
  }
548
560
  }
549
561
  }
@@ -662,14 +674,14 @@ var CoreRouter = class {
662
674
  }
663
675
  static allRoutes(type) {
664
676
  this.ensureState();
665
- if (type === "method") return this.routesByMethod;
666
- if (type === "path") return this.routesByPathMethod;
667
- if (type === "name") return this.routesByName;
668
- return this.routes.filter((e) => e.methods.length > 1 || e.methods[0] !== "options");
677
+ if (type === "method") return Object.fromEntries(this.routesByMethod.entries());
678
+ if (type === "path") return Object.fromEntries(this.routesByPathMethod.entries());
679
+ if (type === "name") return Object.fromEntries(this.routesByName.entries());
680
+ return Array.from(this.routes).filter((e) => e.methods.length > 1 || e.methods[0] !== "options");
669
681
  }
670
682
  static route(name) {
671
683
  this.ensureState();
672
- return this.routesByName[name];
684
+ return this.routesByName.get(name);
673
685
  }
674
686
  static url(name, params) {
675
687
  return this.route(name)?.toPath(params);
@@ -690,6 +702,14 @@ var CoreRouter = class {
690
702
  static setResponseProvider(provider) {
691
703
  this.responseProvider = provider;
692
704
  }
705
+ static hasPackageInstalled(name) {
706
+ try {
707
+ createRequire(import.meta.url).resolve(name, { paths: [process.cwd()] });
708
+ return true;
709
+ } catch {
710
+ return false;
711
+ }
712
+ }
693
713
  static initializeInstance(provider, args) {
694
714
  const isRequest = [
695
715
  "CoreRequest",