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.
@@ -49,6 +49,11 @@ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
49
49
  type RequestData = Record<string, any>;
50
50
  type ApiResourceMiddleware<M = any> = M | M[] | { [K in ControllerAction]?: M | M[] };
51
51
  interface RouterConfig {
52
+ /**
53
+ * When enabled, API param name will be infered from the route path.
54
+ * So instead of getting /api/users/:id, we will now get /api/users/:user
55
+ */
56
+ inferParamName?: boolean;
52
57
  /**
53
58
  * Configuration for method override functionality, allowing clients to use a
54
59
  * specific header or body parameter to override the HTTP method.
@@ -326,6 +331,23 @@ declare abstract class CoreRouter {
326
331
  private static readonly pluginArgumentResolversKey;
327
332
  private static requestProvider?;
328
333
  private static responseProvider?;
334
+ static config: RouterConfig;
335
+ protected static groupContext: AsyncLocalStorage<{
336
+ prefix: string;
337
+ groupMiddlewares: any[];
338
+ }>;
339
+ protected static pluginRequestContext: AsyncLocalStorage<ClearRouterPluginRequestContext>;
340
+ static routes: Set<Route<any, any, any>>;
341
+ static routesByPathMethod: Map<string, Route<any, any, any>>;
342
+ static routesByMethod: Map<"GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD", Route<any, any, any>[]>;
343
+ static routesByName: Map<string, Route<any, any, any>>;
344
+ static prefix: string;
345
+ static groupMiddlewares: any[];
346
+ static globalMiddlewares: any[];
347
+ /**
348
+ * Resets the router to it's default state
349
+ */
350
+ static reset(): typeof CoreRouter;
329
351
  protected static createBaseConfig(): RouterConfig;
330
352
  protected static mergeConfig(target: RouterConfig, source?: RouterConfig): RouterConfig;
331
353
  protected static getDefaultConfig(): RouterConfig;
@@ -340,17 +362,16 @@ declare abstract class CoreRouter {
340
362
  prefix: string;
341
363
  groupMiddlewares: any[];
342
364
  }>;
343
- routes: Array<Route<any, any, any>>;
344
- routesByPathMethod: Record<string, Route<any, any, any>>;
345
- routesByMethod: { [method in Uppercase<HttpMethod>]?: Array<Route<any, any, any>> };
346
- routesByName: Record<string, Route<any, any, any>>;
365
+ routes: Set<never>;
366
+ routesByPathMethod: Map<any, any>;
367
+ routesByMethod: Map<any, any>;
368
+ routesByName: Map<any, any>;
347
369
  prefix: string;
348
370
  groupMiddlewares: any[];
349
371
  globalMiddlewares: any[];
350
372
  };
351
373
  protected static bindStateAccessors(): void;
352
374
  protected static createDefaultOptionsHandler(): any;
353
- static config: RouterConfig;
354
375
  static configureDefaults(options?: RouterConfig): void;
355
376
  /**
356
377
  * Use a registered plugin
@@ -362,18 +383,6 @@ declare abstract class CoreRouter {
362
383
  */
363
384
  static use<Options = any>(plugin: ClearRouterPluginInput<Options>, options?: Options): Promise<void>;
364
385
  protected static pluginsReady(): Promise<void>;
365
- protected static groupContext: AsyncLocalStorage<{
366
- prefix: string;
367
- groupMiddlewares: any[];
368
- }>;
369
- protected static pluginRequestContext: AsyncLocalStorage<ClearRouterPluginRequestContext>;
370
- static routes: Array<Route<any, any, any>>;
371
- static routesByPathMethod: Record<string, Route<any, any, any>>;
372
- static routesByMethod: { [method in Uppercase<HttpMethod>]?: Array<Route<any, any, any>> };
373
- static routesByName: Record<string, Route<any, any, any>>;
374
- static prefix: string;
375
- static groupMiddlewares: any[];
376
- static globalMiddlewares: any[];
377
386
  protected static getCurrentPluginRequestContext(): ClearRouterPluginRequestContext | undefined;
378
387
  protected static createPluginRequestContext(ctx: any): ClearRouterPluginRequestContext;
379
388
  protected static createPluginBind(): PluginBind;
@@ -538,6 +547,7 @@ declare abstract class CoreRouter {
538
547
  * @param provider
539
548
  */
540
549
  static setResponseProvider(provider: typeof Response$2): void;
550
+ private static hasPackageInstalled;
541
551
  /**
542
552
  * Provide a class that will overide the base Response instance
543
553
  *
@@ -49,6 +49,11 @@ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
49
49
  type RequestData = Record<string, any>;
50
50
  type ApiResourceMiddleware<M = any> = M | M[] | { [K in ControllerAction]?: M | M[] };
51
51
  interface RouterConfig {
52
+ /**
53
+ * When enabled, API param name will be infered from the route path.
54
+ * So instead of getting /api/users/:id, we will now get /api/users/:user
55
+ */
56
+ inferParamName?: boolean;
52
57
  /**
53
58
  * Configuration for method override functionality, allowing clients to use a
54
59
  * specific header or body parameter to override the HTTP method.
@@ -326,6 +331,23 @@ declare abstract class CoreRouter {
326
331
  private static readonly pluginArgumentResolversKey;
327
332
  private static requestProvider?;
328
333
  private static responseProvider?;
334
+ static config: RouterConfig;
335
+ protected static groupContext: AsyncLocalStorage<{
336
+ prefix: string;
337
+ groupMiddlewares: any[];
338
+ }>;
339
+ protected static pluginRequestContext: AsyncLocalStorage<ClearRouterPluginRequestContext>;
340
+ static routes: Set<Route<any, any, any>>;
341
+ static routesByPathMethod: Map<string, Route<any, any, any>>;
342
+ static routesByMethod: Map<"GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD", Route<any, any, any>[]>;
343
+ static routesByName: Map<string, Route<any, any, any>>;
344
+ static prefix: string;
345
+ static groupMiddlewares: any[];
346
+ static globalMiddlewares: any[];
347
+ /**
348
+ * Resets the router to it's default state
349
+ */
350
+ static reset(): typeof CoreRouter;
329
351
  protected static createBaseConfig(): RouterConfig;
330
352
  protected static mergeConfig(target: RouterConfig, source?: RouterConfig): RouterConfig;
331
353
  protected static getDefaultConfig(): RouterConfig;
@@ -340,17 +362,16 @@ declare abstract class CoreRouter {
340
362
  prefix: string;
341
363
  groupMiddlewares: any[];
342
364
  }>;
343
- routes: Array<Route<any, any, any>>;
344
- routesByPathMethod: Record<string, Route<any, any, any>>;
345
- routesByMethod: { [method in Uppercase<HttpMethod>]?: Array<Route<any, any, any>> };
346
- routesByName: Record<string, Route<any, any, any>>;
365
+ routes: Set<never>;
366
+ routesByPathMethod: Map<any, any>;
367
+ routesByMethod: Map<any, any>;
368
+ routesByName: Map<any, any>;
347
369
  prefix: string;
348
370
  groupMiddlewares: any[];
349
371
  globalMiddlewares: any[];
350
372
  };
351
373
  protected static bindStateAccessors(): void;
352
374
  protected static createDefaultOptionsHandler(): any;
353
- static config: RouterConfig;
354
375
  static configureDefaults(options?: RouterConfig): void;
355
376
  /**
356
377
  * Use a registered plugin
@@ -362,18 +383,6 @@ declare abstract class CoreRouter {
362
383
  */
363
384
  static use<Options = any>(plugin: ClearRouterPluginInput<Options>, options?: Options): Promise<void>;
364
385
  protected static pluginsReady(): Promise<void>;
365
- protected static groupContext: AsyncLocalStorage<{
366
- prefix: string;
367
- groupMiddlewares: any[];
368
- }>;
369
- protected static pluginRequestContext: AsyncLocalStorage<ClearRouterPluginRequestContext>;
370
- static routes: Array<Route<any, any, any>>;
371
- static routesByPathMethod: Record<string, Route<any, any, any>>;
372
- static routesByMethod: { [method in Uppercase<HttpMethod>]?: Array<Route<any, any, any>> };
373
- static routesByName: Record<string, Route<any, any, any>>;
374
- static prefix: string;
375
- static groupMiddlewares: any[];
376
- static globalMiddlewares: any[];
377
386
  protected static getCurrentPluginRequestContext(): ClearRouterPluginRequestContext | undefined;
378
387
  protected static createPluginRequestContext(ctx: any): ClearRouterPluginRequestContext;
379
388
  protected static createPluginBind(): PluginBind;
@@ -538,6 +547,7 @@ declare abstract class CoreRouter {
538
547
  * @param provider
539
548
  */
540
549
  static setResponseProvider(provider: typeof Response$2): void;
550
+ private static hasPackageInstalled;
541
551
  /**
542
552
  * Provide a class that will overide the base Response instance
543
553
  *
@@ -1,5 +1,6 @@
1
1
  const require_bindings = require('./bindings-CLsZjOEy.cjs');
2
2
  let node_async_hooks = require("node:async_hooks");
3
+ let node_module = require("node:module");
3
4
 
4
5
  //#region src/Route.ts
5
6
  var Route = class {
@@ -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 node_async_hooks.AsyncLocalStorage();
91
+ static pluginRequestContext = new node_async_hooks.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 node_async_hooks.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 node_async_hooks.AsyncLocalStorage();
286
- static pluginRequestContext = new node_async_hooks.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 node_async_hooks.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 } = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("@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
+ (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href).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",
@@ -17,6 +17,11 @@ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
17
17
  type RequestData = Record<string, any>;
18
18
  type ApiResourceMiddleware<M = any> = M | M[] | { [K in ControllerAction]?: M | M[] };
19
19
  interface RouterConfig {
20
+ /**
21
+ * When enabled, API param name will be infered from the route path.
22
+ * So instead of getting /api/users/:id, we will now get /api/users/:user
23
+ */
24
+ inferParamName?: boolean;
20
25
  /**
21
26
  * Configuration for method override functionality, allowing clients to use a
22
27
  * specific header or body parameter to override the HTTP method.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clear-router",
3
- "version": "2.6.1",
3
+ "version": "2.6.3",
4
4
  "description": "Laravel-style routing for Node.js with support for Express, H3, Fastify, Hono, and Koa, including CommonJS, ESM, and TypeScript support.",
5
5
  "keywords": [
6
6
  "h3",
@@ -88,6 +88,7 @@
88
88
  "h3": "^2.0.1-rc.16",
89
89
  "hono": "^4.0.0",
90
90
  "koa": "^3.0.0",
91
+ "@h3ravel/support": "^0.15.11",
91
92
  "reflect-metadata": "^0.2.2"
92
93
  },
93
94
  "peerDependenciesMeta": {
@@ -109,6 +110,9 @@
109
110
  "koa": {
110
111
  "optional": true
111
112
  },
113
+ "@h3ravel/support": {
114
+ "optional": true
115
+ },
112
116
  "reflect-metadata": {
113
117
  "optional": true
114
118
  }