clear-router 2.1.11 → 2.2.0

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/README.md CHANGED
@@ -60,15 +60,15 @@ yarn add clear-router express
60
60
 
61
61
  ### Express JS
62
62
 
63
- See the [Express JS documentation](./docs/EXPRESS.md) for details.
63
+ See the [Express JS documentation](https://arkstack-hq.github.io/clear-router/express) for details.
64
64
 
65
65
  ### H3
66
66
 
67
- See the [H3 documentation](./docs/H3.md) for details.
67
+ See the [H3 documentation](https://arkstack-hq.github.io/clear-router/h3) for details.
68
68
 
69
69
  ## API Reference
70
70
 
71
- See [API.md](./docs/API.md) for complete API documentation.
71
+ See [API.md](https://arkstack-hq.github.io/clear-router/api) for complete API documentation.
72
72
 
73
73
  ## Middleware Execution Order
74
74
 
@@ -92,7 +92,7 @@ npm run test:esm # Test ESM
92
92
  npm run test:ts # Test TypeScript
93
93
  ```
94
94
 
95
- See [TESTING.md](./docs/TESTING.md) for a detailed testing guide.
95
+ See [TESTING.md](https://arkstack-hq.github.io/clear-router/TESTING.md) for a detailed testing guide.
96
96
 
97
97
  ## Examples
98
98
 
@@ -0,0 +1,4 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_router = require('../router-BNVIrTi3.cjs');
3
+
4
+ exports.CoreRouter = require_router.CoreRouter;
@@ -0,0 +1,2 @@
1
+ import { t as CoreRouter } from "../router-CZIh1ZPJ.cjs";
2
+ export { CoreRouter };
@@ -0,0 +1,2 @@
1
+ import { t as CoreRouter } from "../router-C1jVRytA.mjs";
2
+ export { CoreRouter };
@@ -0,0 +1,3 @@
1
+ import { t as CoreRouter } from "../router-BiCuy5TZ.mjs";
2
+
3
+ export { CoreRouter };
@@ -1,292 +1,141 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_Route = require('../Route-DhC4kNPX.cjs');
3
- let node_async_hooks = require("node:async_hooks");
2
+ const require_router = require('../router-BNVIrTi3.cjs');
4
3
 
5
4
  //#region src/express/router.ts
6
5
  /**
7
- * @class clear-router
6
+ * @class clear-router Express Router
8
7
  * @description Laravel-style routing system for Express.js and H3 with support for CommonJS, ESM, and TypeScript
9
8
  * @author Refkinscallv
10
9
  * @author 3m1n3nc3
11
10
  * @repository https://github.com/toneflix/clear-router
12
11
  */
13
- var Router = class Router {
14
- static config = { methodOverride: {
15
- enabled: true,
16
- bodyKeys: ["_method"],
17
- headerKeys: ["x-http-method"]
18
- } };
19
- static groupContext = new node_async_hooks.AsyncLocalStorage();
20
- /**
21
- * All registered routes
22
- */
23
- static routes = [];
24
- /**
25
- * Mapping of routes by path and method for quick lookup.
26
- */
27
- static routesByPathMethod = {};
28
- /**
29
- * Mapping of routes by method for quick lookup.
30
- */
31
- static routesByMethod = {};
32
- /**
33
- * Current route prefix
34
- */
35
- static prefix = "";
36
- /**
37
- * Group-level middlewares
38
- */
39
- static groupMiddlewares = [];
40
- /**
41
- * Global-level middlewares
42
- */
43
- static globalMiddlewares = [];
44
- /**
45
- * Normalize path by removing duplicate slashes and ensuring leading slash
46
- * @param path - Path to normalize
47
- * @returns Normalized path
48
- */
49
- static normalizePath(path) {
50
- return "/" + path.split("/").filter(Boolean).join("/");
12
+ var Router = class Router extends require_router.CoreRouter {
13
+ static ensureRequestBodyAccessor(req) {
14
+ if (typeof req.getBody !== "function") req.getBody = () => req.body ?? {};
51
15
  }
52
16
  /**
53
- * Configure router settings to modify behavior.
17
+ * Adds a new route to the router with the specified HTTP methods, path, handler, and optional middlewares.
54
18
  *
55
- * @param options - Configuration options for the router
56
- * @returns
57
- */
58
- static configure(options) {
59
- if (!this.config.methodOverride) this.config.methodOverride = {
60
- enabled: true,
61
- bodyKeys: ["_method"],
62
- headerKeys: ["x-http-method"]
63
- };
64
- const override = options?.methodOverride;
65
- if (!override) return;
66
- if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
67
- const bodyKeys = override.bodyKeys;
68
- if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
69
- const headerKeys = override.headerKeys;
70
- if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
71
- }
72
- static resolveMethodOverride(method, headers, body) {
73
- if (!this.config.methodOverride?.enabled || method.toLowerCase() !== "post") return null;
74
- let override;
75
- for (const key of this.config.methodOverride?.headerKeys || []) {
76
- const value = headers?.[key];
77
- if (Array.isArray(value) ? value[0] : value) {
78
- override = Array.isArray(value) ? value[0] : value;
79
- break;
80
- }
81
- }
82
- if (!override && body && typeof body === "object") for (const key of this.config.methodOverride?.bodyKeys || []) {
83
- const value = body[key];
84
- if (typeof value !== "undefined" && value !== null && value !== "") {
85
- override = value;
86
- break;
87
- }
88
- }
89
- const normalized = String(override || "").trim().toLowerCase();
90
- if (!normalized) return null;
91
- if ([
92
- "put",
93
- "patch",
94
- "delete",
95
- "post"
96
- ].includes(normalized)) return normalized;
97
- return null;
98
- }
99
- /**
100
- * Add a route with specified HTTP methods, path, handler, and middlewares
101
- * @param methods - HTTP method(s) for the route
102
- * @param path - Route path
103
- * @param handler - Route handler function or controller reference
104
- * @param middlewares - Array of middleware functions
19
+ * @param methods
20
+ * @param path
21
+ * @param handler
22
+ * @param middlewares
105
23
  */
106
24
  static add(methods, path, handler, middlewares) {
107
- const context = this.groupContext.getStore();
108
- const activePrefix = context?.prefix ?? this.prefix;
109
- const activeGroupMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
110
- methods = Array.isArray(methods) ? methods : [methods];
111
- middlewares = middlewares ? Array.isArray(middlewares) ? middlewares : [middlewares] : void 0;
112
- const fullPath = this.normalizePath(`${activePrefix}/${path}`);
113
- const route = new require_Route.Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, [
114
- ...this.globalMiddlewares,
115
- ...activeGroupMiddlewares,
116
- ...middlewares || []
117
- ]);
118
- if (!methods.includes("options") && !this.routesByPathMethod[`OPTIONS ${fullPath}`]) this.options(path, ({ res }) => {
119
- res.set("Allow", "GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD");
120
- res.sendStatus(204);
121
- });
122
- this.routes.push(route);
123
- for (const method of methods.map((m) => m.toUpperCase())) {
124
- this.routesByPathMethod[`${method.toUpperCase()} ${fullPath}`] = route;
125
- if (!this.routesByMethod[method]) this.routesByMethod[method] = [];
126
- this.routesByMethod[method].push(route);
127
- }
25
+ super.add(methods, path, handler, middlewares);
128
26
  }
129
27
  /**
130
- * Register RESTful API resource routes for a controller with optional action filtering
28
+ * Adds a new API resource route to the router for the specified base path and controller, with
29
+ * optional configuration for included/excluded actions and middlewares.
131
30
  *
132
- * @param basePath - Base path for the resource
133
- * @param controller - Controller object containing action methods
134
- * @param options - Optional filtering options for actions
31
+ * @param basePath
32
+ * @param controller
33
+ * @param options
135
34
  */
136
35
  static apiResource(basePath, controller, options) {
137
- const actions = {
138
- index: {
139
- method: "get",
140
- path: "/"
141
- },
142
- show: {
143
- method: "get",
144
- path: "/:id"
145
- },
146
- create: {
147
- method: "post",
148
- path: "/"
149
- },
150
- update: {
151
- method: "put",
152
- path: "/:id"
153
- },
154
- destroy: {
155
- method: "delete",
156
- path: "/:id"
157
- }
158
- };
159
- const only = options?.only || Object.keys(actions);
160
- const except = options?.except || [];
161
- const preController = typeof controller === "function" ? new controller() : controller;
162
- for (const action of only) {
163
- if (except.includes(action)) continue;
164
- if (typeof preController[action] === "function") {
165
- const { method, path } = actions[action];
166
- const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
167
- this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0);
168
- }
169
- }
36
+ super.apiResource(basePath, controller, options);
170
37
  }
171
38
  /**
172
- * Register a GET route
173
- * @param path - Route path
174
- * @param handler - Route handler
175
- * @param middlewares - Middleware functions
39
+ * Adds a new GET route to the router with the specified path, handler, and optional middlewares.
40
+ *
41
+ * @param path
42
+ * @param handler
43
+ * @param middlewares
176
44
  */
177
45
  static get(path, handler, middlewares) {
178
- this.add("get", path, handler, middlewares);
46
+ super.get(path, handler, middlewares);
179
47
  }
180
48
  /**
181
- * Register a POST route
182
- * @param path - Route path
183
- * @param handler - Route handler
184
- * @param middlewares - Middleware functions
49
+ * Adds a new POST route to the router with the specified path, handler, and optional middlewares.
50
+ *
51
+ * @param path
52
+ * @param handler
53
+ * @param middlewares
185
54
  */
186
55
  static post(path, handler, middlewares) {
187
- this.add("post", path, handler, middlewares);
56
+ super.post(path, handler, middlewares);
188
57
  }
189
58
  /**
190
- * Register a PUT route
191
- * @param path - Route path
192
- * @param handler - Route handler
193
- * @param middlewares - Middleware functions
59
+ * Adds a new PUT route to the router with the specified path, handler, and optional middlewares.
60
+ *
61
+ * @param path
62
+ * @param handler
63
+ * @param middlewares
194
64
  */
195
65
  static put(path, handler, middlewares) {
196
- this.add("put", path, handler, middlewares);
66
+ super.put(path, handler, middlewares);
197
67
  }
198
68
  /**
199
- * Register a DELETE route
200
- * @param path - Route path
201
- * @param handler - Route handler
202
- * @param middlewares - Middleware functions
69
+ * Adds a new DELETE route to the router with the specified path, handler, and optional middlewares.
70
+ *
71
+ * @param path
72
+ * @param handler
73
+ * @param middlewares
203
74
  */
204
75
  static delete(path, handler, middlewares) {
205
- this.add("delete", path, handler, middlewares);
76
+ super.delete(path, handler, middlewares);
206
77
  }
207
78
  /**
208
- * Register a PATCH route
209
- * @param path - Route path
210
- * @param handler - Route handler
211
- * @param middlewares - Middleware functions
79
+ * Adds a new PATCH route to the router with the specified path, handler, and optional middlewares.
80
+ *
81
+ * @param path
82
+ * @param handler
83
+ * @param middlewares
212
84
  */
213
85
  static patch(path, handler, middlewares) {
214
- this.add("patch", path, handler, middlewares);
86
+ super.patch(path, handler, middlewares);
215
87
  }
216
88
  /**
217
- * Register an OPTIONS route
218
- * @param path - Route path
219
- * @param handler - Route handler
220
- * @param middlewares - Middleware functions
89
+ * Adds a new OPTIONS route to the router with the specified path, handler, and optional middlewares.
90
+ *
91
+ * @param path
92
+ * @param handler
93
+ * @param middlewares
221
94
  */
222
95
  static options(path, handler, middlewares) {
223
- this.add("options", path, handler, middlewares);
96
+ super.options(path, handler, middlewares);
224
97
  }
225
98
  /**
226
- * Register a HEAD route
227
- * @param path - Route path
228
- * @param handler - Route handler
229
- * @param middlewares - Middleware functions
99
+ * Adds a new HEAD route to the router with the specified path, handler, and optional middlewares.
100
+ *
101
+ * @param path
102
+ * @param handler
103
+ * @param middlewares
230
104
  */
231
105
  static head(path, handler, middlewares) {
232
- this.add("head", path, handler, middlewares);
106
+ super.head(path, handler, middlewares);
233
107
  }
234
108
  /**
235
- * Group routes with a common prefix and middlewares
236
- * @param prefix - URL prefix for grouped routes
237
- * @param callback - Function containing route definitions
238
- * @param middlewares - Middleware functions applied to all routes in group
109
+ * Defines a group of routes with a common prefix and optional middlewares, allowing for better
110
+ * organization and reuse of route configurations.
111
+ *
112
+ * @param prefix
113
+ * @param callback
114
+ * @param middlewares
239
115
  */
240
116
  static async group(prefix, callback, middlewares) {
241
- const context = this.groupContext.getStore();
242
- const previousPrefix = context?.prefix ?? this.prefix;
243
- const previousMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
244
- const fullPrefix = [previousPrefix, prefix].filter(Boolean).join("/");
245
- const nextContext = {
246
- prefix: this.normalizePath(fullPrefix),
247
- groupMiddlewares: [...previousMiddlewares, ...middlewares || []]
248
- };
249
- await this.groupContext.run(nextContext, async () => {
250
- await Promise.resolve(callback());
251
- });
117
+ await super.group(prefix, callback, middlewares);
252
118
  }
253
119
  /**
254
- * Apply global middlewares for the duration of the callback
255
- * @param middlewares - Middleware functions
256
- * @param callback - Function containing route definitions
120
+ * Adds global middlewares to the router, which will be applied to all routes.
121
+ *
122
+ * @param middlewares
123
+ * @param callback
257
124
  */
258
125
  static middleware(middlewares, callback) {
259
- const prevMiddlewares = this.globalMiddlewares;
260
- this.globalMiddlewares = [...prevMiddlewares, ...middlewares || []];
261
- callback();
262
- this.globalMiddlewares = prevMiddlewares;
126
+ super.middleware(middlewares, callback);
263
127
  }
264
128
  static allRoutes(type) {
265
- if (type === "method") return this.routesByMethod;
266
- if (type === "path") return this.routesByPathMethod;
267
- return this.routes.filter((e) => e.methods.length > 1 || e.methods[0] !== "options");
129
+ return super.allRoutes(type);
268
130
  }
269
131
  static async apply(router) {
270
132
  for (const route of this.routes) {
271
133
  let handlerFunction = null;
272
134
  let instance = null;
273
135
  try {
274
- if (typeof route.handler === "function")
275
- /**
276
- * Since we do not have a controller instance, we will call the handler function directly and the route instance will be the this argument. This allows for both controller-based and function-based handlers to work seamlessly.
277
- */
278
- handlerFunction = route.handler.bind(route);
279
- else if (Array.isArray(route.handler) && route.handler.length === 2) {
280
- const [Controller, method] = route.handler;
281
- if (["function", "object"].includes(typeof Controller) && typeof Controller[method] === "function") {
282
- instance = Controller;
283
- handlerFunction = Controller[method].bind(Controller);
284
- } else if (typeof Controller === "function") {
285
- instance = new Controller();
286
- if (typeof instance[method] === "function") handlerFunction = instance[method].bind(instance);
287
- else throw new Error(`Method "${method}" not found in controller instance "${Controller.name}"`);
288
- } else throw new Error(`Invalid controller type for route: ${route.path}`);
289
- } else throw new Error(`Invalid handler format for route: ${route.path}`);
136
+ const resolved = this.resolveHandler(route);
137
+ handlerFunction = resolved.handlerFunction;
138
+ instance = resolved.instance;
290
139
  } catch (error) {
291
140
  console.error(`[ROUTES] Error setting up route ${route.path}:`, error.message);
292
141
  throw error;
@@ -308,19 +157,25 @@ var Router = class Router {
308
157
  console.error("[ROUTES]", error.message);
309
158
  throw error;
310
159
  }
311
- router[method](route.path, (req, res, next) => {
160
+ router[method](route.path, (req, _res, next) => {
161
+ Router.ensureRequestBodyAccessor(req);
312
162
  const override = Router.resolveMethodOverride(req.method, req.headers, req.body);
313
163
  if (method === "post" && override && override !== "post") return next("route");
314
164
  return next();
315
165
  }, ...route.middlewares || [], async (req, res, next) => {
316
166
  try {
167
+ Router.ensureRequestBodyAccessor(req);
317
168
  const ctx = {
318
169
  req,
319
170
  res,
320
171
  next
321
172
  };
322
173
  const inst = instance ?? route;
323
- await Router.bindRequestToInstance(ctx, inst, route);
174
+ Router.bindRequestToInstance(ctx, inst, route, {
175
+ body: ctx.req.getBody(),
176
+ query: ctx.req.query,
177
+ params: ctx.req.params
178
+ });
324
179
  const result = handlerFunction(ctx, inst.clearRequest);
325
180
  await Promise.resolve(result);
326
181
  } catch (error) {
@@ -331,19 +186,25 @@ var Router = class Router {
331
186
  "put",
332
187
  "patch",
333
188
  "delete"
334
- ].includes(method)) router.post(route.path, (req, res, next) => {
189
+ ].includes(method)) router.post(route.path, (req, _res, next) => {
190
+ Router.ensureRequestBodyAccessor(req);
335
191
  if (Router.resolveMethodOverride(req.method, req.headers, req.body) !== method) return next("route");
336
192
  req.method = method.toUpperCase();
337
193
  return next();
338
194
  }, ...route.middlewares || [], async (req, res, next) => {
339
195
  try {
196
+ Router.ensureRequestBodyAccessor(req);
340
197
  const ctx = {
341
198
  req,
342
199
  res,
343
200
  next
344
201
  };
345
202
  const inst = instance ?? route;
346
- await Router.bindRequestToInstance(ctx, inst, route);
203
+ Router.bindRequestToInstance(ctx, inst, route, {
204
+ body: ctx.req.getBody(),
205
+ query: ctx.req.query,
206
+ params: ctx.req.params
207
+ });
347
208
  const result = handlerFunction(ctx, inst.clearRequest);
348
209
  await Promise.resolve(result);
349
210
  } catch (error) {
@@ -353,20 +214,6 @@ var Router = class Router {
353
214
  }
354
215
  }
355
216
  }
356
- static async bindRequestToInstance(ctx, instance, route) {
357
- if (!instance) return;
358
- instance.ctx = ctx;
359
- instance.body = ctx.req.body;
360
- instance.query = ctx.req.query;
361
- instance.params = ctx.req.params;
362
- instance.clearRequest = new require_Route.ClearRequest({
363
- ctx,
364
- route,
365
- body: instance.body,
366
- query: instance.query,
367
- params: instance.params
368
- });
369
- }
370
217
  };
371
218
 
372
219
  //#endregion