tezx 1.0.13 → 1.0.15
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 +8 -3
- package/{dist/cjs → cjs}/index.js +1 -1
- package/{dist/index.js → index.js} +1 -1
- package/package.json +18 -19
- package/dist/MiddlewareConfigure.d.ts +0 -17
- package/dist/MiddlewareConfigure.js +0 -63
- package/dist/adapter.d.ts +0 -10
- package/dist/adapter.js +0 -162
- package/dist/cjs/MiddlewareConfigure.js +0 -68
- package/dist/cjs/adapter.js +0 -166
- package/dist/cjs/common.js +0 -15
- package/dist/cjs/config/config.js +0 -35
- package/dist/cjs/context.js +0 -428
- package/dist/cjs/environment.js +0 -34
- package/dist/cjs/header.js +0 -85
- package/dist/cjs/helper/common.js +0 -11
- package/dist/cjs/helper/env-parser.js +0 -69
- package/dist/cjs/helper/index.js +0 -20
- package/dist/cjs/middleware/cors.js +0 -49
- package/dist/cjs/middleware/index.js +0 -26
- package/dist/cjs/middleware/logger.js +0 -21
- package/dist/cjs/middleware/powered-by.js +0 -10
- package/dist/cjs/middleware/rateLimiter.js +0 -40
- package/dist/cjs/middleware/request-id.js +0 -15
- package/dist/cjs/middleware/sanitizeHeader.js +0 -51
- package/dist/cjs/middleware/secureHeaders.js +0 -42
- package/dist/cjs/middleware/xssProtection.js +0 -26
- package/dist/cjs/request.js +0 -80
- package/dist/cjs/router.js +0 -377
- package/dist/cjs/server.js +0 -170
- package/dist/cjs/utils/colors.js +0 -24
- package/dist/cjs/utils/debugging.js +0 -17
- package/dist/cjs/utils/formData.js +0 -219
- package/dist/cjs/utils/params.js +0 -94
- package/dist/cjs/utils/state.js +0 -34
- package/dist/cjs/utils/staticFile.js +0 -158
- package/dist/cjs/utils/url.js +0 -59
- package/dist/common.d.ts +0 -21
- package/dist/common.js +0 -11
- package/dist/config/config.d.ts +0 -17
- package/dist/config/config.js +0 -31
- package/dist/context.d.ts +0 -195
- package/dist/context.js +0 -424
- package/dist/environment.d.ts +0 -6
- package/dist/environment.js +0 -30
- package/dist/header.d.ts +0 -71
- package/dist/header.js +0 -81
- package/dist/helper/common.d.ts +0 -5
- package/dist/helper/common.js +0 -8
- package/dist/helper/env-parser.d.ts +0 -5
- package/dist/helper/env-parser.js +0 -66
- package/dist/helper/index.d.ts +0 -2
- package/dist/helper/index.js +0 -2
- package/dist/middleware/cors.d.ts +0 -10
- package/dist/middleware/cors.js +0 -46
- package/dist/middleware/index.d.ts +0 -9
- package/dist/middleware/index.js +0 -8
- package/dist/middleware/logger.d.ts +0 -15
- package/dist/middleware/logger.js +0 -18
- package/dist/middleware/powered-by.d.ts +0 -16
- package/dist/middleware/powered-by.js +0 -6
- package/dist/middleware/rateLimiter.d.ts +0 -61
- package/dist/middleware/rateLimiter.js +0 -36
- package/dist/middleware/request-id.d.ts +0 -16
- package/dist/middleware/request-id.js +0 -11
- package/dist/middleware/sanitizeHeader.d.ts +0 -53
- package/dist/middleware/sanitizeHeader.js +0 -47
- package/dist/middleware/secureHeaders.d.ts +0 -78
- package/dist/middleware/secureHeaders.js +0 -38
- package/dist/middleware/xssProtection.d.ts +0 -43
- package/dist/middleware/xssProtection.js +0 -22
- package/dist/request.d.ts +0 -82
- package/dist/request.js +0 -76
- package/dist/router.d.ts +0 -191
- package/dist/router.js +0 -373
- package/dist/server.d.ts +0 -54
- package/dist/server.js +0 -166
- package/dist/utils/colors.d.ts +0 -21
- package/dist/utils/colors.js +0 -21
- package/dist/utils/debugging.d.ts +0 -7
- package/dist/utils/debugging.js +0 -13
- package/dist/utils/formData.d.ts +0 -5
- package/dist/utils/formData.js +0 -213
- package/dist/utils/params.d.ts +0 -7
- package/dist/utils/params.js +0 -91
- package/dist/utils/state.d.ts +0 -50
- package/dist/utils/state.js +0 -30
- package/dist/utils/staticFile.d.ts +0 -9
- package/dist/utils/staticFile.js +0 -154
- package/dist/utils/url.d.ts +0 -16
- package/dist/utils/url.js +0 -55
- /package/{dist/index.d.ts → index.d.ts} +0 -0
package/dist/header.d.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
export declare class HeadersParser {
|
|
2
|
-
private headers;
|
|
3
|
-
constructor(init?: [string, string | string[]][] | Record<string, string>);
|
|
4
|
-
/**
|
|
5
|
-
* Adds multiple headers to the parser.
|
|
6
|
-
* @param headers - Headers as an array of tuples or a record object.
|
|
7
|
-
*/
|
|
8
|
-
add(headers: [string, string | string[]][] | Record<string, string>): this;
|
|
9
|
-
/**
|
|
10
|
-
* Sets a header value.
|
|
11
|
-
* @param key - Header name.
|
|
12
|
-
* @param value - Header value(s).
|
|
13
|
-
*/
|
|
14
|
-
set(key: string, value: string | string[]): this;
|
|
15
|
-
clear(): this;
|
|
16
|
-
/**
|
|
17
|
-
* Retrieves the first value of a header.
|
|
18
|
-
* @param key - Header name.
|
|
19
|
-
* @returns The first header value or undefined if not found.
|
|
20
|
-
*/
|
|
21
|
-
get(key: string): string | undefined;
|
|
22
|
-
/**
|
|
23
|
-
* Retrieves all values of a header.
|
|
24
|
-
* @param key - Header name.
|
|
25
|
-
* @returns An array of header values.
|
|
26
|
-
*/
|
|
27
|
-
getAll(key: string): string[];
|
|
28
|
-
/**
|
|
29
|
-
* Checks if a header exists.
|
|
30
|
-
* @param key - Header name.
|
|
31
|
-
* @returns True if the header exists, false otherwise.
|
|
32
|
-
*/
|
|
33
|
-
has(key: string): boolean;
|
|
34
|
-
/**
|
|
35
|
-
* Deletes a header.
|
|
36
|
-
* @param key - Header name.
|
|
37
|
-
* @returns True if deleted successfully, false otherwise.
|
|
38
|
-
*/
|
|
39
|
-
delete(key: string): boolean;
|
|
40
|
-
/**
|
|
41
|
-
* Appends a value to an existing header or creates a new one.
|
|
42
|
-
* @param key - Header name.
|
|
43
|
-
* @param value - Value to append.
|
|
44
|
-
*/
|
|
45
|
-
append(key: string, value: string): this;
|
|
46
|
-
/**
|
|
47
|
-
* Returns an iterator over header entries.
|
|
48
|
-
* @returns IterableIterator of header key-value pairs.
|
|
49
|
-
*/
|
|
50
|
-
entries(): IterableIterator<[string, string[]]>;
|
|
51
|
-
/**
|
|
52
|
-
* Returns an iterator over header keys.
|
|
53
|
-
* @returns IterableIterator of header names.
|
|
54
|
-
*/
|
|
55
|
-
keys(): IterableIterator<string>;
|
|
56
|
-
/**
|
|
57
|
-
* Returns an iterator over header values.
|
|
58
|
-
* @returns IterableIterator of header values arrays.
|
|
59
|
-
*/
|
|
60
|
-
values(): IterableIterator<string[]>;
|
|
61
|
-
/**
|
|
62
|
-
* Iterates over headers and executes a callback function.
|
|
63
|
-
* @param callback - Function to execute for each header.
|
|
64
|
-
*/
|
|
65
|
-
forEach(callback: (value: string[], key: string) => void): void;
|
|
66
|
-
/**
|
|
67
|
-
* Converts headers into a plain object.
|
|
68
|
-
* @returns A record of headers where single-value headers are returned as a string.
|
|
69
|
-
*/
|
|
70
|
-
toObject(): Record<string, string | string[]>;
|
|
71
|
-
}
|
package/dist/header.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
export class HeadersParser {
|
|
2
|
-
headers = new Map();
|
|
3
|
-
constructor(init) {
|
|
4
|
-
if (init) {
|
|
5
|
-
this.add(init);
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
add(headers) {
|
|
9
|
-
if (Array.isArray(headers)) {
|
|
10
|
-
for (const [key, value] of headers) {
|
|
11
|
-
this.set(key, value);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
else if (typeof Headers !== "undefined" && headers instanceof Headers) {
|
|
15
|
-
for (const [key, value] of headers.entries()) {
|
|
16
|
-
this.set(key, value);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
else if (typeof headers === "object") {
|
|
20
|
-
for (const key in headers) {
|
|
21
|
-
if (Object.prototype.hasOwnProperty.call(headers, key)) {
|
|
22
|
-
this.set(key, headers[key]);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return this;
|
|
27
|
-
}
|
|
28
|
-
set(key, value) {
|
|
29
|
-
this.headers.set(key.toLowerCase(), Array.isArray(value) ? value : [value]);
|
|
30
|
-
return this;
|
|
31
|
-
}
|
|
32
|
-
clear() {
|
|
33
|
-
this.headers.clear();
|
|
34
|
-
return this;
|
|
35
|
-
}
|
|
36
|
-
get(key) {
|
|
37
|
-
const values = this.headers.get(key.toLowerCase());
|
|
38
|
-
return values ? values[0] : undefined;
|
|
39
|
-
}
|
|
40
|
-
getAll(key) {
|
|
41
|
-
return this.headers.get(key.toLowerCase()) || [];
|
|
42
|
-
}
|
|
43
|
-
has(key) {
|
|
44
|
-
return this.headers.has(key.toLowerCase());
|
|
45
|
-
}
|
|
46
|
-
delete(key) {
|
|
47
|
-
return this.headers.delete(key.toLowerCase());
|
|
48
|
-
}
|
|
49
|
-
append(key, value) {
|
|
50
|
-
const lowerKey = key.toLowerCase();
|
|
51
|
-
if (this.headers.has(lowerKey)) {
|
|
52
|
-
this.headers.get(lowerKey).push(value);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
this.headers.set(lowerKey, [value]);
|
|
56
|
-
}
|
|
57
|
-
return this;
|
|
58
|
-
}
|
|
59
|
-
entries() {
|
|
60
|
-
return this.headers.entries();
|
|
61
|
-
}
|
|
62
|
-
keys() {
|
|
63
|
-
return this.headers.keys();
|
|
64
|
-
}
|
|
65
|
-
values() {
|
|
66
|
-
return this.headers.values();
|
|
67
|
-
}
|
|
68
|
-
forEach(callback) {
|
|
69
|
-
for (const [key, value] of this.headers) {
|
|
70
|
-
callback(value, key);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
toObject() {
|
|
74
|
-
const obj = {};
|
|
75
|
-
for (const [key, value] of this.headers.entries()) {
|
|
76
|
-
obj[key] = value.length > 1 ? value : value[0];
|
|
77
|
-
}
|
|
78
|
-
return obj;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
Object.defineProperty(HeadersParser, "name", { value: "Headers" });
|
package/dist/helper/common.d.ts
DELETED
package/dist/helper/common.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export function generateID() {
|
|
2
|
-
const timestamp = Date.now().toString(16);
|
|
3
|
-
const random = Math.floor(Math.random() * 0xffffffffffff)
|
|
4
|
-
.toString(16)
|
|
5
|
-
.padStart(12, "0");
|
|
6
|
-
const pid = (process.pid % 0x10000).toString(16).padStart(4, "0");
|
|
7
|
-
return `${timestamp}-${random}-${pid}`;
|
|
8
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { EnvironmentDetector } from "../environment";
|
|
2
|
-
function parseEnvFile(filePath, result) {
|
|
3
|
-
try {
|
|
4
|
-
let fileExists = false;
|
|
5
|
-
let runtime = EnvironmentDetector.getEnvironment;
|
|
6
|
-
if (runtime === "node" || runtime === "bun") {
|
|
7
|
-
const { existsSync } = require("fs");
|
|
8
|
-
fileExists = existsSync(filePath);
|
|
9
|
-
}
|
|
10
|
-
else if (runtime === "deno") {
|
|
11
|
-
try {
|
|
12
|
-
Deno.statSync(filePath);
|
|
13
|
-
fileExists = true;
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
fileExists = false;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
if (!fileExists) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
let fileContent = "";
|
|
23
|
-
if (runtime === "node" || runtime === "bun") {
|
|
24
|
-
const { readFileSync } = require("fs");
|
|
25
|
-
fileContent = readFileSync(filePath, "utf8");
|
|
26
|
-
}
|
|
27
|
-
else if (runtime === "deno") {
|
|
28
|
-
fileContent = new TextDecoder("utf-8").decode(Deno.readFileSync(filePath));
|
|
29
|
-
}
|
|
30
|
-
const lines = fileContent.split("\n");
|
|
31
|
-
for (const line of lines) {
|
|
32
|
-
const trimmedLine = line.trim();
|
|
33
|
-
if (!trimmedLine || trimmedLine.startsWith("#"))
|
|
34
|
-
continue;
|
|
35
|
-
const [key, value] = trimmedLine.split("=", 2).map((part) => part.trim());
|
|
36
|
-
if (key && value) {
|
|
37
|
-
const parsedValue = value
|
|
38
|
-
.replace(/^"(.*)"$/, "$1")
|
|
39
|
-
.replace(/^'(.*)'$/, "$1");
|
|
40
|
-
result[key] = parsedValue;
|
|
41
|
-
if (runtime === "node" || runtime === "bun") {
|
|
42
|
-
process.env[key] = parsedValue;
|
|
43
|
-
}
|
|
44
|
-
else if (runtime === "deno") {
|
|
45
|
-
Deno.env.set(key, parsedValue);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
console.error(`[dotenv] Error parsing file: ${filePath}`, error);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
export function loadEnv(basePath = "./") {
|
|
55
|
-
const result = {};
|
|
56
|
-
const envFiles = [
|
|
57
|
-
".env",
|
|
58
|
-
".env.local",
|
|
59
|
-
`.env.${process?.env?.NODE_ENV || "development"}`,
|
|
60
|
-
`.env.${process?.env?.NODE_ENV || "development"}.local`,
|
|
61
|
-
];
|
|
62
|
-
for (const envFile of envFiles) {
|
|
63
|
-
parseEnvFile(`${basePath}${envFile}`, result);
|
|
64
|
-
}
|
|
65
|
-
return result;
|
|
66
|
-
}
|
package/dist/helper/index.d.ts
DELETED
package/dist/helper/index.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { ctx as Context } from "../router";
|
|
2
|
-
export type CorsOptions = {
|
|
3
|
-
origin?: string | RegExp | (string | RegExp)[] | ((reqOrigin: string) => boolean);
|
|
4
|
-
methods?: string[];
|
|
5
|
-
allowedHeaders?: string[];
|
|
6
|
-
exposedHeaders?: string[];
|
|
7
|
-
credentials?: boolean;
|
|
8
|
-
maxAge?: number;
|
|
9
|
-
};
|
|
10
|
-
export declare function cors(option?: CorsOptions): (ctx: Context, next: () => Promise<any>) => Promise<any>;
|
package/dist/middleware/cors.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
export function cors(option = {}) {
|
|
2
|
-
const { methods, allowedHeaders, credentials, exposedHeaders, maxAge, origin, } = option;
|
|
3
|
-
return async (ctx, next) => {
|
|
4
|
-
const reqOrigin = ctx.req.headers.get("origin") || "";
|
|
5
|
-
let allowOrigin = "*";
|
|
6
|
-
if (typeof origin === "string") {
|
|
7
|
-
allowOrigin = origin;
|
|
8
|
-
}
|
|
9
|
-
else if (origin instanceof RegExp) {
|
|
10
|
-
allowOrigin = origin.test(reqOrigin) ? reqOrigin : "";
|
|
11
|
-
}
|
|
12
|
-
else if (Array.isArray(origin)) {
|
|
13
|
-
const isAllowed = origin.some((item) => {
|
|
14
|
-
if (typeof item === "string") {
|
|
15
|
-
return item === reqOrigin;
|
|
16
|
-
}
|
|
17
|
-
else if (item instanceof RegExp) {
|
|
18
|
-
return item.test(reqOrigin);
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
allowOrigin = isAllowed ? reqOrigin : "";
|
|
22
|
-
}
|
|
23
|
-
else if (typeof origin === "function") {
|
|
24
|
-
allowOrigin = origin(reqOrigin) ? reqOrigin : "";
|
|
25
|
-
}
|
|
26
|
-
ctx.headers.set("Access-Control-Allow-Origin", allowOrigin);
|
|
27
|
-
ctx.headers.set("Access-Control-Allow-Methods", (methods || ["GET", "POST", "PUT", "DELETE"]).join(", "));
|
|
28
|
-
ctx.headers.set("Access-Control-Allow-Headers", (allowedHeaders || ["Content-Type", "Authorization"]).join(", "));
|
|
29
|
-
if (exposedHeaders) {
|
|
30
|
-
ctx.headers.set("Access-Control-Expose-Headers", exposedHeaders.join(", "));
|
|
31
|
-
}
|
|
32
|
-
if (credentials) {
|
|
33
|
-
ctx.headers.set("Access-Control-Allow-Credentials", "true");
|
|
34
|
-
}
|
|
35
|
-
if (maxAge) {
|
|
36
|
-
ctx.headers.set("Access-Control-Max-Age", maxAge.toString());
|
|
37
|
-
}
|
|
38
|
-
if (ctx.req.method === "OPTIONS") {
|
|
39
|
-
return new Response(null, {
|
|
40
|
-
status: 204,
|
|
41
|
-
headers: ctx.headers.toObject(),
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
return await next();
|
|
45
|
-
};
|
|
46
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export { cors } from "./cors";
|
|
2
|
-
export type { CorsOptions } from "./cors";
|
|
3
|
-
export * from "./logger";
|
|
4
|
-
export * from "./powered-by";
|
|
5
|
-
export * from "./request-id";
|
|
6
|
-
export * from "./secureHeaders";
|
|
7
|
-
export * from "./xssProtection";
|
|
8
|
-
export * from "./sanitizeHeader";
|
|
9
|
-
export * from "./rateLimiter";
|
package/dist/middleware/index.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Middleware } from "../router";
|
|
2
|
-
/**
|
|
3
|
-
* Logger Middleware
|
|
4
|
-
* Logs incoming requests with method, pathname, status, and execution time.
|
|
5
|
-
*
|
|
6
|
-
* @returns {Middleware} - A middleware function for logging HTTP requests.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* import { logger } from 'tezx';
|
|
11
|
-
*
|
|
12
|
-
* app.use(logger());
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
export declare function logger(): Middleware;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { COLORS } from "../utils/colors";
|
|
2
|
-
export function logger() {
|
|
3
|
-
return async (ctx, next) => {
|
|
4
|
-
try {
|
|
5
|
-
console.log(`${COLORS.bold}<-- ${COLORS.reset}${COLORS.bgMagenta} ${ctx.method} ${COLORS.reset} ${ctx.pathname}`);
|
|
6
|
-
const startTime = performance.now();
|
|
7
|
-
let n = await next();
|
|
8
|
-
const elapsed = performance.now() - startTime;
|
|
9
|
-
console.log(`${COLORS.bold}--> ${COLORS.reset}${COLORS.bgBlue} ${ctx.method} ${COLORS.reset} ${ctx.pathname} ` +
|
|
10
|
-
`${COLORS.yellow}${ctx.getStatus}${COLORS.reset} ${COLORS.magenta}${elapsed.toFixed(2)}ms${COLORS.reset}`);
|
|
11
|
-
return n;
|
|
12
|
-
}
|
|
13
|
-
catch (err) {
|
|
14
|
-
console.error(`${COLORS.red}Error:${COLORS.reset}`, err.stack);
|
|
15
|
-
throw new Error(err.stack);
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Middleware } from "../router";
|
|
2
|
-
/**
|
|
3
|
-
* PoweredBy Middleware
|
|
4
|
-
* Adds an "X-Powered-By" header to responses.
|
|
5
|
-
*
|
|
6
|
-
* @param {string} [serverName] - Optional custom server name; defaults to "TezX".
|
|
7
|
-
* @returns {Middleware} - A middleware function for setting the "X-Powered-By" header.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { poweredBy } from 'tezx';
|
|
12
|
-
*
|
|
13
|
-
* app.use(poweredBy("MyServer"));
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export declare const poweredBy: (serverName?: string) => Middleware;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { Context } from "../context";
|
|
2
|
-
import { Middleware } from "../router";
|
|
3
|
-
export type RateLimiterOptions = {
|
|
4
|
-
/**
|
|
5
|
-
* 🔴 Maximum allowed requests in the time window
|
|
6
|
-
* @example
|
|
7
|
-
* maxRequests: 100 // Allow 100 requests per window
|
|
8
|
-
*/
|
|
9
|
-
maxRequests: number;
|
|
10
|
-
/**
|
|
11
|
-
* 🕒 Time window in milliseconds
|
|
12
|
-
* @example
|
|
13
|
-
* windowMs: 60_000 // 1 minute window
|
|
14
|
-
*/
|
|
15
|
-
windowMs: number;
|
|
16
|
-
/**
|
|
17
|
-
* 🔑 Client identifier generator function
|
|
18
|
-
* @default (ctx) => `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`
|
|
19
|
-
* @example
|
|
20
|
-
* keyGenerator: (ctx) => ctx.user?.id || ctx.ip // Use user ID if authenticated
|
|
21
|
-
*/
|
|
22
|
-
keyGenerator?: (ctx: Context) => string;
|
|
23
|
-
/**
|
|
24
|
-
// * ⚠️ (Future) Storage backend - currently memory only
|
|
25
|
-
// * @todo Implement Redis storage
|
|
26
|
-
// */
|
|
27
|
-
/**
|
|
28
|
-
* 🛑 Custom rate limit exceeded handler
|
|
29
|
-
* @default Sends 429 status with Retry-After header
|
|
30
|
-
* @example
|
|
31
|
-
* onError: (ctx, retryAfter) => {
|
|
32
|
-
* ctx.status = 429;
|
|
33
|
-
* ctx.body = { error: `Try again in ${retryAfter} seconds` };
|
|
34
|
-
* }
|
|
35
|
-
*/
|
|
36
|
-
onError?: (ctx: Context, retryAfter: number, error: Error) => void;
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* 🚦 Rate limiting middleware for request throttling
|
|
40
|
-
*
|
|
41
|
-
* Enforces maximum request limits per client with sliding window.
|
|
42
|
-
* Currently supports in-memory storage only (Redis coming soon).
|
|
43
|
-
*
|
|
44
|
-
* @param {RateLimiterOptions} options - Configuration
|
|
45
|
-
* @returns {Middleware} Middleware function
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* // Basic rate limiting (100 requests/minute)
|
|
49
|
-
* app.use(rateLimiter({
|
|
50
|
-
* maxRequests: 100,
|
|
51
|
-
* windowMs: 60_000
|
|
52
|
-
* }));
|
|
53
|
-
*
|
|
54
|
-
* // Custom client identification
|
|
55
|
-
* app.use(rateLimiter({
|
|
56
|
-
* maxRequests: 10,
|
|
57
|
-
* windowMs: 10_000,
|
|
58
|
-
* keyGenerator: (ctx) => ctx.user?.id || ctx.ip
|
|
59
|
-
* }));
|
|
60
|
-
*/
|
|
61
|
-
export declare const rateLimiter: (options: RateLimiterOptions) => Middleware;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export const rateLimiter = (options) => {
|
|
2
|
-
const { maxRequests, windowMs, keyGenerator = (ctx) => `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`, onError = (ctx, retryAfter, error) => {
|
|
3
|
-
ctx.setStatus = 429;
|
|
4
|
-
throw new Error(`Rate limit exceeded. Try again in ${retryAfter} seconds.`);
|
|
5
|
-
}, } = options;
|
|
6
|
-
const memoryStore = new Map();
|
|
7
|
-
return async (ctx, next) => {
|
|
8
|
-
const key = keyGenerator(ctx);
|
|
9
|
-
let requestCount;
|
|
10
|
-
let resetTime;
|
|
11
|
-
for (const [key, entry] of memoryStore.entries()) {
|
|
12
|
-
if (Date.now() >= entry.resetTime) {
|
|
13
|
-
memoryStore.delete(key);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
const entry = memoryStore.get(key);
|
|
17
|
-
if (entry && Date.now() < entry.resetTime) {
|
|
18
|
-
requestCount = entry.count + 1;
|
|
19
|
-
resetTime = entry.resetTime;
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
requestCount = 1;
|
|
23
|
-
resetTime = Date.now() + windowMs;
|
|
24
|
-
memoryStore.set(key, { count: requestCount, resetTime });
|
|
25
|
-
}
|
|
26
|
-
if (requestCount > maxRequests) {
|
|
27
|
-
const retryAfter = Math.ceil((resetTime - Date.now()) / 1000);
|
|
28
|
-
ctx.headers.set("Retry-After", retryAfter.toString());
|
|
29
|
-
return onError(ctx, retryAfter, new Error(`Rate limit exceeded. Retry after ${retryAfter} seconds.`));
|
|
30
|
-
}
|
|
31
|
-
ctx.headers.set("X-RateLimit-Limit", maxRequests.toString());
|
|
32
|
-
ctx.headers.set("X-RateLimit-Remaining", (maxRequests - requestCount).toString());
|
|
33
|
-
ctx.headers.set("X-RateLimit-Reset", resetTime.toString());
|
|
34
|
-
return await next();
|
|
35
|
-
};
|
|
36
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Middleware } from "../router";
|
|
2
|
-
/**
|
|
3
|
-
* Request ID Middleware
|
|
4
|
-
* Assigns a unique request ID to each incoming request.
|
|
5
|
-
*
|
|
6
|
-
* @param {string} [headerName="X-Request-ID"] - Header name to use for request ID.
|
|
7
|
-
* @returns {Middleware} - A middleware function for tracking requests.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { requestID } from 'tezx';
|
|
12
|
-
*
|
|
13
|
-
* app.use(requestID());
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export declare const requestID: (headerName?: string) => Middleware;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { generateID } from "../helper";
|
|
2
|
-
export const requestID = (headerName = "X-Request-ID") => {
|
|
3
|
-
return (ctx, next) => {
|
|
4
|
-
const existingID = ctx.headers?.get(headerName.toLowerCase()) ||
|
|
5
|
-
ctx.headers?.get(headerName);
|
|
6
|
-
const requestId = existingID || `req-${generateID()}`;
|
|
7
|
-
ctx.state.set("requestID", requestId);
|
|
8
|
-
ctx.header(headerName, requestId);
|
|
9
|
-
return next();
|
|
10
|
-
};
|
|
11
|
-
};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { Middleware } from "../router";
|
|
2
|
-
type SanitizeHeadersOptions = {
|
|
3
|
-
/**
|
|
4
|
-
* 🟢 Whitelist of allowed headers (case-insensitive)
|
|
5
|
-
* @default [] (allow all if empty)
|
|
6
|
-
* @example
|
|
7
|
-
* whitelist: ['content-type', 'authorization'] // Only allow these headers
|
|
8
|
-
*/
|
|
9
|
-
whitelist?: string[];
|
|
10
|
-
/**
|
|
11
|
-
* 🔴 Blacklist of disallowed headers (case-insensitive)
|
|
12
|
-
* @default [] (block none if empty)
|
|
13
|
-
* @example
|
|
14
|
-
* blacklist: ['x-powered-by', 'server'] // Block server info headers
|
|
15
|
-
*/
|
|
16
|
-
blacklist?: string[];
|
|
17
|
-
/**
|
|
18
|
-
* 🔵 Normalize header keys to lowercase
|
|
19
|
-
* @default true
|
|
20
|
-
* @example
|
|
21
|
-
* normalizeKeys: false // Preserve original header case
|
|
22
|
-
*/
|
|
23
|
-
normalizeKeys?: boolean;
|
|
24
|
-
/**
|
|
25
|
-
* 🟠 Allow potentially unsafe characters in header values
|
|
26
|
-
* @default false
|
|
27
|
-
* @warning Enabling this may reduce security
|
|
28
|
-
* @example
|
|
29
|
-
* allowUnsafeCharacters: true // Allow CR/LF in headers
|
|
30
|
-
*/
|
|
31
|
-
allowUnsafeCharacters?: boolean;
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* 🧼 Middleware to sanitize HTTP headers for security and compliance
|
|
35
|
-
*
|
|
36
|
-
* Removes dangerous headers, enforces allow/block lists, and normalizes headers.
|
|
37
|
-
* Protects against header injection and information leakage.
|
|
38
|
-
*
|
|
39
|
-
* @param {SanitizeHeadersOptions} [options={}] - Configuration options
|
|
40
|
-
* @returns {Middleware} Middleware function
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* // Basic usage with defaults
|
|
44
|
-
* app.use(sanitizeHeaders());
|
|
45
|
-
*
|
|
46
|
-
* // Strict configuration
|
|
47
|
-
* app.use(sanitizeHeaders({
|
|
48
|
-
* whitelist: ['accept', 'content-type'],
|
|
49
|
-
* normalizeKeys: true
|
|
50
|
-
* }));
|
|
51
|
-
*/
|
|
52
|
-
export declare const sanitizeHeaders: (options?: SanitizeHeadersOptions) => Middleware;
|
|
53
|
-
export {};
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { GlobalConfig } from "../config/config";
|
|
2
|
-
export const sanitizeHeaders = (options = {}) => {
|
|
3
|
-
const { whitelist = [], blacklist = [], normalizeKeys = true, allowUnsafeCharacters = false, } = options;
|
|
4
|
-
return async (ctx, next) => {
|
|
5
|
-
const sanitizedHeaders = new Map();
|
|
6
|
-
for (const [key, values] of ctx.headers.entries()) {
|
|
7
|
-
if (!Array.isArray(values) || values.length === 0) {
|
|
8
|
-
continue;
|
|
9
|
-
}
|
|
10
|
-
const normalizedKey = normalizeKeys ? key.toLowerCase() : key;
|
|
11
|
-
if (whitelist.length > 0 &&
|
|
12
|
-
!whitelist.some((r) => r?.toLowerCase() === normalizedKey)) {
|
|
13
|
-
GlobalConfig.debugging.warn(`🚫 Header "${normalizedKey}" not in whitelist - removed`);
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
if (blacklist.some((r) => r.toLowerCase() === normalizedKey)) {
|
|
17
|
-
GlobalConfig.debugging.warn(`🚫 Header "${normalizedKey}" in blacklist - removed`);
|
|
18
|
-
continue;
|
|
19
|
-
}
|
|
20
|
-
if (!isValidHeaderName(normalizedKey)) {
|
|
21
|
-
GlobalConfig.debugging.warn(`⚠️ Invalid header name: "${normalizedKey}" - removed`);
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
const sanitizedValues = values
|
|
25
|
-
.map((value) => sanitizeHeaderValue(value, allowUnsafeCharacters))
|
|
26
|
-
.filter(Boolean);
|
|
27
|
-
if (sanitizedValues.length === 0) {
|
|
28
|
-
GlobalConfig.debugging.warn(`⚠️ All values for "${normalizedKey}" invalid - removed`);
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
sanitizedHeaders.set(normalizedKey, sanitizedValues);
|
|
32
|
-
}
|
|
33
|
-
ctx.headers.clear().add([...sanitizedHeaders.entries()]);
|
|
34
|
-
return await next();
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
const isValidHeaderName = (name) => {
|
|
38
|
-
const HEADER_NAME_REGEX = /^[a-zA-Z0-9\-_]+$/;
|
|
39
|
-
return HEADER_NAME_REGEX.test(name);
|
|
40
|
-
};
|
|
41
|
-
const sanitizeHeaderValue = (value, allowUnsafeCharacters) => {
|
|
42
|
-
let sanitized = value.trim();
|
|
43
|
-
if (!allowUnsafeCharacters) {
|
|
44
|
-
sanitized = sanitized.replace(/[\x00-\x1F\x7F]/g, "");
|
|
45
|
-
}
|
|
46
|
-
return sanitized;
|
|
47
|
-
};
|