clear-router 2.6.1 → 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,23 @@ 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[];
260
+ /**
261
+ * Resets the router to it's default state
262
+ */
263
+ static reset(): typeof CoreRouter;
242
264
  protected static createBaseConfig(): RouterConfig;
243
265
  protected static mergeConfig(target: RouterConfig, source?: RouterConfig): RouterConfig;
244
266
  protected static getDefaultConfig(): RouterConfig;
@@ -253,17 +275,16 @@ declare abstract class CoreRouter {
253
275
  prefix: string;
254
276
  groupMiddlewares: any[];
255
277
  }>;
256
- routes: Array<Route<any, any, any>>;
257
- routesByPathMethod: Record<string, Route<any, any, any>>;
258
- routesByMethod: { [method in Uppercase<HttpMethod>]?: Array<Route<any, any, any>> };
259
- 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>;
260
282
  prefix: string;
261
283
  groupMiddlewares: any[];
262
284
  globalMiddlewares: any[];
263
285
  };
264
286
  protected static bindStateAccessors(): void;
265
287
  protected static createDefaultOptionsHandler(): any;
266
- static config: RouterConfig;
267
288
  static configureDefaults(options?: RouterConfig): void;
268
289
  /**
269
290
  * Use a registered plugin
@@ -275,18 +296,6 @@ declare abstract class CoreRouter {
275
296
  */
276
297
  static use<Options = any>(plugin: ClearRouterPluginInput<Options>, options?: Options): Promise<void>;
277
298
  protected static pluginsReady(): Promise<void>;
278
- protected static groupContext: AsyncLocalStorage<{
279
- prefix: string;
280
- groupMiddlewares: any[];
281
- }>;
282
- protected static pluginRequestContext: AsyncLocalStorage<ClearRouterPluginRequestContext>;
283
- static routes: Array<Route<any, any, any>>;
284
- static routesByPathMethod: Record<string, Route<any, any, any>>;
285
- static routesByMethod: { [method in Uppercase<HttpMethod>]?: Array<Route<any, any, any>> };
286
- static routesByName: Record<string, Route<any, any, any>>;
287
- static prefix: string;
288
- static groupMiddlewares: any[];
289
- static globalMiddlewares: any[];
290
299
  protected static getCurrentPluginRequestContext(): ClearRouterPluginRequestContext | undefined;
291
300
  protected static createPluginRequestContext(ctx: any): ClearRouterPluginRequestContext;
292
301
  protected static createPluginBind(): PluginBind;
@@ -451,6 +460,7 @@ declare abstract class CoreRouter {
451
460
  * @param provider
452
461
  */
453
462
  static setResponseProvider(provider: typeof Response$1): void;
463
+ private static hasPackageInstalled;
454
464
  /**
455
465
  * Provide a class that will overide the base Response instance
456
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,8 +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 = [];
325
+ /**
326
+ * Resets the router to it's default state
327
+ */
328
+ static reset() {
329
+ this.routes.clear();
330
+ this.prefix = "";
331
+ this.groupMiddlewares = [];
332
+ this.globalMiddlewares = [];
333
+ this.routesByPathMethod.clear();
334
+ this.routesByMethod.clear();
335
+ this.routesByName.clear();
336
+ return this;
337
+ }
303
338
  static createBaseConfig() {
304
339
  return {
340
+ inferParamName: false,
305
341
  methodOverride: {
306
342
  enabled: true,
307
343
  bodyKeys: ["_method"],
@@ -329,6 +365,7 @@ var CoreRouter = class {
329
365
  const g = globalThis;
330
366
  if (!g[this.defaultConfigKey]) g[this.defaultConfigKey] = this.createBaseConfig();
331
367
  return {
368
+ inferParamName: g[this.defaultConfigKey].inferParamName,
332
369
  methodOverride: { ...g[this.defaultConfigKey].methodOverride },
333
370
  container: { ...g[this.defaultConfigKey].container }
334
371
  };
@@ -360,10 +397,10 @@ var CoreRouter = class {
360
397
  return {
361
398
  config: this.getDefaultConfig(),
362
399
  groupContext: new AsyncLocalStorage(),
363
- routes: [],
364
- routesByPathMethod: {},
365
- routesByMethod: {},
366
- routesByName: {},
400
+ routes: /* @__PURE__ */ new Set([]),
401
+ routesByPathMethod: /* @__PURE__ */ new Map(),
402
+ routesByMethod: /* @__PURE__ */ new Map(),
403
+ routesByName: /* @__PURE__ */ new Map(),
367
404
  prefix: "",
368
405
  groupMiddlewares: [],
369
406
  globalMiddlewares: []
@@ -437,17 +474,6 @@ var CoreRouter = class {
437
474
  }
438
475
  };
439
476
  }
440
- static config = {
441
- methodOverride: {
442
- enabled: true,
443
- bodyKeys: ["_method"],
444
- headerKeys: ["x-http-method"]
445
- },
446
- container: {
447
- enabled: false,
448
- autoDiscover: false
449
- }
450
- };
451
477
  static configureDefaults(options) {
452
478
  const g = globalThis;
453
479
  const defaults = this.mergeConfig(g[this.defaultConfigKey] || this.createBaseConfig(), options);
@@ -508,15 +534,6 @@ var CoreRouter = class {
508
534
  if (!pending.length) return;
509
535
  await Promise.all(pending);
510
536
  }
511
- static groupContext = new AsyncLocalStorage();
512
- static pluginRequestContext = new AsyncLocalStorage();
513
- static routes = [];
514
- static routesByPathMethod = {};
515
- static routesByMethod = {};
516
- static routesByName = {};
517
- static prefix = "";
518
- static groupMiddlewares = [];
519
- static globalMiddlewares = [];
520
537
  static getCurrentPluginRequestContext() {
521
538
  return this.pluginRequestContext.getStore();
522
539
  }
@@ -562,10 +579,10 @@ var CoreRouter = class {
562
579
  headerKeys: ["x-http-method"]
563
580
  } };
564
581
  if (!this.groupContext) this.groupContext = new AsyncLocalStorage();
565
- if (!Array.isArray(this.routes)) this.routes = [];
566
- if (!this.routesByPathMethod || typeof this.routesByPathMethod !== "object") this.routesByPathMethod = {};
567
- if (!this.routesByMethod || typeof this.routesByMethod !== "object") this.routesByMethod = {};
568
- 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();
569
586
  if (typeof this.prefix !== "string") this.prefix = "";
570
587
  if (!Array.isArray(this.groupMiddlewares)) this.groupMiddlewares = [];
571
588
  if (!Array.isArray(this.globalMiddlewares)) this.globalMiddlewares = [];
@@ -636,13 +653,15 @@ var CoreRouter = class {
636
653
  if (typeof container.enabled === "boolean") this.config.container.enabled = container.enabled;
637
654
  if (typeof container.autoDiscover === "boolean") this.config.container.autoDiscover = container.autoDiscover;
638
655
  }
656
+ if (options?.inferParamName) this.config.inferParamName = options?.inferParamName;
639
657
  const override = options?.methodOverride;
640
- if (!override) return;
641
- if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
642
- const bodyKeys = override.bodyKeys;
643
- if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
644
- const headerKeys = override.headerKeys;
645
- 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
+ }
646
665
  }
647
666
  static resolveMethodOverride(method, headers, body) {
648
667
  this.ensureState();
@@ -704,16 +723,16 @@ var CoreRouter = class {
704
723
  registrationPaths,
705
724
  parameters,
706
725
  onName: (name, route, previousName) => {
707
- if (previousName && this.routesByName[previousName] === route) delete this.routesByName[previousName];
708
- this.routesByName[name] = route;
726
+ if (previousName && this.routesByName.get(previousName) === route) this.routesByName.delete(previousName);
727
+ this.routesByName.set(name, route);
709
728
  }
710
729
  });
711
- if (!methods.includes("options") && !this.routesByPathMethod[`OPTIONS ${fullPath}`]) this.options(path, this.createDefaultOptionsHandler());
712
- this.routes.push(route);
730
+ if (!methods.includes("options") && !this.routesByPathMethod.get(`OPTIONS ${fullPath}`)) this.options(path, this.createDefaultOptionsHandler());
731
+ this.routes.add(route);
713
732
  for (const method of methods.map((m) => m.toUpperCase())) {
714
- this.routesByPathMethod[`${method} ${fullPath}`] = route;
715
- if (!this.routesByMethod[method]) this.routesByMethod[method] = [];
716
- 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);
717
736
  }
718
737
  return route;
719
738
  }
@@ -726,6 +745,11 @@ var CoreRouter = class {
726
745
  * @param options
727
746
  */
728
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
+ }
729
753
  const actions = {
730
754
  index: {
731
755
  method: "get",
@@ -733,7 +757,7 @@ var CoreRouter = class {
733
757
  },
734
758
  show: {
735
759
  method: "get",
736
- path: "/:id"
760
+ path: `/:${paramName}`
737
761
  },
738
762
  create: {
739
763
  method: "post",
@@ -741,11 +765,11 @@ var CoreRouter = class {
741
765
  },
742
766
  update: {
743
767
  method: "put",
744
- path: "/:id"
768
+ path: `/:${paramName}`
745
769
  },
746
770
  destroy: {
747
771
  method: "delete",
748
- path: "/:id"
772
+ path: `/:${paramName}`
749
773
  }
750
774
  };
751
775
  const only = options?.only || Object.keys(actions);
@@ -756,7 +780,8 @@ var CoreRouter = class {
756
780
  if (typeof preController[action] === "function") {
757
781
  const { method, path } = actions[action];
758
782
  const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
759
- 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());
760
785
  }
761
786
  }
762
787
  }
@@ -875,14 +900,14 @@ var CoreRouter = class {
875
900
  }
876
901
  static allRoutes(type) {
877
902
  this.ensureState();
878
- if (type === "method") return this.routesByMethod;
879
- if (type === "path") return this.routesByPathMethod;
880
- if (type === "name") return this.routesByName;
881
- 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");
882
907
  }
883
908
  static route(name) {
884
909
  this.ensureState();
885
- return this.routesByName[name];
910
+ return this.routesByName.get(name);
886
911
  }
887
912
  static url(name, params) {
888
913
  return this.route(name)?.toPath(params);
@@ -903,6 +928,14 @@ var CoreRouter = class {
903
928
  static setResponseProvider(provider) {
904
929
  this.responseProvider = provider;
905
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
+ }
906
939
  static initializeInstance(provider, args) {
907
940
  const isRequest = [
908
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-BJYHBKMY.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-B90hsOKM.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-BfYAFCqD.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-CbrVYD5L.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,8 +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 = [];
99
+ /**
100
+ * Resets the router to it's default state
101
+ */
102
+ static reset() {
103
+ this.routes.clear();
104
+ this.prefix = "";
105
+ this.groupMiddlewares = [];
106
+ this.globalMiddlewares = [];
107
+ this.routesByPathMethod.clear();
108
+ this.routesByMethod.clear();
109
+ this.routesByName.clear();
110
+ return this;
111
+ }
77
112
  static createBaseConfig() {
78
113
  return {
114
+ inferParamName: false,
79
115
  methodOverride: {
80
116
  enabled: true,
81
117
  bodyKeys: ["_method"],
@@ -103,6 +139,7 @@ var CoreRouter = class {
103
139
  const g = globalThis;
104
140
  if (!g[this.defaultConfigKey]) g[this.defaultConfigKey] = this.createBaseConfig();
105
141
  return {
142
+ inferParamName: g[this.defaultConfigKey].inferParamName,
106
143
  methodOverride: { ...g[this.defaultConfigKey].methodOverride },
107
144
  container: { ...g[this.defaultConfigKey].container }
108
145
  };
@@ -134,10 +171,10 @@ var CoreRouter = class {
134
171
  return {
135
172
  config: this.getDefaultConfig(),
136
173
  groupContext: new AsyncLocalStorage(),
137
- routes: [],
138
- routesByPathMethod: {},
139
- routesByMethod: {},
140
- routesByName: {},
174
+ routes: /* @__PURE__ */ new Set([]),
175
+ routesByPathMethod: /* @__PURE__ */ new Map(),
176
+ routesByMethod: /* @__PURE__ */ new Map(),
177
+ routesByName: /* @__PURE__ */ new Map(),
141
178
  prefix: "",
142
179
  groupMiddlewares: [],
143
180
  globalMiddlewares: []
@@ -211,17 +248,6 @@ var CoreRouter = class {
211
248
  }
212
249
  };
213
250
  }
214
- static config = {
215
- methodOverride: {
216
- enabled: true,
217
- bodyKeys: ["_method"],
218
- headerKeys: ["x-http-method"]
219
- },
220
- container: {
221
- enabled: false,
222
- autoDiscover: false
223
- }
224
- };
225
251
  static configureDefaults(options) {
226
252
  const g = globalThis;
227
253
  const defaults = this.mergeConfig(g[this.defaultConfigKey] || this.createBaseConfig(), options);
@@ -282,15 +308,6 @@ var CoreRouter = class {
282
308
  if (!pending.length) return;
283
309
  await Promise.all(pending);
284
310
  }
285
- static groupContext = new AsyncLocalStorage();
286
- static pluginRequestContext = new AsyncLocalStorage();
287
- static routes = [];
288
- static routesByPathMethod = {};
289
- static routesByMethod = {};
290
- static routesByName = {};
291
- static prefix = "";
292
- static groupMiddlewares = [];
293
- static globalMiddlewares = [];
294
311
  static getCurrentPluginRequestContext() {
295
312
  return this.pluginRequestContext.getStore();
296
313
  }
@@ -336,10 +353,10 @@ var CoreRouter = class {
336
353
  headerKeys: ["x-http-method"]
337
354
  } };
338
355
  if (!this.groupContext) this.groupContext = new AsyncLocalStorage();
339
- if (!Array.isArray(this.routes)) this.routes = [];
340
- if (!this.routesByPathMethod || typeof this.routesByPathMethod !== "object") this.routesByPathMethod = {};
341
- if (!this.routesByMethod || typeof this.routesByMethod !== "object") this.routesByMethod = {};
342
- 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();
343
360
  if (typeof this.prefix !== "string") this.prefix = "";
344
361
  if (!Array.isArray(this.groupMiddlewares)) this.groupMiddlewares = [];
345
362
  if (!Array.isArray(this.globalMiddlewares)) this.globalMiddlewares = [];
@@ -410,13 +427,15 @@ var CoreRouter = class {
410
427
  if (typeof container.enabled === "boolean") this.config.container.enabled = container.enabled;
411
428
  if (typeof container.autoDiscover === "boolean") this.config.container.autoDiscover = container.autoDiscover;
412
429
  }
430
+ if (options?.inferParamName) this.config.inferParamName = options?.inferParamName;
413
431
  const override = options?.methodOverride;
414
- if (!override) return;
415
- if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
416
- const bodyKeys = override.bodyKeys;
417
- if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
418
- const headerKeys = override.headerKeys;
419
- 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
+ }
420
439
  }
421
440
  static resolveMethodOverride(method, headers, body) {
422
441
  this.ensureState();
@@ -478,16 +497,16 @@ var CoreRouter = class {
478
497
  registrationPaths,
479
498
  parameters,
480
499
  onName: (name, route, previousName) => {
481
- if (previousName && this.routesByName[previousName] === route) delete this.routesByName[previousName];
482
- this.routesByName[name] = route;
500
+ if (previousName && this.routesByName.get(previousName) === route) this.routesByName.delete(previousName);
501
+ this.routesByName.set(name, route);
483
502
  }
484
503
  });
485
- if (!methods.includes("options") && !this.routesByPathMethod[`OPTIONS ${fullPath}`]) this.options(path, this.createDefaultOptionsHandler());
486
- this.routes.push(route);
504
+ if (!methods.includes("options") && !this.routesByPathMethod.get(`OPTIONS ${fullPath}`)) this.options(path, this.createDefaultOptionsHandler());
505
+ this.routes.add(route);
487
506
  for (const method of methods.map((m) => m.toUpperCase())) {
488
- this.routesByPathMethod[`${method} ${fullPath}`] = route;
489
- if (!this.routesByMethod[method]) this.routesByMethod[method] = [];
490
- 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);
491
510
  }
492
511
  return route;
493
512
  }
@@ -500,6 +519,11 @@ var CoreRouter = class {
500
519
  * @param options
501
520
  */
502
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
+ }
503
527
  const actions = {
504
528
  index: {
505
529
  method: "get",
@@ -507,7 +531,7 @@ var CoreRouter = class {
507
531
  },
508
532
  show: {
509
533
  method: "get",
510
- path: "/:id"
534
+ path: `/:${paramName}`
511
535
  },
512
536
  create: {
513
537
  method: "post",
@@ -515,11 +539,11 @@ var CoreRouter = class {
515
539
  },
516
540
  update: {
517
541
  method: "put",
518
- path: "/:id"
542
+ path: `/:${paramName}`
519
543
  },
520
544
  destroy: {
521
545
  method: "delete",
522
- path: "/:id"
546
+ path: `/:${paramName}`
523
547
  }
524
548
  };
525
549
  const only = options?.only || Object.keys(actions);
@@ -530,7 +554,8 @@ var CoreRouter = class {
530
554
  if (typeof preController[action] === "function") {
531
555
  const { method, path } = actions[action];
532
556
  const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
533
- 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());
534
559
  }
535
560
  }
536
561
  }
@@ -649,14 +674,14 @@ var CoreRouter = class {
649
674
  }
650
675
  static allRoutes(type) {
651
676
  this.ensureState();
652
- if (type === "method") return this.routesByMethod;
653
- if (type === "path") return this.routesByPathMethod;
654
- if (type === "name") return this.routesByName;
655
- 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");
656
681
  }
657
682
  static route(name) {
658
683
  this.ensureState();
659
- return this.routesByName[name];
684
+ return this.routesByName.get(name);
660
685
  }
661
686
  static url(name, params) {
662
687
  return this.route(name)?.toPath(params);
@@ -677,6 +702,14 @@ var CoreRouter = class {
677
702
  static setResponseProvider(provider) {
678
703
  this.responseProvider = provider;
679
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
+ }
680
713
  static initializeInstance(provider, args) {
681
714
  const isRequest = [
682
715
  "CoreRequest",