tezx 1.0.34 → 1.0.36
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/cjs/core/context.js +10 -3
- package/cjs/core/router.js +3 -2
- package/cjs/core/server.js +7 -3
- package/cjs/index.js +1 -1
- package/cjs/middleware/lazyLoadModules.js +2 -2
- package/cjs/middleware/pagination.js +25 -29
- package/cjs/middleware/rateLimiter.js +5 -6
- package/core/context.d.ts +12 -1
- package/core/context.js +10 -3
- package/core/router.d.ts +21 -11
- package/core/router.js +3 -2
- package/core/server.js +7 -3
- package/index.js +1 -1
- package/middleware/lazyLoadModules.d.ts +0 -1
- package/middleware/lazyLoadModules.js +2 -2
- package/middleware/pagination.d.ts +19 -5
- package/middleware/pagination.js +25 -29
- package/middleware/rateLimiter.d.ts +20 -1
- package/middleware/rateLimiter.js +5 -6
- package/package.json +1 -1
package/cjs/core/context.js
CHANGED
|
@@ -82,7 +82,7 @@ class Context {
|
|
|
82
82
|
#status = 200;
|
|
83
83
|
state = new state_1.State();
|
|
84
84
|
#params = {};
|
|
85
|
-
|
|
85
|
+
#resBody;
|
|
86
86
|
#localAddress = {};
|
|
87
87
|
#remoteAddress = {};
|
|
88
88
|
constructor(req, connInfo) {
|
|
@@ -167,8 +167,8 @@ class Context {
|
|
|
167
167
|
else if (typeof args[0] === "object") {
|
|
168
168
|
headers = args[0];
|
|
169
169
|
}
|
|
170
|
-
if (!headers["Content-Type"]) {
|
|
171
|
-
if (typeof body === "string") {
|
|
170
|
+
if ((!headers["Content-Type"] && !headers['content-type'])) {
|
|
171
|
+
if (typeof body === "string" || typeof body == 'number') {
|
|
172
172
|
headers["Content-Type"] = "text/plain;";
|
|
173
173
|
}
|
|
174
174
|
else if (typeof body === "object" && body !== null) {
|
|
@@ -388,6 +388,7 @@ class Context {
|
|
|
388
388
|
headers,
|
|
389
389
|
});
|
|
390
390
|
let clone = response.clone();
|
|
391
|
+
this.body = body;
|
|
391
392
|
this.res = response;
|
|
392
393
|
return clone;
|
|
393
394
|
}
|
|
@@ -397,6 +398,12 @@ class Context {
|
|
|
397
398
|
set params(params) {
|
|
398
399
|
this.#params = params;
|
|
399
400
|
}
|
|
401
|
+
set body(body) {
|
|
402
|
+
this.#resBody = body;
|
|
403
|
+
}
|
|
404
|
+
get body() {
|
|
405
|
+
return this.#resBody;
|
|
406
|
+
}
|
|
400
407
|
get params() {
|
|
401
408
|
return this.#params;
|
|
402
409
|
}
|
package/cjs/core/router.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Router = void 0;
|
|
4
|
-
const config_1 = require("./config");
|
|
5
|
-
const MiddlewareConfigure_1 = require("./MiddlewareConfigure");
|
|
6
4
|
const staticFile_1 = require("../utils/staticFile");
|
|
7
5
|
const url_1 = require("../utils/url");
|
|
6
|
+
const config_1 = require("./config");
|
|
7
|
+
const MiddlewareConfigure_1 = require("./MiddlewareConfigure");
|
|
8
|
+
;
|
|
8
9
|
class TrieRouter {
|
|
9
10
|
children = new Map();
|
|
10
11
|
handlers = new Map();
|
package/cjs/core/server.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TezX = void 0;
|
|
4
|
+
const colors_1 = require("../utils/colors");
|
|
4
5
|
const config_1 = require("./config");
|
|
5
6
|
const context_1 = require("./context");
|
|
6
7
|
const router_1 = require("./router");
|
|
7
|
-
const colors_1 = require("../utils/colors");
|
|
8
8
|
const params_1 = require("../utils/params");
|
|
9
9
|
class TezX extends router_1.Router {
|
|
10
10
|
constructor({ basePath = "/", env = {}, debugMode = false, allowDuplicateMw = false, overwriteMethod = true, } = {}) {
|
|
@@ -86,10 +86,14 @@ class TezX extends router_1.Router {
|
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
88
|
const response = await next();
|
|
89
|
-
if (
|
|
89
|
+
if (response instanceof Response) {
|
|
90
|
+
return response;
|
|
91
|
+
}
|
|
92
|
+
if (!response && !ctx.body) {
|
|
90
93
|
throw new Error(`Handler did not return a response or next() was not called. Path: ${ctx.pathname}, Method: ${ctx.method}`);
|
|
91
94
|
}
|
|
92
|
-
|
|
95
|
+
const resBody = response || ctx.body;
|
|
96
|
+
return ctx.send(resBody, ctx.headers.toObject());
|
|
93
97
|
};
|
|
94
98
|
}
|
|
95
99
|
#findMiddleware(pathname) {
|
package/cjs/index.js
CHANGED
|
@@ -7,4 +7,4 @@ var server_1 = require("./core/server");
|
|
|
7
7
|
Object.defineProperty(exports, "TezX", { enumerable: true, get: function () { return server_1.TezX; } });
|
|
8
8
|
var params_1 = require("./utils/params");
|
|
9
9
|
Object.defineProperty(exports, "useParams", { enumerable: true, get: function () { return params_1.useParams; } });
|
|
10
|
-
exports.version = "1.0.
|
|
10
|
+
exports.version = "1.0.36";
|
|
@@ -48,14 +48,14 @@ const lazyLoadModules = (options) => {
|
|
|
48
48
|
lifecycleHooks.onCacheSet?.(moduleName, module, ctx);
|
|
49
49
|
}
|
|
50
50
|
ctx[moduleContextKey] = module;
|
|
51
|
-
lifecycleHooks.onComplete?.(moduleName, module, ctx);
|
|
52
|
-
config_1.GlobalConfig.debugging.success(`Successfully loaded module: ${moduleName}`);
|
|
53
51
|
if (module.init && typeof module.init === "function") {
|
|
54
52
|
const initResult = await module.init(dependencies, ctx);
|
|
55
53
|
if (initResult instanceof Response) {
|
|
56
54
|
return initResult;
|
|
57
55
|
}
|
|
58
56
|
}
|
|
57
|
+
lifecycleHooks.onComplete?.(moduleName, module, ctx);
|
|
58
|
+
config_1.GlobalConfig.debugging.success(`Successfully loaded module: ${moduleName}`);
|
|
59
59
|
}
|
|
60
60
|
catch (error) {
|
|
61
61
|
config_1.GlobalConfig.debugging.error(`Error loading module: ${moduleName}`, error);
|
|
@@ -12,40 +12,36 @@ const paginationHandler = (options = {}) => {
|
|
|
12
12
|
ctx.pagination = {
|
|
13
13
|
page,
|
|
14
14
|
limit,
|
|
15
|
-
offset
|
|
15
|
+
offset,
|
|
16
16
|
queryKeyPage,
|
|
17
|
-
queryKeyLimit
|
|
17
|
+
queryKeyLimit,
|
|
18
18
|
};
|
|
19
19
|
if (getDataSource) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
catch (error) {
|
|
43
|
-
ctx.setStatus = 500;
|
|
44
|
-
ctx.body = { error: "Internal Server Error", };
|
|
45
|
-
throw new Error("Error fetching or processing data:", error?.message);
|
|
20
|
+
const dataSourceResponse = await getDataSource(ctx, { page, limit, offset });
|
|
21
|
+
const total = dataSourceResponse?.[countKey];
|
|
22
|
+
const data = dataSourceResponse?.[dataKey];
|
|
23
|
+
const pagination = {
|
|
24
|
+
page,
|
|
25
|
+
limit,
|
|
26
|
+
totalItems: total,
|
|
27
|
+
totalPages: Math.ceil(total / limit),
|
|
28
|
+
hasNextPage: page < Math.ceil(total / limit),
|
|
29
|
+
hasPrevPage: page > 1,
|
|
30
|
+
nextPage: page < Math.ceil(total / limit) ? page + 1 : null,
|
|
31
|
+
prevPage: page > 1 ? page - 1 : null,
|
|
32
|
+
};
|
|
33
|
+
ctx.pagination = pagination;
|
|
34
|
+
const body = {
|
|
35
|
+
[dataKey]: data,
|
|
36
|
+
[countKey]: total,
|
|
37
|
+
pagination,
|
|
38
|
+
};
|
|
39
|
+
if (next) {
|
|
40
|
+
ctx.body = body;
|
|
41
|
+
return await next();
|
|
46
42
|
}
|
|
43
|
+
return (ctx.body = body);
|
|
47
44
|
}
|
|
48
|
-
return await next();
|
|
49
45
|
};
|
|
50
46
|
};
|
|
51
47
|
exports.paginationHandler = paginationHandler;
|
|
@@ -2,21 +2,20 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.rateLimiter = void 0;
|
|
4
4
|
const rateLimiter = (options) => {
|
|
5
|
-
const { maxRequests, windowMs, keyGenerator = (ctx) => `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`, onError = (ctx, retryAfter, error) => {
|
|
5
|
+
const { maxRequests, windowMs, keyGenerator = (ctx) => `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`, cacheStorage = new Map(), onError = (ctx, retryAfter, error) => {
|
|
6
6
|
ctx.setStatus = 429;
|
|
7
7
|
throw new Error(`Rate limit exceeded. Try again in ${retryAfter} seconds.`);
|
|
8
8
|
}, } = options;
|
|
9
|
-
const memoryStore = new Map();
|
|
10
9
|
return async (ctx, next) => {
|
|
11
10
|
const key = keyGenerator(ctx);
|
|
12
11
|
let requestCount;
|
|
13
12
|
let resetTime;
|
|
14
|
-
for (const [key, entry] of
|
|
13
|
+
for (const [key, entry] of cacheStorage.entries()) {
|
|
15
14
|
if (Date.now() >= entry.resetTime) {
|
|
16
|
-
|
|
15
|
+
cacheStorage.delete(key);
|
|
17
16
|
}
|
|
18
17
|
}
|
|
19
|
-
const entry =
|
|
18
|
+
const entry = cacheStorage.get(key);
|
|
20
19
|
if (entry && Date.now() < entry.resetTime) {
|
|
21
20
|
requestCount = entry.count + 1;
|
|
22
21
|
resetTime = entry.resetTime;
|
|
@@ -24,7 +23,7 @@ const rateLimiter = (options) => {
|
|
|
24
23
|
else {
|
|
25
24
|
requestCount = 1;
|
|
26
25
|
resetTime = Date.now() + windowMs;
|
|
27
|
-
|
|
26
|
+
cacheStorage.set(key, { count: requestCount, resetTime });
|
|
28
27
|
}
|
|
29
28
|
if (requestCount > maxRequests) {
|
|
30
29
|
const retryAfter = Math.ceil((resetTime - Date.now()) / 1000);
|
package/core/context.d.ts
CHANGED
|
@@ -51,7 +51,6 @@ export declare class Context<T extends Record<string, any> = {}> {
|
|
|
51
51
|
* @type {State}
|
|
52
52
|
*/
|
|
53
53
|
state: State;
|
|
54
|
-
body: Record<string, any>;
|
|
55
54
|
constructor(req: any, connInfo: ConnAddress);
|
|
56
55
|
/**
|
|
57
56
|
* Cookie handling utility with get/set/delete operations
|
|
@@ -192,5 +191,17 @@ export declare class Context<T extends Record<string, any> = {}> {
|
|
|
192
191
|
*/
|
|
193
192
|
get req(): Request;
|
|
194
193
|
protected set params(params: Record<string, any>);
|
|
194
|
+
/**
|
|
195
|
+
* Sets the HTTP response body to be returned to the client.
|
|
196
|
+
* This can be a string, object, or any serializable data.
|
|
197
|
+
* @param body - The response payload to be stored internally.
|
|
198
|
+
*/
|
|
199
|
+
set body(body: any | undefined);
|
|
200
|
+
/**
|
|
201
|
+
* Retrieves the current response body set for the outgoing HTTP response.
|
|
202
|
+
* This value will be used when sending the final response.
|
|
203
|
+
* @returns The internally stored response payload.
|
|
204
|
+
*/
|
|
205
|
+
get body(): any | undefined;
|
|
195
206
|
protected get params(): Record<string, any>;
|
|
196
207
|
}
|
package/core/context.js
CHANGED
|
@@ -79,7 +79,7 @@ export class Context {
|
|
|
79
79
|
#status = 200;
|
|
80
80
|
state = new State();
|
|
81
81
|
#params = {};
|
|
82
|
-
|
|
82
|
+
#resBody;
|
|
83
83
|
#localAddress = {};
|
|
84
84
|
#remoteAddress = {};
|
|
85
85
|
constructor(req, connInfo) {
|
|
@@ -164,8 +164,8 @@ export class Context {
|
|
|
164
164
|
else if (typeof args[0] === "object") {
|
|
165
165
|
headers = args[0];
|
|
166
166
|
}
|
|
167
|
-
if (!headers["Content-Type"]) {
|
|
168
|
-
if (typeof body === "string") {
|
|
167
|
+
if ((!headers["Content-Type"] && !headers['content-type'])) {
|
|
168
|
+
if (typeof body === "string" || typeof body == 'number') {
|
|
169
169
|
headers["Content-Type"] = "text/plain;";
|
|
170
170
|
}
|
|
171
171
|
else if (typeof body === "object" && body !== null) {
|
|
@@ -385,6 +385,7 @@ export class Context {
|
|
|
385
385
|
headers,
|
|
386
386
|
});
|
|
387
387
|
let clone = response.clone();
|
|
388
|
+
this.body = body;
|
|
388
389
|
this.res = response;
|
|
389
390
|
return clone;
|
|
390
391
|
}
|
|
@@ -394,6 +395,12 @@ export class Context {
|
|
|
394
395
|
set params(params) {
|
|
395
396
|
this.#params = params;
|
|
396
397
|
}
|
|
398
|
+
set body(body) {
|
|
399
|
+
this.#resBody = body;
|
|
400
|
+
}
|
|
401
|
+
get body() {
|
|
402
|
+
return this.#resBody;
|
|
403
|
+
}
|
|
397
404
|
get params() {
|
|
398
405
|
return this.#params;
|
|
399
406
|
}
|
package/core/router.d.ts
CHANGED
|
@@ -3,8 +3,9 @@ import MiddlewareConfigure, { DuplicateMiddlewares, UniqueMiddlewares } from "./
|
|
|
3
3
|
import { HTTPMethod } from "./request";
|
|
4
4
|
export type NextCallback = () => Promise<any>;
|
|
5
5
|
export type ctx<T extends Record<string, any> = {}> = Context<T> & T;
|
|
6
|
-
export type
|
|
7
|
-
export type
|
|
6
|
+
export type CallbackReturnType = Promise<Response> | Response | string | Record<string, any>;
|
|
7
|
+
export type Callback<T extends Record<string, any> = {}> = (ctx: ctx<T>) => CallbackReturnType;
|
|
8
|
+
export type Middleware<T extends Record<string, any> = {}> = (ctx: ctx<T>, next: NextCallback) => NextCallback | Promise<NextCallback | Response> | Response | string | Record<string, any>;
|
|
8
9
|
export type RouterConfig = {
|
|
9
10
|
/**
|
|
10
11
|
* `env` allows you to define environment variables for the router.
|
|
@@ -75,64 +76,72 @@ export declare class Router<T extends Record<string, any> = {}> extends Middlewa
|
|
|
75
76
|
* app.get('/admin', [authMiddleware, adminMiddleware], (ctx) => { ... });
|
|
76
77
|
*/
|
|
77
78
|
get(path: string, callback: Callback<T>): this;
|
|
79
|
+
get(path: string, middleware: Middleware<T>): this;
|
|
80
|
+
get(path: string, middleware: Middleware<T>, callback: Callback<T>): this;
|
|
78
81
|
get(path: string, middlewares: Middleware<T>[], callback: Callback<T>): this;
|
|
79
|
-
get(path: string, middlewares: Middleware<T>, callback: Callback<T>): this;
|
|
80
82
|
/**
|
|
81
83
|
* Registers a POST route with optional middleware(s)
|
|
82
84
|
* @param path - URL path pattern
|
|
83
85
|
* @param args - Handler callback or middleware(s) + handler
|
|
84
86
|
*/
|
|
85
87
|
post(path: string, callback: Callback<T>): this;
|
|
88
|
+
post(path: string, middleware: Middleware<T>): this;
|
|
89
|
+
post(path: string, middleware: Middleware<T>, callback: Callback<T>): this;
|
|
86
90
|
post(path: string, middlewares: Middleware<T>[], callback: Callback<T>): this;
|
|
87
|
-
post(path: string, middlewares: Middleware<T>, callback: Callback<T>): this;
|
|
88
91
|
/**
|
|
89
92
|
* Registers a PUT route with optional middleware(s)
|
|
90
93
|
* @param path - URL path pattern
|
|
91
94
|
* @param args - Handler callback or middleware(s) + handler
|
|
92
95
|
*/
|
|
93
96
|
put(path: string, callback: Callback<T>): this;
|
|
97
|
+
put(path: string, middleware: Middleware<T>): this;
|
|
98
|
+
put(path: string, middleware: Middleware<T>, callback: Callback<T>): this;
|
|
94
99
|
put(path: string, middlewares: Middleware<T>[], callback: Callback<T>): this;
|
|
95
|
-
put(path: string, middlewares: Middleware<T>, callback: Callback<T>): this;
|
|
96
100
|
/**
|
|
97
101
|
* Registers a PATCH route with optional middleware(s)
|
|
98
102
|
* @param path - URL path pattern
|
|
99
103
|
* @param args - Handler callback or middleware(s) + handler
|
|
100
104
|
*/
|
|
101
105
|
patch(path: string, callback: Callback<T>): this;
|
|
106
|
+
patch(path: string, middleware: Middleware<T>): this;
|
|
107
|
+
patch(path: string, middleware: Middleware<T>, callback: Callback<T>): this;
|
|
102
108
|
patch(path: string, middlewares: Middleware<T>[], callback: Callback<T>): this;
|
|
103
|
-
patch(path: string, middlewares: Middleware<T>, callback: Callback<T>): this;
|
|
104
109
|
/**
|
|
105
110
|
* Registers a DELETE route with optional middleware(s)
|
|
106
111
|
* @param path - URL path pattern
|
|
107
112
|
* @param args - Handler callback or middleware(s) + handler
|
|
108
113
|
*/
|
|
109
114
|
delete(path: string, callback: Callback<T>): this;
|
|
115
|
+
delete(path: string, middleware: Middleware<T>): this;
|
|
116
|
+
delete(path: string, middleware: Middleware<T>, callback: Callback<T>): this;
|
|
110
117
|
delete(path: string, middlewares: Middleware<T>[], callback: Callback<T>): this;
|
|
111
|
-
delete(path: string, middlewares: Middleware<T>, callback: Callback<T>): this;
|
|
112
118
|
/**
|
|
113
119
|
* Registers an OPTIONS route (primarily for CORS preflight requests)
|
|
114
120
|
* @param path - URL path pattern
|
|
115
121
|
* @param args - Handler callback or middleware(s) + handler
|
|
116
122
|
*/
|
|
117
123
|
options(path: string, callback: Callback<T>): this;
|
|
124
|
+
options(path: string, middleware: Middleware<T>): this;
|
|
125
|
+
options(path: string, middleware: Middleware<T>, callback: Callback<T>): this;
|
|
118
126
|
options(path: string, middlewares: Middleware<T>[], callback: Callback<T>): this;
|
|
119
|
-
options(path: string, middlewares: Middleware<T>, callback: Callback<T>): this;
|
|
120
127
|
/**
|
|
121
128
|
* Registers a HEAD route (returns headers only)
|
|
122
129
|
* @param path - URL path pattern
|
|
123
130
|
* @param args - Handler callback or middleware(s) + handler
|
|
124
131
|
*/
|
|
125
132
|
head(path: string, callback: Callback<T>): this;
|
|
133
|
+
head(path: string, middleware: Middleware<T>): this;
|
|
134
|
+
head(path: string, middleware: Middleware<T>, callback: Callback<T>): this;
|
|
126
135
|
head(path: string, middlewares: Middleware<T>[], callback: Callback<T>): this;
|
|
127
|
-
head(path: string, middlewares: Middleware<T>, callback: Callback<T>): this;
|
|
128
136
|
/**
|
|
129
137
|
* Registers a route that responds to all HTTP methods
|
|
130
138
|
* @param path - URL path pattern
|
|
131
139
|
* @param args - Handler callback or middleware(s) + handler
|
|
132
140
|
*/
|
|
133
141
|
all(path: string, callback: Callback<T>): this;
|
|
142
|
+
all(path: string, middleware: Middleware<T>): this;
|
|
143
|
+
all(path: string, middleware: Middleware<T>, callback: Callback<T>): this;
|
|
134
144
|
all(path: string, middlewares: Middleware<T>[], callback: Callback<T>): this;
|
|
135
|
-
all(path: string, middlewares: Middleware<T>, callback: Callback<T>): this;
|
|
136
145
|
/**
|
|
137
146
|
* Generic method registration for custom HTTP methods
|
|
138
147
|
* @param method - HTTP method name (e.g., 'PURGE')
|
|
@@ -144,8 +153,9 @@ export declare class Router<T extends Record<string, any> = {}> extends Middlewa
|
|
|
144
153
|
* server.addRoute('PURGE', '/cache', purgeHandler);
|
|
145
154
|
*/
|
|
146
155
|
addRoute(method: HTTPMethod, path: string, callback: Callback<T>): this;
|
|
156
|
+
addRoute(method: HTTPMethod, path: string, middleware: Middleware<T>): this;
|
|
157
|
+
addRoute(method: HTTPMethod, path: string, middleware: Middleware<T>, callback: Callback<T>): this;
|
|
147
158
|
addRoute(method: HTTPMethod, path: string, middlewares: Middleware<T>[], callback: Callback<T>): this;
|
|
148
|
-
addRoute(method: HTTPMethod, path: string, middlewares: Middleware<T>, callback: Callback<T>): this;
|
|
149
159
|
/**
|
|
150
160
|
* Mount a sub-router at specific path prefix
|
|
151
161
|
* @param path - Base path for the sub-router
|
package/core/router.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { GlobalConfig } from "./config";
|
|
2
|
-
import MiddlewareConfigure, { TriMiddleware, } from "./MiddlewareConfigure";
|
|
3
1
|
import { getFiles } from "../utils/staticFile";
|
|
4
2
|
import { sanitizePathSplit } from "../utils/url";
|
|
3
|
+
import { GlobalConfig } from "./config";
|
|
4
|
+
import MiddlewareConfigure, { TriMiddleware, } from "./MiddlewareConfigure";
|
|
5
|
+
;
|
|
5
6
|
class TrieRouter {
|
|
6
7
|
children = new Map();
|
|
7
8
|
handlers = new Map();
|
package/core/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { COLORS } from "../utils/colors";
|
|
1
2
|
import { GlobalConfig } from "./config";
|
|
2
3
|
import { Context, httpStatusMap } from "./context";
|
|
3
4
|
import { Router } from "./router";
|
|
4
|
-
import { COLORS } from "../utils/colors";
|
|
5
5
|
import { useParams } from "../utils/params";
|
|
6
6
|
export class TezX extends Router {
|
|
7
7
|
constructor({ basePath = "/", env = {}, debugMode = false, allowDuplicateMw = false, overwriteMethod = true, } = {}) {
|
|
@@ -83,10 +83,14 @@ export class TezX extends Router {
|
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
85
|
const response = await next();
|
|
86
|
-
if (
|
|
86
|
+
if (response instanceof Response) {
|
|
87
|
+
return response;
|
|
88
|
+
}
|
|
89
|
+
if (!response && !ctx.body) {
|
|
87
90
|
throw new Error(`Handler did not return a response or next() was not called. Path: ${ctx.pathname}, Method: ${ctx.method}`);
|
|
88
91
|
}
|
|
89
|
-
|
|
92
|
+
const resBody = response || ctx.body;
|
|
93
|
+
return ctx.send(resBody, ctx.headers.toObject());
|
|
90
94
|
};
|
|
91
95
|
}
|
|
92
96
|
#findMiddleware(pathname) {
|
package/index.js
CHANGED
|
@@ -42,7 +42,6 @@ interface LazyLoadOptions<T> {
|
|
|
42
42
|
get: (key: string) => CacheItem<T> | undefined;
|
|
43
43
|
set: (key: string, value: CacheItem<T>) => void;
|
|
44
44
|
delete: (key: string) => void;
|
|
45
|
-
clear?: () => void;
|
|
46
45
|
};
|
|
47
46
|
/**
|
|
48
47
|
* ⏳ Cache Time-To-Live (TTL) in milliseconds. This determines how long cached modules are valid.
|
|
@@ -45,14 +45,14 @@ export const lazyLoadModules = (options) => {
|
|
|
45
45
|
lifecycleHooks.onCacheSet?.(moduleName, module, ctx);
|
|
46
46
|
}
|
|
47
47
|
ctx[moduleContextKey] = module;
|
|
48
|
-
lifecycleHooks.onComplete?.(moduleName, module, ctx);
|
|
49
|
-
GlobalConfig.debugging.success(`Successfully loaded module: ${moduleName}`);
|
|
50
48
|
if (module.init && typeof module.init === "function") {
|
|
51
49
|
const initResult = await module.init(dependencies, ctx);
|
|
52
50
|
if (initResult instanceof Response) {
|
|
53
51
|
return initResult;
|
|
54
52
|
}
|
|
55
53
|
}
|
|
54
|
+
lifecycleHooks.onComplete?.(moduleName, module, ctx);
|
|
55
|
+
GlobalConfig.debugging.success(`Successfully loaded module: ${moduleName}`);
|
|
56
56
|
}
|
|
57
57
|
catch (error) {
|
|
58
58
|
GlobalConfig.debugging.error(`Error loading module: ${moduleName}`, error);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Context
|
|
1
|
+
import { Context } from "..";
|
|
2
|
+
import { Middleware } from "../core/router";
|
|
2
3
|
export type PaginationOptions = {
|
|
3
4
|
/**
|
|
4
5
|
* 🔢 Default page number when not specified
|
|
@@ -52,7 +53,7 @@ export type PaginationOptions = {
|
|
|
52
53
|
* return db.find().skip((page-1)*limit).limit(limit);
|
|
53
54
|
* }
|
|
54
55
|
*/
|
|
55
|
-
getDataSource?: (ctx: Context
|
|
56
|
+
getDataSource?: <T extends Record<string, any> = {}>(ctx: Context<T>, pagination: {
|
|
56
57
|
page: number;
|
|
57
58
|
limit: number;
|
|
58
59
|
offset: number;
|
|
@@ -60,6 +61,19 @@ export type PaginationOptions = {
|
|
|
60
61
|
[key: string]: any;
|
|
61
62
|
}>;
|
|
62
63
|
};
|
|
64
|
+
export type PaginationBodyType = {
|
|
65
|
+
[x: string]: any;
|
|
66
|
+
pagination: {
|
|
67
|
+
page: number;
|
|
68
|
+
limit: number;
|
|
69
|
+
totalItems: any;
|
|
70
|
+
totalPages: number;
|
|
71
|
+
hasNextPage: boolean;
|
|
72
|
+
hasPrevPage: boolean;
|
|
73
|
+
nextPage: number | null;
|
|
74
|
+
prevPage: number | null;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
63
77
|
/**
|
|
64
78
|
* 🗂️ Advanced pagination middleware with dynamic data fetching
|
|
65
79
|
*
|
|
@@ -69,12 +83,12 @@ export type PaginationOptions = {
|
|
|
69
83
|
* - Comprehensive pagination metadata
|
|
70
84
|
* - Built-in error handling
|
|
71
85
|
*
|
|
72
|
-
* @param {PaginationOptions} [options={}] - Configuration options
|
|
73
|
-
* @returns {
|
|
86
|
+
* @param {PaginationOptions} [options={}] - Configuration options for pagination behavior
|
|
87
|
+
* @returns {Callback} Middleware function that processes pagination and sets response
|
|
74
88
|
*
|
|
75
89
|
* @example
|
|
76
90
|
* // Basic usage
|
|
77
|
-
* app.get('/users', paginationHandler()
|
|
91
|
+
* app.get('/users', paginationHandler());
|
|
78
92
|
*
|
|
79
93
|
* // With dynamic data source
|
|
80
94
|
* app.get('/products', paginationHandler({
|
package/middleware/pagination.js
CHANGED
|
@@ -9,39 +9,35 @@ export const paginationHandler = (options = {}) => {
|
|
|
9
9
|
ctx.pagination = {
|
|
10
10
|
page,
|
|
11
11
|
limit,
|
|
12
|
-
offset
|
|
12
|
+
offset,
|
|
13
13
|
queryKeyPage,
|
|
14
|
-
queryKeyLimit
|
|
14
|
+
queryKeyLimit,
|
|
15
15
|
};
|
|
16
16
|
if (getDataSource) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
catch (error) {
|
|
40
|
-
ctx.setStatus = 500;
|
|
41
|
-
ctx.body = { error: "Internal Server Error", };
|
|
42
|
-
throw new Error("Error fetching or processing data:", error?.message);
|
|
17
|
+
const dataSourceResponse = await getDataSource(ctx, { page, limit, offset });
|
|
18
|
+
const total = dataSourceResponse?.[countKey];
|
|
19
|
+
const data = dataSourceResponse?.[dataKey];
|
|
20
|
+
const pagination = {
|
|
21
|
+
page,
|
|
22
|
+
limit,
|
|
23
|
+
totalItems: total,
|
|
24
|
+
totalPages: Math.ceil(total / limit),
|
|
25
|
+
hasNextPage: page < Math.ceil(total / limit),
|
|
26
|
+
hasPrevPage: page > 1,
|
|
27
|
+
nextPage: page < Math.ceil(total / limit) ? page + 1 : null,
|
|
28
|
+
prevPage: page > 1 ? page - 1 : null,
|
|
29
|
+
};
|
|
30
|
+
ctx.pagination = pagination;
|
|
31
|
+
const body = {
|
|
32
|
+
[dataKey]: data,
|
|
33
|
+
[countKey]: total,
|
|
34
|
+
pagination,
|
|
35
|
+
};
|
|
36
|
+
if (next) {
|
|
37
|
+
ctx.body = body;
|
|
38
|
+
return await next();
|
|
43
39
|
}
|
|
40
|
+
return (ctx.body = body);
|
|
44
41
|
}
|
|
45
|
-
return await next();
|
|
46
42
|
};
|
|
47
43
|
};
|
|
@@ -24,13 +24,32 @@ export type RateLimiterOptions = {
|
|
|
24
24
|
// * ⚠️ (Future) Storage backend - currently memory only
|
|
25
25
|
// * @todo Implement Redis storage
|
|
26
26
|
// */
|
|
27
|
+
/**
|
|
28
|
+
* 🔄 Custom cache storage implementation (e.g., using `Map`, `Redis`, etc.).
|
|
29
|
+
* By default, it uses a `Map<string, { count: number; resetTime: number }>`.
|
|
30
|
+
*/
|
|
31
|
+
cacheStorage?: {
|
|
32
|
+
get: (key: string) => {
|
|
33
|
+
count: number;
|
|
34
|
+
resetTime: number;
|
|
35
|
+
} | undefined;
|
|
36
|
+
set: (key: string, value: {
|
|
37
|
+
count: number;
|
|
38
|
+
resetTime: number;
|
|
39
|
+
}) => void;
|
|
40
|
+
delete: (key: string) => void;
|
|
41
|
+
entries: () => IterableIterator<[string, {
|
|
42
|
+
count: number;
|
|
43
|
+
resetTime: number;
|
|
44
|
+
}]>;
|
|
45
|
+
};
|
|
27
46
|
/**
|
|
28
47
|
* 🛑 Custom rate limit exceeded handler
|
|
29
48
|
* @default Sends 429 status with Retry-After header
|
|
30
49
|
* @example
|
|
31
50
|
* onError: (ctx, retryAfter) => {
|
|
32
51
|
* ctx.status = 429;
|
|
33
|
-
*
|
|
52
|
+
* throw new Error( `Rate limit exceeded. Try again in ${retryAfter} seconds.`);
|
|
34
53
|
* }
|
|
35
54
|
*/
|
|
36
55
|
onError?: (ctx: Context, retryAfter: number, error: Error) => void;
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
export const rateLimiter = (options) => {
|
|
2
|
-
const { maxRequests, windowMs, keyGenerator = (ctx) => `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`, onError = (ctx, retryAfter, error) => {
|
|
2
|
+
const { maxRequests, windowMs, keyGenerator = (ctx) => `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`, cacheStorage = new Map(), onError = (ctx, retryAfter, error) => {
|
|
3
3
|
ctx.setStatus = 429;
|
|
4
4
|
throw new Error(`Rate limit exceeded. Try again in ${retryAfter} seconds.`);
|
|
5
5
|
}, } = options;
|
|
6
|
-
const memoryStore = new Map();
|
|
7
6
|
return async (ctx, next) => {
|
|
8
7
|
const key = keyGenerator(ctx);
|
|
9
8
|
let requestCount;
|
|
10
9
|
let resetTime;
|
|
11
|
-
for (const [key, entry] of
|
|
10
|
+
for (const [key, entry] of cacheStorage.entries()) {
|
|
12
11
|
if (Date.now() >= entry.resetTime) {
|
|
13
|
-
|
|
12
|
+
cacheStorage.delete(key);
|
|
14
13
|
}
|
|
15
14
|
}
|
|
16
|
-
const entry =
|
|
15
|
+
const entry = cacheStorage.get(key);
|
|
17
16
|
if (entry && Date.now() < entry.resetTime) {
|
|
18
17
|
requestCount = entry.count + 1;
|
|
19
18
|
resetTime = entry.resetTime;
|
|
@@ -21,7 +20,7 @@ export const rateLimiter = (options) => {
|
|
|
21
20
|
else {
|
|
22
21
|
requestCount = 1;
|
|
23
22
|
resetTime = Date.now() + windowMs;
|
|
24
|
-
|
|
23
|
+
cacheStorage.set(key, { count: requestCount, resetTime });
|
|
25
24
|
}
|
|
26
25
|
if (requestCount > maxRequests) {
|
|
27
26
|
const retryAfter = Math.ceil((resetTime - Date.now()) / 1000);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tezx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.36",
|
|
4
4
|
"description": "TezX is a high-performance, lightweight JavaScript framework designed for speed, scalability, and flexibility. It enables efficient routing, middleware management, and static file serving with minimal configuration. Fully compatible with Node.js, Deno, and Bun.",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "index.js",
|