tezx 1.0.21 → 1.0.23

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/router.js ADDED
@@ -0,0 +1,373 @@
1
+ import { GlobalConfig } from "./config/config";
2
+ import MiddlewareConfigure, { TriMiddleware, } from "./MiddlewareConfigure";
3
+ import { getFiles } from "./utils/staticFile";
4
+ import { sanitizePathSplit } from "./utils/url";
5
+ class TrieRouter {
6
+ children = new Map();
7
+ handlers = new Map();
8
+ pathname;
9
+ paramName;
10
+ isParam = false;
11
+ constructor(pathname = "/") {
12
+ this.children = new Map();
13
+ this.pathname = pathname;
14
+ }
15
+ }
16
+ export class Router extends MiddlewareConfigure {
17
+ routers = new Map();
18
+ env = {};
19
+ triRouter;
20
+ constructor({ basePath = "/", env = {} } = {}) {
21
+ super(basePath);
22
+ this.basePath = basePath;
23
+ this.env = { ...env };
24
+ this.triRouter = new TrieRouter(basePath);
25
+ this.get.bind(this);
26
+ this.post.bind(this);
27
+ this.put.bind(this);
28
+ this.delete.bind(this);
29
+ this.all.bind(this);
30
+ this.#routeAddTriNode.bind(this);
31
+ this.addRouter.bind(this);
32
+ this.group.bind(this);
33
+ }
34
+ static(...args) {
35
+ let route = "";
36
+ let dir;
37
+ let options = {};
38
+ switch (args.length) {
39
+ case 3:
40
+ [route, dir, options] = args;
41
+ break;
42
+ case 2:
43
+ if (typeof args[1] === "object") {
44
+ [dir, options] = args;
45
+ }
46
+ else {
47
+ [route, dir] = args;
48
+ }
49
+ break;
50
+ case 1:
51
+ [dir] = args;
52
+ break;
53
+ default:
54
+ throw new Error(`\x1b[1;31m404 Not Found\x1b[0m \x1b[1;32mInvalid arguments\x1b[0m`);
55
+ }
56
+ getFiles(dir, route, this, options);
57
+ return this;
58
+ }
59
+ get(path, ...args) {
60
+ this.#registerRoute("GET", path, ...args);
61
+ return this;
62
+ }
63
+ post(path, ...args) {
64
+ this.#registerRoute("POST", path, ...args);
65
+ return this;
66
+ }
67
+ put(path, ...args) {
68
+ this.#registerRoute("PUT", path, ...args);
69
+ return this;
70
+ }
71
+ patch(path, ...args) {
72
+ this.#registerRoute("PATCH", path, ...args);
73
+ return this;
74
+ }
75
+ delete(path, ...args) {
76
+ this.#registerRoute("DELETE", path, ...args);
77
+ return this;
78
+ }
79
+ options(path, ...args) {
80
+ this.#registerRoute("OPTIONS", path, ...args);
81
+ return this;
82
+ }
83
+ head(path, ...args) {
84
+ this.#registerRoute("HEAD", path, ...args);
85
+ return this;
86
+ }
87
+ all(path, ...args) {
88
+ this.#registerRoute("ALL", path, ...args);
89
+ return this;
90
+ }
91
+ addRoute(method, path, ...args) {
92
+ this.#registerRoute(method, path, ...args);
93
+ return this;
94
+ }
95
+ addRouter(path, router) {
96
+ return this.#routeAddTriNode(path, router);
97
+ }
98
+ group(prefix, callback) {
99
+ const router = new Router({
100
+ basePath: prefix,
101
+ });
102
+ callback(router);
103
+ this.#routeAddTriNode("/", router);
104
+ return this;
105
+ }
106
+ use(...args) {
107
+ let path = "/";
108
+ let middlewares = [];
109
+ let router;
110
+ if (typeof args[0] === "string") {
111
+ path = args[0];
112
+ if (Array.isArray(args[1])) {
113
+ middlewares = args[1];
114
+ router = args[2];
115
+ }
116
+ else if (typeof args[1] === "function") {
117
+ middlewares = [args[1]];
118
+ router = args[2];
119
+ }
120
+ else {
121
+ router = args[1];
122
+ }
123
+ }
124
+ else if (typeof args[0] === "function") {
125
+ if (args.length === 1) {
126
+ middlewares = [args[0]];
127
+ }
128
+ else {
129
+ middlewares = [args[0]];
130
+ router = args[1];
131
+ }
132
+ }
133
+ else if (Array.isArray(args[0])) {
134
+ middlewares = args[0];
135
+ router = args[1];
136
+ }
137
+ else if (args[0] instanceof Router) {
138
+ router = args[0];
139
+ }
140
+ this.#addRouteMiddleware(path, middlewares);
141
+ if (router && router instanceof Router) {
142
+ this.addRouter(path, router);
143
+ }
144
+ return this;
145
+ }
146
+ #registerRoute(method, path, ...args) {
147
+ if (args.length === 0) {
148
+ throw new Error("At least one handler is required.");
149
+ }
150
+ let middlewares = [];
151
+ let callback;
152
+ if (args.length > 1) {
153
+ if (Array.isArray(args[0])) {
154
+ middlewares = args[0];
155
+ }
156
+ else if (typeof args[0] === "function") {
157
+ middlewares = [args[0]];
158
+ }
159
+ callback = args[args.length - 1];
160
+ }
161
+ else {
162
+ callback = args[0];
163
+ }
164
+ if (typeof callback !== "function") {
165
+ throw new Error("Route callback function is missing or invalid.");
166
+ }
167
+ if (!middlewares.every((middleware) => typeof middleware === "function")) {
168
+ throw new Error("Middleware must be a function or an array of functions.");
169
+ }
170
+ this.#addRoute(method, path, callback, middlewares);
171
+ }
172
+ #addRoute(method, path, callback, middlewares) {
173
+ const parts = sanitizePathSplit(this.basePath, path);
174
+ let finalMiddleware = middlewares;
175
+ if (!GlobalConfig.allowDuplicateMw) {
176
+ finalMiddleware = new Set(middlewares);
177
+ }
178
+ let p = parts.join("/");
179
+ if (/(\/\*|\?)/.test(p)) {
180
+ let handler = this.routers.get(p);
181
+ if (!handler) {
182
+ handler = new Map();
183
+ handler.set(method, {
184
+ callback: callback,
185
+ middlewares: finalMiddleware,
186
+ });
187
+ return this.routers.set(p, handler);
188
+ }
189
+ if (!GlobalConfig.overwriteMethod && handler.has(method))
190
+ return;
191
+ return handler.set(method, { callback, middlewares: finalMiddleware });
192
+ }
193
+ let node = this.triRouter;
194
+ for (const part of parts) {
195
+ if (part.startsWith(":")) {
196
+ if (!node.children.has(":")) {
197
+ node.children.set(":", new TrieRouter());
198
+ }
199
+ node = node.children.get(":");
200
+ node.isParam = true;
201
+ if (!node.paramName) {
202
+ node.paramName = part.slice(1);
203
+ }
204
+ }
205
+ else {
206
+ if (!node.children.has(part)) {
207
+ node.children.set(part, new TrieRouter());
208
+ }
209
+ node = node.children.get(part);
210
+ }
211
+ }
212
+ if (!GlobalConfig.overwriteMethod && node.handlers.has(method))
213
+ return;
214
+ node.handlers.set(method, {
215
+ callback: callback,
216
+ middlewares: finalMiddleware,
217
+ });
218
+ node.pathname = path;
219
+ }
220
+ #addRouteMiddleware(path, middlewareFunctions) {
221
+ this.addMiddleware(path, middlewareFunctions);
222
+ }
223
+ #routeAddTriNode(path, router) {
224
+ this.env = { ...this.env, ...router.env };
225
+ if (!(router instanceof Router)) {
226
+ throw new Error("Router instance is required.");
227
+ }
228
+ const parts = sanitizePathSplit(this.basePath, path);
229
+ if (router.routers.size) {
230
+ for (const [segment, handlers] of router.routers) {
231
+ let path = parts.length ? parts.join("/") + "/" + segment : segment;
232
+ if (this.routers.has(path)) {
233
+ const baseRouter = this.routers.get(path);
234
+ for (const [method, handler] of handlers) {
235
+ if (!GlobalConfig.overwriteMethod && baseRouter.has(method))
236
+ return;
237
+ baseRouter.set(method, handler);
238
+ }
239
+ }
240
+ else {
241
+ this.routers.set(path, new Map(handlers));
242
+ }
243
+ }
244
+ }
245
+ let rootNode = this.triRouter;
246
+ let rootMiddlewares = this.triMiddlewares;
247
+ if (parts.length == 0) {
248
+ this.#addMiddlewareHandlerIDsTriNode(rootNode, rootMiddlewares, router);
249
+ }
250
+ else {
251
+ for (const part of parts) {
252
+ if (part.startsWith(":")) {
253
+ if (!rootNode.children.has(":")) {
254
+ rootNode.children.set(":", new TrieRouter());
255
+ }
256
+ rootNode = rootNode.children.get(":");
257
+ rootNode.isParam = true;
258
+ if (!rootNode.paramName) {
259
+ rootNode.paramName = part.slice(1);
260
+ }
261
+ }
262
+ else {
263
+ if (!rootNode.children.has(part)) {
264
+ rootNode.children.set(part, new TrieRouter());
265
+ }
266
+ rootNode = rootNode.children.get(part);
267
+ }
268
+ }
269
+ for (const part of parts) {
270
+ if (part.startsWith("*")) {
271
+ if (!rootMiddlewares.children.has("*")) {
272
+ rootMiddlewares.children.set("*", new TriMiddleware());
273
+ }
274
+ rootMiddlewares = rootMiddlewares.children.get("*");
275
+ }
276
+ else if (part.startsWith(":")) {
277
+ const isOptional = part?.endsWith("?");
278
+ if (isOptional) {
279
+ rootMiddlewares.isOptional = isOptional;
280
+ continue;
281
+ }
282
+ if (!rootMiddlewares.children.has(":")) {
283
+ rootMiddlewares.children.set(":", new TriMiddleware());
284
+ }
285
+ rootMiddlewares = rootMiddlewares.children.get(":");
286
+ }
287
+ else {
288
+ if (!rootMiddlewares.children.has(part)) {
289
+ rootMiddlewares.children.set(part, new TriMiddleware());
290
+ }
291
+ rootMiddlewares = rootMiddlewares.children.get(part);
292
+ }
293
+ }
294
+ this.#addMiddlewareHandlerIDsTriNode(rootNode, rootMiddlewares, router);
295
+ }
296
+ }
297
+ #addMiddlewareHandlerIDsTriNode(rootNode, rootMiddlewares, router) {
298
+ function addSubRouter(children, node) {
299
+ let rtN = node;
300
+ for (const element of children) {
301
+ const pathSegment = element[0];
302
+ const subRouter = element[1];
303
+ if (rtN.children.has(pathSegment)) {
304
+ let findNode = rtN.children.get(pathSegment);
305
+ for (const [method, handlers] of subRouter.handlers) {
306
+ if (!GlobalConfig.overwriteMethod && node.handlers.has(method))
307
+ return;
308
+ findNode.handlers.set(method, handlers);
309
+ }
310
+ if (subRouter.children.size) {
311
+ addSubRouter(subRouter.children, findNode);
312
+ }
313
+ }
314
+ else {
315
+ rtN.children.set(pathSegment, subRouter);
316
+ }
317
+ }
318
+ }
319
+ let routerNode = router.triRouter;
320
+ const routerMiddlewares = router.triMiddlewares;
321
+ for (const [method, handlers] of routerNode.handlers) {
322
+ if (!GlobalConfig.overwriteMethod) {
323
+ rootNode.handlers.set(method, handlers);
324
+ continue;
325
+ }
326
+ if (!rootNode.handlers.has(method)) {
327
+ rootNode.handlers.set(method, handlers);
328
+ }
329
+ }
330
+ if (routerNode.children.size > 0) {
331
+ addSubRouter(routerNode.children, rootNode);
332
+ }
333
+ function addMiddleware(children, node) {
334
+ let n = node;
335
+ for (const [path, middlewareNode] of children) {
336
+ if (n.children.has(path)) {
337
+ let findNode = n.children.get(path);
338
+ if (GlobalConfig.allowDuplicateMw) {
339
+ findNode.middlewares.push(...middlewareNode.middlewares);
340
+ }
341
+ else {
342
+ for (const mw of middlewareNode.middlewares) {
343
+ if (findNode.middlewares.has(mw)) {
344
+ middlewareNode.middlewares.delete(mw);
345
+ }
346
+ findNode.middlewares.add(mw);
347
+ }
348
+ }
349
+ if (middlewareNode.children.size) {
350
+ addMiddleware(middlewareNode.children, findNode);
351
+ }
352
+ }
353
+ else {
354
+ n.children.set(path, middlewareNode);
355
+ }
356
+ }
357
+ }
358
+ if (GlobalConfig.allowDuplicateMw) {
359
+ rootMiddlewares.middlewares.push(...routerMiddlewares.middlewares);
360
+ }
361
+ else {
362
+ for (const mw of routerMiddlewares.middlewares) {
363
+ if (rootMiddlewares.middlewares.has(mw)) {
364
+ routerMiddlewares.middlewares.delete(mw);
365
+ }
366
+ rootMiddlewares.middlewares.add(mw);
367
+ }
368
+ }
369
+ if (routerMiddlewares.children.size > 0) {
370
+ addMiddleware(routerMiddlewares.children, rootMiddlewares);
371
+ }
372
+ }
373
+ }
package/server.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ import { HTTPMethod } from "./request";
2
+ import { Middleware, Router, RouterConfig } from "./router";
3
+ interface ServeResponse {
4
+ status: number;
5
+ headers: {
6
+ [key: string]: string;
7
+ };
8
+ body: string;
9
+ statusText: string;
10
+ }
11
+ export type TezXConfig = {
12
+ /**
13
+ * `allowDuplicateMw` determines whether duplicate middleware functions
14
+ * are allowed in the router.
15
+ *
16
+ * - When `true`: The same middleware can be added multiple times.
17
+ * - When `false`: Ensures each middleware is registered only once
18
+ * per route or application context.
19
+ *
20
+ * @default false
21
+ */
22
+ allowDuplicateMw?: boolean;
23
+ /**
24
+ * `overwriteMethod` controls whether existing route handlers
25
+ * should be overwritten when a new handler for the same
26
+ * HTTP method and path is added.
27
+ *
28
+ * - When `true`: The new handler replaces the existing one.
29
+ * - When `false`: Prevents overwriting, ensuring that the
30
+ * first registered handler remains active.
31
+ *
32
+ * @default true
33
+ */
34
+ overwriteMethod?: boolean;
35
+ /**
36
+ * Enables or disables debugging for the middleware.
37
+ * When set to `true`, detailed debug logs will be output,
38
+ * useful for tracking the flow of requests and identifying issues.
39
+ *
40
+ * @default false
41
+ */
42
+ debugMode?: boolean;
43
+ } & RouterConfig;
44
+ export declare class TezX<T extends Record<string, any> = {}> extends Router<T> {
45
+ #private;
46
+ constructor({ basePath, env, debugMode, allowDuplicateMw, overwriteMethod, }?: TezXConfig);
47
+ protected findRoute(method: HTTPMethod, pathname: string): {
48
+ callback: any;
49
+ middlewares: Middleware<T>[];
50
+ params: Record<string, string>;
51
+ } | null;
52
+ serve(req: Request, connInfo: any): Promise<ServeResponse | any>;
53
+ }
54
+ export {};
package/server.js ADDED
@@ -0,0 +1,166 @@
1
+ import { GlobalConfig } from "./config/config";
2
+ import { Context, httpStatusMap } from "./context";
3
+ import { Router } from "./router";
4
+ import { COLORS } from "./utils/colors";
5
+ import { useParams } from "./utils/params";
6
+ export class TezX extends Router {
7
+ constructor({ basePath = "/", env = {}, debugMode = false, allowDuplicateMw = false, overwriteMethod = true, } = {}) {
8
+ GlobalConfig.allowDuplicateMw = allowDuplicateMw;
9
+ GlobalConfig.overwriteMethod = overwriteMethod;
10
+ if (debugMode) {
11
+ GlobalConfig.debugMode = debugMode;
12
+ }
13
+ super({ basePath, env });
14
+ this.serve = this.serve.bind(this);
15
+ }
16
+ #hashRouter(method, pathname) {
17
+ const routers = this.routers;
18
+ for (let pattern of this.routers.keys()) {
19
+ const { success, params } = useParams({
20
+ path: pathname,
21
+ urlPattern: pattern,
22
+ });
23
+ const handlers = routers.get(pattern)?.get(method) || routers.get(pattern)?.get("ALL");
24
+ if (success && handlers) {
25
+ return {
26
+ callback: handlers.callback,
27
+ middlewares: handlers.middlewares,
28
+ params: params,
29
+ };
30
+ }
31
+ }
32
+ return null;
33
+ }
34
+ #triRouter(method, pathname) {
35
+ const parts = pathname.split("/").filter(Boolean);
36
+ const params = {};
37
+ let node = this.triRouter;
38
+ for (let part of parts) {
39
+ if (node.children.has(part)) {
40
+ node = node.children.get(part);
41
+ }
42
+ else if (node.children.has(":")) {
43
+ node = node.children.get(":");
44
+ if (node.paramName)
45
+ params[node.paramName] = part;
46
+ }
47
+ else {
48
+ return null;
49
+ }
50
+ }
51
+ if (node?.handlers?.size && node?.pathname) {
52
+ const handlers = node.handlers.get(method) || node.handlers.get("ALL");
53
+ if (handlers) {
54
+ return {
55
+ middlewares: handlers.middlewares,
56
+ callback: handlers.callback,
57
+ params: params,
58
+ };
59
+ }
60
+ return null;
61
+ }
62
+ return null;
63
+ }
64
+ findRoute(method, pathname) {
65
+ const route = this.#triRouter(method, pathname) || this.#hashRouter(method, pathname);
66
+ if (route) {
67
+ return {
68
+ ...route,
69
+ middlewares: [...route.middlewares],
70
+ };
71
+ }
72
+ return null;
73
+ }
74
+ #createHandler(middlewares, finalCallback) {
75
+ return async (ctx) => {
76
+ let index = 0;
77
+ const next = async () => {
78
+ if (index < middlewares.length) {
79
+ return await middlewares[index++](ctx, next);
80
+ }
81
+ else {
82
+ return await finalCallback(ctx);
83
+ }
84
+ };
85
+ const response = await next();
86
+ if (!response) {
87
+ throw new Error(`Handler did not return a response or next() was not called. Path: ${ctx.pathname}, Method: ${ctx.method}`);
88
+ }
89
+ return response;
90
+ };
91
+ }
92
+ #findMiddleware(pathname) {
93
+ const parts = pathname.split("/").filter(Boolean);
94
+ let middlewares = [];
95
+ let node = this.triMiddlewares;
96
+ for (let part of parts) {
97
+ if (node.children.has(part)) {
98
+ node = node.children.get(part);
99
+ }
100
+ else if (node.children.has("*")) {
101
+ node = node.children.get("*");
102
+ }
103
+ else if (node.children.has(":")) {
104
+ node = node.children.get(":");
105
+ }
106
+ else {
107
+ break;
108
+ }
109
+ middlewares.push(...node.middlewares);
110
+ }
111
+ return middlewares;
112
+ }
113
+ async #handleRequest(req, connInfo) {
114
+ let ctx = new Context(req, connInfo);
115
+ const urlRef = ctx.req.urlRef;
116
+ const { pathname } = urlRef;
117
+ let middlewares = this.#findMiddleware(pathname);
118
+ ctx.env = this.env;
119
+ try {
120
+ let callback = async (ctx) => {
121
+ const find = this.findRoute(ctx.req.method, pathname);
122
+ if (find?.callback) {
123
+ ctx.params = find.params;
124
+ const callback = find.callback;
125
+ let middlewares = find.middlewares;
126
+ return (await this.#createHandler(middlewares, callback)(ctx));
127
+ }
128
+ else {
129
+ let res = await GlobalConfig.notFound(ctx);
130
+ ctx.setStatus = res.status;
131
+ return res;
132
+ }
133
+ };
134
+ let response = await this.#createHandler([...this.triMiddlewares.middlewares, ...middlewares], callback)(ctx);
135
+ let finalResponse = () => {
136
+ return (ctx) => {
137
+ if (response?.headers) {
138
+ ctx.headers.add(response.headers);
139
+ }
140
+ const statusText = response?.statusText || httpStatusMap[response?.status] || "";
141
+ const status = response.status || ctx.getStatus;
142
+ let headers = ctx.headers.toObject();
143
+ return new Response(response.body, {
144
+ status,
145
+ statusText,
146
+ headers,
147
+ });
148
+ };
149
+ };
150
+ return finalResponse()(ctx);
151
+ }
152
+ catch (err) {
153
+ let error = err;
154
+ if (err instanceof Error) {
155
+ error = err.stack;
156
+ }
157
+ GlobalConfig.debugging.error(`${COLORS.bgRed} ${ctx.pathname}, Method: ${ctx.method} ${COLORS.reset}`, `${httpStatusMap[500]}: ${error} `);
158
+ let res = await GlobalConfig.onError(error, ctx);
159
+ ctx.setStatus = res.status;
160
+ return res;
161
+ }
162
+ }
163
+ async serve(req, connInfo) {
164
+ return this.#handleRequest(req, connInfo);
165
+ }
166
+ }
@@ -0,0 +1,21 @@
1
+ export declare const COLORS: {
2
+ reset: string;
3
+ bold: string;
4
+ underline: string;
5
+ gray: string;
6
+ white: string;
7
+ black: string;
8
+ red: string;
9
+ green: string;
10
+ yellow: string;
11
+ blue: string;
12
+ magenta: string;
13
+ cyan: string;
14
+ bgRed: string;
15
+ bgGreen: string;
16
+ bgYellow: string;
17
+ bgBlue: string;
18
+ bgMagenta: string;
19
+ bgCyan: string;
20
+ bgWhite: string;
21
+ };
@@ -0,0 +1,21 @@
1
+ export const COLORS = {
2
+ reset: "\x1b[0m",
3
+ bold: "\x1b[1m",
4
+ underline: "\x1b[4m",
5
+ gray: "\x1b[90m",
6
+ white: "\x1b[97m",
7
+ black: "\x1b[30m",
8
+ red: "\x1b[31m",
9
+ green: "\x1b[32m",
10
+ yellow: "\x1b[33m",
11
+ blue: "\x1b[34m",
12
+ magenta: "\x1b[35m",
13
+ cyan: "\x1b[36m",
14
+ bgRed: "\x1b[41m",
15
+ bgGreen: "\x1b[42m",
16
+ bgYellow: "\x1b[43m",
17
+ bgBlue: "\x1b[44m",
18
+ bgMagenta: "\x1b[45m",
19
+ bgCyan: "\x1b[46m",
20
+ bgWhite: "\x1b[47m",
21
+ };
@@ -0,0 +1,7 @@
1
+ export type LogLevel = "info" | "warn" | "error" | "debug" | "success";
2
+ export declare const loggerOutput: (level: LogLevel, message: string, ...args: unknown[]) => void;
3
+ /**
4
+ * A universal logger function that measures and logs the processing time of an operation.
5
+ * @param label - A label to identify the operation being logged.
6
+ * @param callback - The operation to measure and execute.
7
+ */
@@ -0,0 +1,13 @@
1
+ import { COLORS } from "./colors";
2
+ export const loggerOutput = (level, message, ...args) => {
3
+ const timestamp = new Date().toISOString();
4
+ const LEVEL_COLORS = {
5
+ info: COLORS.blue,
6
+ warn: COLORS.yellow,
7
+ error: COLORS.red,
8
+ debug: COLORS.cyan,
9
+ success: COLORS.green,
10
+ };
11
+ const levelText = `${LEVEL_COLORS[level]}[${level.toUpperCase()}]${COLORS.reset}`;
12
+ console.log(` ${levelText} ${message}`, ...args?.flat());
13
+ };
@@ -0,0 +1,5 @@
1
+ import { FormDataOptions } from "../request";
2
+ export declare function parseJsonBody(req: any): Promise<Record<string, any>>;
3
+ export declare function parseTextBody(req: any): Promise<string>;
4
+ export declare function parseUrlEncodedBody(req: any): Promise<Record<string, any>>;
5
+ export declare function parseMultipartBody(req: any, boundary: string, options?: FormDataOptions): Promise<Record<string, any>>;