clear-router 2.1.12 → 2.3.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.
Files changed (43) hide show
  1. package/README.md +4 -4
  2. package/dist/core/index.cjs +4 -0
  3. package/dist/core/index.d.cts +2 -0
  4. package/dist/core/index.d.mts +2 -0
  5. package/dist/core/index.mjs +3 -0
  6. package/dist/express/index.cjs +86 -247
  7. package/dist/express/index.d.cts +68 -97
  8. package/dist/express/index.d.mts +68 -97
  9. package/dist/express/index.mjs +86 -247
  10. package/dist/fastify/index.cjs +213 -0
  11. package/dist/fastify/index.d.cts +137 -0
  12. package/dist/fastify/index.d.mts +137 -0
  13. package/dist/fastify/index.mjs +212 -0
  14. package/dist/h3/index.cjs +88 -242
  15. package/dist/h3/index.d.cts +72 -93
  16. package/dist/h3/index.d.mts +72 -93
  17. package/dist/h3/index.mjs +88 -242
  18. package/dist/hono/index.cjs +227 -0
  19. package/dist/hono/index.d.cts +141 -0
  20. package/dist/hono/index.d.mts +141 -0
  21. package/dist/hono/index.mjs +226 -0
  22. package/dist/index.cjs +357 -0
  23. package/dist/index.d.cts +195 -40
  24. package/dist/index.d.mts +195 -40
  25. package/dist/index.mjs +358 -1
  26. package/dist/router-Ba2MVNn-.cjs +412 -0
  27. package/dist/router-Bug2IE_u.mjs +407 -0
  28. package/dist/router-DLmimm_U.d.cts +320 -0
  29. package/dist/router-cWYmcfTX.d.mts +320 -0
  30. package/dist/types/ClearRequest.d.mts +1 -1
  31. package/dist/types/Route.d.mts +5 -5
  32. package/dist/types/basic.d.mts +1 -4
  33. package/dist/types/express.d.mts +1 -1
  34. package/dist/types/fastify.d.mts +19 -0
  35. package/dist/types/fastify.mjs +1 -0
  36. package/dist/types/h3.d.mts +1 -1
  37. package/dist/types/hono.d.mts +18 -0
  38. package/dist/types/hono.mjs +1 -0
  39. package/package.json +26 -2
  40. package/dist/Route-BbPXcDGX.mjs +0 -50
  41. package/dist/Route-DhC4kNPX.cjs +0 -62
  42. package/dist/basic-C_1O6RVq.d.cts +0 -138
  43. package/dist/basic-cLeny2Zk.d.mts +0 -138
package/dist/index.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
1
3
  //#region src/ClearRequest.ts
2
4
  var ClearRequest = class {
3
5
  /**
@@ -57,4 +59,359 @@ var Route = class {
57
59
  };
58
60
 
59
61
  //#endregion
60
- export { ClearRequest, Controller, Route };
62
+ //#region src/core/router.ts
63
+ /**
64
+ * @class clear-router CoreRouter
65
+ * @description Core routing logic for clear-router, shared between all supported adapters (Express.js, H3, etc.)
66
+ * @author 3m1n3nc3
67
+ * @repository https://github.com/toneflix/clear-router
68
+ */
69
+ var CoreRouter = class {
70
+ static createDefaultOptionsHandler() {
71
+ return (ctx) => {
72
+ const allow = "GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD";
73
+ if (ctx?.header && ctx?.status && ctx?.body) {
74
+ ctx.header("Allow", allow);
75
+ ctx.status(204);
76
+ return ctx.body(null);
77
+ }
78
+ if (ctx?.res?.headers?.set) {
79
+ ctx.res.headers.set("Allow", allow);
80
+ ctx.res.status = 204;
81
+ return;
82
+ }
83
+ if (ctx?.res?.set) {
84
+ ctx.res.set("Allow", allow);
85
+ ctx.res.sendStatus(204);
86
+ return;
87
+ }
88
+ if (ctx?.reply?.header) {
89
+ ctx.reply.header("Allow", allow);
90
+ ctx.reply.code(204).send();
91
+ return;
92
+ }
93
+ };
94
+ }
95
+ static config = { methodOverride: {
96
+ enabled: true,
97
+ bodyKeys: ["_method"],
98
+ headerKeys: ["x-http-method"]
99
+ } };
100
+ static groupContext = new AsyncLocalStorage();
101
+ static routes = [];
102
+ static routesByPathMethod = {};
103
+ static routesByMethod = {};
104
+ static prefix = "";
105
+ static groupMiddlewares = [];
106
+ static globalMiddlewares = [];
107
+ static ensureState() {
108
+ if (!Object.prototype.hasOwnProperty.call(this, "config")) this.config = { methodOverride: {
109
+ enabled: true,
110
+ bodyKeys: ["_method"],
111
+ headerKeys: ["x-http-method"]
112
+ } };
113
+ if (!Object.prototype.hasOwnProperty.call(this, "groupContext")) this.groupContext = new AsyncLocalStorage();
114
+ if (!Object.prototype.hasOwnProperty.call(this, "routes")) this.routes = [];
115
+ if (!Object.prototype.hasOwnProperty.call(this, "routesByPathMethod")) this.routesByPathMethod = {};
116
+ if (!Object.prototype.hasOwnProperty.call(this, "routesByMethod")) this.routesByMethod = {};
117
+ if (!Object.prototype.hasOwnProperty.call(this, "prefix")) this.prefix = "";
118
+ if (!Object.prototype.hasOwnProperty.call(this, "groupMiddlewares")) this.groupMiddlewares = [];
119
+ if (!Object.prototype.hasOwnProperty.call(this, "globalMiddlewares")) this.globalMiddlewares = [];
120
+ }
121
+ /**
122
+ * Normalizes a path by ensuring it starts with a single slash and does not have trailing
123
+ * slashes, while preserving dynamic segments and parameters.
124
+ *
125
+ * @param path The path to normalize.
126
+ * @returns The normalized path.
127
+ */
128
+ static normalizePath(path) {
129
+ return "/" + path.split("/").filter(Boolean).join("/");
130
+ }
131
+ /**
132
+ * Configures the router with the given options, such as method override settings.
133
+ *
134
+ * @param this
135
+ * @param options
136
+ * @returns
137
+ */
138
+ static configure(options) {
139
+ this.ensureState();
140
+ if (!this.config.methodOverride) this.config.methodOverride = {
141
+ enabled: true,
142
+ bodyKeys: ["_method"],
143
+ headerKeys: ["x-http-method"]
144
+ };
145
+ const override = options?.methodOverride;
146
+ if (!override) return;
147
+ if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
148
+ const bodyKeys = override.bodyKeys;
149
+ if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
150
+ const headerKeys = override.headerKeys;
151
+ if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
152
+ }
153
+ static resolveMethodOverride(method, headers, body) {
154
+ this.ensureState();
155
+ if (!this.config.methodOverride?.enabled || method.toLowerCase() !== "post") return null;
156
+ let override;
157
+ const headerValueFor = (key) => {
158
+ if (typeof headers.get === "function") return headers.get(key);
159
+ const value = headers?.[key];
160
+ return Array.isArray(value) ? value[0] : value;
161
+ };
162
+ for (const key of this.config.methodOverride?.headerKeys || []) {
163
+ const value = headerValueFor(key);
164
+ if (value) {
165
+ override = value;
166
+ break;
167
+ }
168
+ }
169
+ if (!override && body && typeof body === "object") for (const key of this.config.methodOverride?.bodyKeys || []) {
170
+ const value = body[key];
171
+ if (typeof value !== "undefined" && value !== null && value !== "") {
172
+ override = value;
173
+ break;
174
+ }
175
+ }
176
+ const normalized = String(override || "").trim().toLowerCase();
177
+ if (!normalized) return null;
178
+ if ([
179
+ "put",
180
+ "patch",
181
+ "delete",
182
+ "post"
183
+ ].includes(normalized)) return normalized;
184
+ return null;
185
+ }
186
+ /**
187
+ * Adds a new route to the router with the specified methods, path, handler, and middlewares.
188
+ *
189
+ * @param this
190
+ * @param methods
191
+ * @param path
192
+ * @param handler
193
+ * @param middlewares
194
+ */
195
+ static add(methods, path, handler, middlewares) {
196
+ this.ensureState();
197
+ const context = this.groupContext.getStore();
198
+ const activePrefix = context?.prefix ?? this.prefix;
199
+ const activeGroupMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
200
+ methods = Array.isArray(methods) ? methods : [methods];
201
+ middlewares = middlewares ? Array.isArray(middlewares) ? middlewares : [middlewares] : void 0;
202
+ const fullPath = this.normalizePath(`${activePrefix}/${path}`);
203
+ const route = new Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, [
204
+ ...this.globalMiddlewares,
205
+ ...activeGroupMiddlewares,
206
+ ...middlewares || []
207
+ ]);
208
+ if (!methods.includes("options") && !this.routesByPathMethod[`OPTIONS ${fullPath}`]) this.options(path, this.createDefaultOptionsHandler());
209
+ this.routes.push(route);
210
+ for (const method of methods.map((m) => m.toUpperCase())) {
211
+ this.routesByPathMethod[`${method} ${fullPath}`] = route;
212
+ if (!this.routesByMethod[method]) this.routesByMethod[method] = [];
213
+ this.routesByMethod[method].push(route);
214
+ }
215
+ }
216
+ /**
217
+ * Adds a new API resource route to the router for the specified base path and controller, with
218
+ * options to include/exclude specific actions and apply middlewares.
219
+ *
220
+ * @param this
221
+ * @param basePath
222
+ * @param controller
223
+ * @param options
224
+ */
225
+ static apiResource(basePath, controller, options) {
226
+ const actions = {
227
+ index: {
228
+ method: "get",
229
+ path: "/"
230
+ },
231
+ show: {
232
+ method: "get",
233
+ path: "/:id"
234
+ },
235
+ create: {
236
+ method: "post",
237
+ path: "/"
238
+ },
239
+ update: {
240
+ method: "put",
241
+ path: "/:id"
242
+ },
243
+ destroy: {
244
+ method: "delete",
245
+ path: "/:id"
246
+ }
247
+ };
248
+ const only = options?.only || Object.keys(actions);
249
+ const except = options?.except || [];
250
+ const preController = typeof controller === "function" ? new controller() : controller;
251
+ for (const action of only) {
252
+ if (except.includes(action)) continue;
253
+ if (typeof preController[action] === "function") {
254
+ const { method, path } = actions[action];
255
+ const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
256
+ this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0);
257
+ }
258
+ }
259
+ }
260
+ /**
261
+ * Adds a new GET route to the router with the specified path, handler, and optional middlewares.
262
+ *
263
+ * @param this The router instance.
264
+ * @param path The path for the GET route.
265
+ * @param handler The handler function for the GET route.
266
+ * @param middlewares Optional middlewares to apply to the GET route.
267
+ */
268
+ static get(path, handler, middlewares) {
269
+ this.add("get", path, handler, middlewares);
270
+ }
271
+ /**
272
+ * Adds a new POST route to the router with the specified path, handler, and optional middlewares.
273
+ *
274
+ * @param this
275
+ * @param path
276
+ * @param handler
277
+ * @param middlewares
278
+ */
279
+ static post(path, handler, middlewares) {
280
+ this.add("post", path, handler, middlewares);
281
+ }
282
+ /**
283
+ * Adds a new PUT route to the router with the specified path, handler, and optional middlewares.
284
+ *
285
+ * @param this
286
+ * @param path
287
+ * @param handler
288
+ * @param middlewares
289
+ */
290
+ static put(path, handler, middlewares) {
291
+ this.add("put", path, handler, middlewares);
292
+ }
293
+ /**
294
+ * Adds a new DELETE route to the router with the specified path, handler, and optional middlewares.
295
+ *
296
+ * @param this
297
+ * @param path
298
+ * @param handler
299
+ * @param middlewares
300
+ */
301
+ static delete(path, handler, middlewares) {
302
+ this.add("delete", path, handler, middlewares);
303
+ }
304
+ /**
305
+ * Adds a new PATCH route to the router with the specified path, handler, and optional middlewares.
306
+ *
307
+ * @param this
308
+ * @param path
309
+ * @param handler
310
+ * @param middlewares
311
+ */
312
+ static patch(path, handler, middlewares) {
313
+ this.add("patch", path, handler, middlewares);
314
+ }
315
+ /**
316
+ * Adds a new OPTIONS route to the router with the specified path, handler, and optional middlewares.
317
+ *
318
+ * @param this
319
+ * @param path
320
+ * @param handler
321
+ * @param middlewares
322
+ */
323
+ static options(path, handler, middlewares) {
324
+ this.add("options", path, handler, middlewares);
325
+ }
326
+ /**
327
+ * Adds a new HEAD route to the router with the specified path, handler, and optional middlewares.
328
+ *
329
+ * @param this
330
+ * @param path
331
+ * @param handler
332
+ * @param middlewares
333
+ */
334
+ static head(path, handler, middlewares) {
335
+ this.add("head", path, handler, middlewares);
336
+ }
337
+ /**
338
+ * Defines a group of routes with a common prefix and optional middlewares, allowing for better
339
+ * organization and reuse of route configurations.
340
+ *
341
+ * @param this
342
+ * @param prefix
343
+ * @param callback
344
+ * @param middlewares
345
+ */
346
+ static async group(prefix, callback, middlewares) {
347
+ this.ensureState();
348
+ const context = this.groupContext.getStore();
349
+ const previousPrefix = context?.prefix ?? this.prefix;
350
+ const previousMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
351
+ const fullPrefix = [previousPrefix, prefix].filter(Boolean).join("/");
352
+ const nextContext = {
353
+ prefix: this.normalizePath(fullPrefix),
354
+ groupMiddlewares: [...previousMiddlewares, ...middlewares || []]
355
+ };
356
+ await this.groupContext.run(nextContext, async () => {
357
+ await Promise.resolve(callback());
358
+ });
359
+ }
360
+ /**
361
+ * Adds global middlewares to the router, which will be applied to all routes.
362
+ *
363
+ * @param this
364
+ * @param middlewares
365
+ * @param callback
366
+ */
367
+ static middleware(middlewares, callback) {
368
+ this.ensureState();
369
+ const prevMiddlewares = this.globalMiddlewares;
370
+ this.globalMiddlewares = [...prevMiddlewares, ...middlewares || []];
371
+ callback();
372
+ this.globalMiddlewares = prevMiddlewares;
373
+ }
374
+ static allRoutes(type) {
375
+ this.ensureState();
376
+ if (type === "method") return this.routesByMethod;
377
+ if (type === "path") return this.routesByPathMethod;
378
+ return this.routes.filter((e) => e.methods.length > 1 || e.methods[0] !== "options");
379
+ }
380
+ static resolveHandler(route) {
381
+ let handlerFunction;
382
+ let instance = null;
383
+ if (typeof route.handler === "function") handlerFunction = route.handler.bind(route);
384
+ else if (Array.isArray(route.handler) && route.handler.length === 2) {
385
+ const [ControllerType, method] = route.handler;
386
+ if (["function", "object"].includes(typeof ControllerType) && typeof ControllerType[method] === "function") {
387
+ instance = ControllerType;
388
+ handlerFunction = ControllerType[method].bind(ControllerType);
389
+ } else if (typeof ControllerType === "function") {
390
+ instance = new ControllerType();
391
+ if (typeof instance[method] === "function") handlerFunction = instance[method].bind(instance);
392
+ else throw new Error(`Method "${method}" not found in controller instance "${ControllerType.name}"`);
393
+ } else throw new Error(`Invalid controller type for route: ${route.path}`);
394
+ } else throw new Error(`Invalid handler format for route: ${route.path}`);
395
+ return {
396
+ handlerFunction,
397
+ instance
398
+ };
399
+ }
400
+ static bindRequestToInstance(ctx, instance, route, payload) {
401
+ if (!instance) return;
402
+ instance.ctx = ctx;
403
+ instance.body = payload.body;
404
+ instance.query = payload.query;
405
+ instance.params = payload.params;
406
+ instance.clearRequest = new ClearRequest({
407
+ ctx,
408
+ route,
409
+ body: instance.body,
410
+ query: instance.query,
411
+ params: instance.params
412
+ });
413
+ }
414
+ };
415
+
416
+ //#endregion
417
+ export { ClearRequest, Controller, CoreRouter, Route };