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