tezx 3.0.9-beta → 3.0.11-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/bun/index.d.ts +4 -2
  2. package/bun/index.js +3 -2
  3. package/bun/ws.d.ts +37 -0
  4. package/bun/ws.js +23 -0
  5. package/cjs/bun/index.js +37 -5
  6. package/cjs/bun/ws.js +25 -0
  7. package/cjs/core/context.js +86 -88
  8. package/cjs/core/error.js +41 -0
  9. package/cjs/core/request.js +6 -6
  10. package/cjs/core/router.js +6 -6
  11. package/cjs/core/server.js +45 -63
  12. package/cjs/index.js +5 -2
  13. package/cjs/middleware/basic-auth.js +28 -54
  14. package/cjs/middleware/bearer-auth.js +34 -0
  15. package/cjs/middleware/cors.js +14 -24
  16. package/cjs/middleware/index.js +25 -0
  17. package/cjs/middleware/logger.js +6 -3
  18. package/cjs/middleware/pagination.js +1 -1
  19. package/cjs/middleware/powered-by.js +1 -1
  20. package/cjs/middleware/rate-limiter.js +20 -7
  21. package/cjs/middleware/request-id.js +4 -7
  22. package/cjs/middleware/sanitize-headers.js +8 -40
  23. package/cjs/middleware/xss-protection.js +2 -6
  24. package/cjs/registry/RadixRouter.js +72 -23
  25. package/cjs/utils/cookie.js +1 -1
  26. package/cjs/utils/rateLimit.js +2 -2
  27. package/cjs/utils/regexRouter.js +1 -0
  28. package/cjs/utils/response.js +21 -30
  29. package/core/context.d.ts +68 -68
  30. package/core/context.js +87 -89
  31. package/core/error.d.ts +95 -0
  32. package/core/error.js +37 -0
  33. package/core/request.d.ts +2 -2
  34. package/core/request.js +6 -6
  35. package/core/router.d.ts +11 -6
  36. package/core/router.js +6 -6
  37. package/core/server.js +45 -63
  38. package/index.d.ts +5 -3
  39. package/index.js +4 -2
  40. package/middleware/basic-auth.d.ts +38 -66
  41. package/middleware/basic-auth.js +28 -54
  42. package/middleware/bearer-auth.d.ts +52 -0
  43. package/middleware/bearer-auth.js +30 -0
  44. package/middleware/cors.d.ts +7 -21
  45. package/middleware/cors.js +14 -24
  46. package/middleware/index.d.ts +9 -0
  47. package/middleware/index.js +9 -0
  48. package/middleware/logger.d.ts +3 -1
  49. package/middleware/logger.js +6 -3
  50. package/middleware/pagination.d.ts +8 -6
  51. package/middleware/pagination.js +1 -1
  52. package/middleware/powered-by.js +1 -1
  53. package/middleware/rate-limiter.d.ts +0 -4
  54. package/middleware/rate-limiter.js +20 -7
  55. package/middleware/request-id.d.ts +1 -1
  56. package/middleware/request-id.js +3 -6
  57. package/middleware/sanitize-headers.d.ts +3 -11
  58. package/middleware/sanitize-headers.js +8 -40
  59. package/middleware/xss-protection.d.ts +1 -1
  60. package/middleware/xss-protection.js +1 -5
  61. package/package.json +6 -1
  62. package/registry/RadixRouter.js +72 -23
  63. package/types/index.d.ts +2 -1
  64. package/utils/cookie.js +1 -1
  65. package/utils/rateLimit.d.ts +1 -2
  66. package/utils/rateLimit.js +2 -2
  67. package/utils/regexRouter.js +1 -0
  68. package/utils/response.d.ts +12 -14
  69. package/utils/response.js +20 -29
  70. package/cjs/middleware/cache-control.js +0 -93
  71. package/cjs/middleware/detect-bot.js +0 -66
  72. package/cjs/middleware/detect-locale.js +0 -45
  73. package/cjs/middleware/i18n.js +0 -93
  74. package/cjs/middleware/lazy-loader.js +0 -74
  75. package/cjs/middleware/request-timeout.js +0 -43
  76. package/cjs/middleware/secure-headers.js +0 -43
  77. package/middleware/cache-control.d.ts +0 -56
  78. package/middleware/cache-control.js +0 -56
  79. package/middleware/detect-bot.d.ts +0 -111
  80. package/middleware/detect-bot.js +0 -62
  81. package/middleware/detect-locale.d.ts +0 -56
  82. package/middleware/detect-locale.js +0 -41
  83. package/middleware/i18n.d.ts +0 -102
  84. package/middleware/i18n.js +0 -89
  85. package/middleware/lazy-loader.d.ts +0 -73
  86. package/middleware/lazy-loader.js +0 -70
  87. package/middleware/request-timeout.d.ts +0 -26
  88. package/middleware/request-timeout.js +0 -39
  89. package/middleware/secure-headers.d.ts +0 -78
  90. package/middleware/secure-headers.js +0 -39
package/index.js CHANGED
@@ -1,9 +1,11 @@
1
+ import { TezXError } from "./core/error.js";
1
2
  import { Router } from "./core/router.js";
2
3
  import { TezX } from "./core/server.js";
3
- export { Router, TezX };
4
- export let version = "3.0.9-beta";
4
+ export { Router, TezX, TezXError };
5
+ export let version = "3.0.11-beta";
5
6
  export default {
6
7
  Router,
7
8
  TezX,
8
9
  version,
10
+ TezXError,
9
11
  };
@@ -1,83 +1,55 @@
1
1
  import { Context } from "../core/context.js";
2
2
  import { HttpBaseResponse, Middleware } from "../types/index.js";
3
3
  /**
4
- * Supported authentication method types.
4
+ * Options for Basic Authentication middleware.
5
5
  */
6
- export type AuthMethod = "basic" | "api-key" | "bearer-token";
7
- export type AuthCredential = {
8
- username?: any;
9
- password?: any;
10
- token?: any;
11
- apiKey?: any;
12
- };
13
- /**
14
- * Configuration options for dynamic basic authentication.
15
- */
16
- export type DynamicBasicAuthOptions = {
6
+ export type BasicAuthOptions = {
17
7
  /**
18
- * 🔐 Function to validate the provided credentials.
19
- * @param method - The method of authentication.
20
- * @param credentials - The extracted credentials.
8
+ * Function to validate the username and password.
9
+ * Can return a boolean or a Promise<boolean>.
10
+ *
11
+ * @param username - The username extracted from the request.
12
+ * @param password - The password extracted from the request.
21
13
  * @param ctx - The current request context.
22
- * @returns A boolean or Promise resolving to whether the credentials are valid.
14
+ * @returns Whether the credentials are valid.
23
15
  */
24
- validateCredentials: (method: AuthMethod, credentials: AuthCredential, ctx: Context) => boolean | Promise<boolean>;
16
+ validate: (username: string, password: string, ctx: Context) => boolean | Promise<boolean>;
25
17
  /**
26
- * 🔒 Function to dynamically determine the realm for authentication prompt.
27
- * @param ctx - The current request context.
28
- * @returns The authentication realm string.
18
+ * Realm name shown in the `WWW-Authenticate` header.
19
+ * Defaults to `"Restricted Area"`.
29
20
  */
30
- getRealm?: (ctx: Context) => string;
21
+ realm?: string;
31
22
  /**
32
- * Custom handler for unauthorized access.
23
+ * Custom handler for unauthorized requests.
24
+ *
33
25
  * @param ctx - The current request context.
34
- * @param error - Optional error information.
35
- * @returns A CallbackReturn to end the response.
26
+ * @param error - Optional error object describing why access was denied.
27
+ * @returns HttpBaseResponse to send to the client.
36
28
  */
37
29
  onUnauthorized?: (ctx: Context, error?: Error) => HttpBaseResponse;
38
- /**
39
- * 🚦 Rate-limiting configuration.
40
- * @requires getConnInfo middleware. for parse remote address.
41
- */
42
- rateLimit?: {
43
- /**
44
- * 🧠 Custom cache or storage for rate-limit tracking.
45
- */
46
- storage?: {
47
- get: (key: string) => {
48
- count: number;
49
- resetTime: number;
50
- } | undefined;
51
- set: (key: string, value: {
52
- count: number;
53
- resetTime: number;
54
- }) => void;
55
- clearExpired: () => void;
56
- };
57
- /** 🔁 Max requests allowed within the window */
58
- maxRequests: number;
59
- /** ⏲️ Duration of window in milliseconds */
60
- windowMs: number;
61
- };
62
- /**
63
- * 🛠 Supported authentication types.
64
- * @default ["basic"]
65
- */
66
- supportedMethods?: AuthMethod[];
67
- /**
68
- * 🧑‍⚖️ Optional RBAC (Role-Based Access Control) check.
69
- * @param ctx - The current request context.
70
- * @param credentials - The validated credentials.
71
- * @returns Whether access is allowed.
72
- */
73
- checkAccess?: (ctx: Context, credentials: AuthCredential) => boolean | Promise<boolean>;
74
30
  };
75
31
  /**
76
- * 🔐 Middleware for flexible authentication using Basic, API Key, or Bearer Token.
77
- * Supports rate limiting, IP filtering, and role-based access control.
32
+ * Basic Authentication Middleware
33
+ *
34
+ * Verifies that incoming requests contain a valid Basic Auth header
35
+ * (`Authorization: Basic <base64-credentials>`). Supports async
36
+ * validation and custom unauthorized handling.
37
+ *
38
+ * @param options - Configuration options for validation, realm, and error handling.
39
+ * @returns Middleware function to use in routes.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import basicAuth from "./middleware/basicAuth.js";
44
+ *
45
+ * const auth = basicAuth({
46
+ * validate: async (username, password) => username === "admin" && password === "1234",
47
+ * });
78
48
  *
79
- * @param options - Custom authentication handler options.
80
- * @returns A middleware function.
49
+ * app.use("/admin", auth, (ctx) => {
50
+ * ctx.json({ message: "Access granted" });
51
+ * });
52
+ * ```
81
53
  */
82
- declare const basicAuth: (options: DynamicBasicAuthOptions) => Middleware;
83
- export { basicAuth, basicAuth as default };
54
+ export declare const basicAuth: <T extends Record<string, any> = {}, Path extends string = any>(options: BasicAuthOptions) => Middleware<T, Path>;
55
+ export default basicAuth;
@@ -1,69 +1,43 @@
1
1
  import { Buffer } from "node:buffer";
2
- import { GlobalConfig } from "../core/config.js";
3
- import { colorText } from "../utils/colors.js";
4
- import { createRateLimitDefaultStorage, isRateLimit, } from "../utils/rateLimit.js";
5
- const basicAuth = (options) => {
6
- const { validateCredentials, getRealm = () => "Restricted Area", onUnauthorized = (ctx, error) => {
7
- const realm = getRealm(ctx);
2
+ export const basicAuth = (options) => {
3
+ const { validate, realm = "Restricted Area", onUnauthorized = (ctx, error) => {
8
4
  ctx.setStatus = 401;
9
5
  ctx.setHeader("WWW-Authenticate", `Basic realm="${realm}"`);
10
- ctx.body = { error: error?.message };
11
- }, rateLimit, supportedMethods = ["basic", "api-key", "bearer-token"], checkAccess, } = options;
12
- let storage = rateLimit?.storage;
13
- if (rateLimit && !rateLimit.storage) {
14
- storage = createRateLimitDefaultStorage();
15
- }
16
- return async function basicAuth(ctx, next) {
17
- let authMethod;
18
- let credentials = {};
6
+ return ctx.json({ error: error?.message || "Unauthorized" });
7
+ }, } = options;
8
+ return async (ctx, next) => {
19
9
  const auth = ctx.req.header("authorization");
20
- if (auth) {
21
- if (auth.startsWith("Basic ")) {
22
- authMethod = "basic";
23
- const base64Credentials = auth.split(" ")[1];
24
- const decoded = Buffer.from(base64Credentials, "base64").toString("utf-8");
25
- const [username, password] = decoded.split(":");
26
- credentials = { username, password };
27
- }
28
- else if (auth.startsWith("Bearer ")) {
29
- authMethod = "bearer-token";
30
- credentials = { token: auth.split(" ")[1] };
31
- }
10
+ if (!auth || !auth.startsWith("Basic ")) {
11
+ return onUnauthorized(ctx, new Error("Basic authentication required"));
32
12
  }
33
- else if (ctx.header("x-api-key")) {
34
- authMethod = "api-key";
35
- credentials = { apiKey: ctx.header("x-api-key") };
13
+ const base64 = auth.slice(6).trim();
14
+ if (!base64) {
15
+ return onUnauthorized(ctx, new Error("Empty credentials"));
36
16
  }
37
- if (!authMethod || !supportedMethods.includes(authMethod)) {
38
- GlobalConfig.debugging.error(`${colorText("[AUTH]", "bgRed")} Unsupported or missing authentication method.`);
39
- return onUnauthorized(ctx, new Error("Unsupported authentication method"));
17
+ let username, password;
18
+ try {
19
+ const decoded = Buffer.from(base64, "base64").toString("utf-8");
20
+ const idx = decoded.indexOf(":");
21
+ if (idx === -1)
22
+ throw new Error("Missing colon in credentials");
23
+ username = decoded.slice(0, idx);
24
+ password = decoded.slice(idx + 1);
40
25
  }
41
- if (rateLimit) {
42
- let key = `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`;
43
- const { check, entry } = isRateLimit(ctx, key, storage, rateLimit.maxRequests, rateLimit.windowMs);
44
- if (check) {
45
- const retryAfter = Math.ceil((entry.resetTime - Date.now()) / 1000);
46
- ctx.setHeader("Retry-After", retryAfter.toString());
47
- return onUnauthorized(ctx, new Error(`Rate limit exceeded. Retry after ${retryAfter} seconds.`));
48
- }
26
+ catch (err) {
27
+ return onUnauthorized(ctx, new Error("Invalid Basic auth format"));
49
28
  }
50
29
  try {
51
- const isValid = await validateCredentials(authMethod, credentials, ctx);
52
- if (!isValid) {
53
- throw new Error("Invalid credentials.");
54
- }
55
- if (checkAccess) {
56
- const hasAccess = await checkAccess(ctx, credentials);
57
- if (!hasAccess) {
58
- return onUnauthorized(ctx, new Error("Access denied."));
59
- }
30
+ const valid = await validate(username, password, ctx);
31
+ if (!valid) {
32
+ return onUnauthorized(ctx, new Error("Invalid username or password"));
60
33
  }
61
- return await next();
34
+ ctx.user = { username };
35
+ await next();
62
36
  }
63
- catch (error) {
64
- GlobalConfig.debugging.error(`${colorText("[AUTH]", "bgRed")} Failure for method: ${ctx.method}`);
37
+ catch (err) {
38
+ const error = err instanceof Error ? err : new Error(String(err));
65
39
  return onUnauthorized(ctx, error);
66
40
  }
67
41
  };
68
42
  };
69
- export { basicAuth, basicAuth as default };
43
+ export default basicAuth;
@@ -0,0 +1,52 @@
1
+ import { Context } from "../core/context.js";
2
+ import { HttpBaseResponse, Middleware } from "../types/index.js";
3
+ /**
4
+ * Options for Bearer Authentication middleware.
5
+ */
6
+ export type BearerAuthOptions = {
7
+ /**
8
+ * Function to validate the token.
9
+ * Can be synchronous or asynchronous.
10
+ * @param token - The Bearer token from the request.
11
+ * @param ctx - The current request context.
12
+ * @returns Boolean indicating if the token is valid.
13
+ */
14
+ validate: (token: string, ctx: Context) => boolean | Promise<boolean>;
15
+ /**
16
+ * Realm name shown in the `WWW-Authenticate` header.
17
+ * Defaults to `"API"`.
18
+ */
19
+ realm?: string;
20
+ /**
21
+ * Custom handler for unauthorized requests.
22
+ * @param ctx - The current request context.
23
+ * @param error - Optional error object describing the reason.
24
+ * @returns HttpBaseResponse to send to the client.
25
+ */
26
+ onUnauthorized?: (ctx: Context, error?: Error) => HttpBaseResponse;
27
+ };
28
+ /**
29
+ * Bearer Authentication Middleware
30
+ *
31
+ * Verifies that incoming requests contain a valid Bearer token
32
+ * in the `Authorization` header. Supports async token validation
33
+ * and custom error handling.
34
+ *
35
+ * @param options - Configuration options for token validation, realm, and error handling.
36
+ * @returns Middleware function to use in routes.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { bearerAuth } from "./middleware/bearerAuth.js";
41
+ *
42
+ * const auth = bearerAuth({
43
+ * validate: async (token) => token === "secret-token",
44
+ * });
45
+ *
46
+ * app.use("/api", auth, (ctx) => {
47
+ * ctx.json({ message: "Access granted" });
48
+ * });
49
+ * ```
50
+ */
51
+ export declare const bearerAuth: <T extends Record<string, any> = {}, Path extends string = any>(options: BearerAuthOptions) => Middleware<T, Path>;
52
+ export default bearerAuth;
@@ -0,0 +1,30 @@
1
+ export const bearerAuth = (options) => {
2
+ const { validate, realm = "API", onUnauthorized = (ctx, error) => {
3
+ ctx.setStatus = 401;
4
+ ctx.setHeader("WWW-Authenticate", `Bearer realm="${realm}"`);
5
+ return ctx.json({ error: error?.message || "Unauthorized" });
6
+ }, } = options;
7
+ return async (ctx, next) => {
8
+ const auth = ctx.req.header("authorization");
9
+ if (!auth || !auth.startsWith("Bearer ")) {
10
+ return onUnauthorized(ctx, new Error("Bearer token required"));
11
+ }
12
+ const token = auth.slice(7).trim();
13
+ if (!token) {
14
+ return onUnauthorized(ctx, new Error("Empty token"));
15
+ }
16
+ try {
17
+ const valid = await validate(token, ctx);
18
+ if (!valid) {
19
+ return onUnauthorized(ctx, new Error("Invalid or expired token"));
20
+ }
21
+ ctx.token = token;
22
+ await next();
23
+ }
24
+ catch (err) {
25
+ const error = err instanceof Error ? err : new Error(String(err));
26
+ return onUnauthorized(ctx, error);
27
+ }
28
+ };
29
+ };
30
+ export default bearerAuth;
@@ -1,11 +1,10 @@
1
- import { Ctx } from "../types/index.js";
1
+ import { Middleware } from "../types/index.js";
2
2
  export type CorsOptions = {
3
3
  /**
4
4
  * Allowed origins for CORS.
5
- * Can be a string, a RegExp, an array of strings or RegExps, or a function
6
- * that takes the request origin and returns a boolean indicating if it is allowed.
5
+ * Can be a string, an array of strings, or a function that returns a boolean.
7
6
  */
8
- origin?: string | RegExp | (string | RegExp)[] | ((reqOrigin: string) => boolean);
7
+ origin?: string | string[] | ((reqOrigin: string) => boolean);
9
8
  /**
10
9
  * Allowed HTTP methods for CORS requests.
11
10
  * Defaults to ['GET', 'POST', 'PUT', 'DELETE'].
@@ -21,31 +20,18 @@ export type CorsOptions = {
21
20
  */
22
21
  exposedHeaders?: string[];
23
22
  /**
24
- * Indicates whether the response to the request can be exposed
25
- * when the credentials flag is true.
23
+ * Indicates whether credentials are allowed.
26
24
  */
27
25
  credentials?: boolean;
28
26
  /**
29
- * Indicates how long the results of a preflight request
30
- * can be cached (in seconds).
27
+ * Preflight cache duration in seconds.
31
28
  */
32
29
  maxAge?: number;
33
30
  };
34
31
  /**
35
32
  * Middleware for handling Cross-Origin Resource Sharing (CORS).
36
33
  *
37
- * @param {CorsOptions} [option={}] - Configuration options for CORS.
38
- * @returns {Function} Middleware function compatible with the framework's middleware signature.
39
- *
40
- * @example
41
- * ```ts
42
- * app.use(cors({
43
- * origin: ["https://example.com", /https:\/\/.*\.example\.com/],
44
- * methods: ["GET", "POST"],
45
- * credentials: true,
46
- * maxAge: 600,
47
- * }));
48
- * ```
34
+ * @param option - Configuration options for CORS.
49
35
  */
50
- declare function cors(option?: CorsOptions): (ctx: Ctx, next: () => Promise<any>) => Promise<any>;
36
+ declare function cors<T extends Record<string, any> = {}, Path extends string = any>(option?: CorsOptions): Middleware<T, Path>;
51
37
  export { cors, cors as default };
@@ -1,45 +1,35 @@
1
1
  function cors(option = {}) {
2
- const { methods, allowedHeaders, credentials, exposedHeaders, maxAge, origin, } = option;
2
+ const { credentials, maxAge, origin, } = option;
3
+ let methods = (option.methods || ["GET", "POST", "PUT", "DELETE"]).join(", ");
4
+ let allowedHeaders = (option.allowedHeaders || ["Content-Type", "Authorization"]).join(", ");
5
+ let exposedHeaders = option?.exposedHeaders?.join(", ");
3
6
  return async function cors(ctx, next) {
4
7
  const reqOrigin = ctx.req.header("origin") || "";
5
8
  let allowOrigin = "*";
6
9
  if (typeof origin === "string") {
7
10
  allowOrigin = origin;
8
11
  }
9
- else if (origin instanceof RegExp) {
10
- allowOrigin = origin.test(reqOrigin) ? reqOrigin : "";
11
- }
12
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 : "";
13
+ allowOrigin = origin.includes(reqOrigin) ? reqOrigin : "";
22
14
  }
23
15
  else if (typeof origin === "function") {
24
16
  allowOrigin = origin(reqOrigin) ? reqOrigin : "";
25
17
  }
26
- ctx.setHeader("Access-Control-Allow-Origin", allowOrigin);
27
- ctx.setHeader("Access-Control-Allow-Methods", (methods || ["GET", "POST", "PUT", "DELETE"]).join(", "));
28
- ctx.setHeader("Access-Control-Allow-Headers", (allowedHeaders || ["Content-Type", "Authorization"]).join(", "));
18
+ ctx.headers.set("Access-Control-Allow-Origin", allowOrigin);
19
+ ctx.headers.set("Access-Control-Allow-Methods", methods);
20
+ ctx.headers.set("Access-Control-Allow-Headers", allowedHeaders);
29
21
  if (exposedHeaders) {
30
- ctx.setHeader("Access-Control-Expose-Headers", exposedHeaders.join(", "));
22
+ ctx.headers.set("Access-Control-Expose-Headers", exposedHeaders);
31
23
  }
32
24
  if (credentials) {
33
- ctx.setHeader("Access-Control-Allow-Credentials", "true");
25
+ ctx.headers.set("Access-Control-Allow-Credentials", "true");
34
26
  }
35
27
  if (maxAge) {
36
- ctx.setHeader("Access-Control-Max-Age", maxAge.toString());
28
+ ctx.headers.set("Access-Control-Max-Age", maxAge.toString());
37
29
  }
38
- if (ctx.req.method === "OPTIONS") {
39
- return new Response(null, {
40
- status: 204,
41
- headers: ctx.header(),
42
- });
30
+ if (ctx.method === "OPTIONS") {
31
+ ctx.setStatus = 204;
32
+ return;
43
33
  }
44
34
  return await next();
45
35
  };
@@ -0,0 +1,9 @@
1
+ export * from "./basic-auth.js";
2
+ export * from "./bearer-auth.js";
3
+ export * from "./cors.js";
4
+ export * from "./logger.js";
5
+ export * from "./pagination.js";
6
+ export * from "./powered-by.js";
7
+ export * from "./request-id.js";
8
+ export * from "./sanitize-headers.js";
9
+ export * from "./xss-protection.js";
@@ -0,0 +1,9 @@
1
+ export * from "./basic-auth.js";
2
+ export * from "./bearer-auth.js";
3
+ export * from "./cors.js";
4
+ export * from "./logger.js";
5
+ export * from "./pagination.js";
6
+ export * from "./powered-by.js";
7
+ export * from "./request-id.js";
8
+ export * from "./sanitize-headers.js";
9
+ export * from "./xss-protection.js";
@@ -12,5 +12,7 @@ import { Middleware } from "../types/index.js";
12
12
  * app.use(logger());
13
13
  * ```
14
14
  */
15
- declare function logger(): Middleware;
15
+ declare function logger(options?: {
16
+ enabled: boolean;
17
+ }): Middleware;
16
18
  export { logger, logger as default };
@@ -1,13 +1,16 @@
1
1
  import { colorText } from "../utils/colors.js";
2
- function logger() {
2
+ function logger(options = { enabled: true }) {
3
3
  return async function logger(ctx, next) {
4
4
  try {
5
+ if (!options?.enabled) {
6
+ return next();
7
+ }
5
8
  console.log(`${colorText("<--", "bold")} ${colorText(ctx.method, "bgMagenta")} ${ctx.pathname}`);
6
9
  const startTime = performance.now();
7
- let n = await next();
10
+ let n = (await next());
8
11
  const elapsed = performance.now() - startTime;
9
12
  console.log(`${colorText("-->", "bold")} ${colorText(ctx.method, "bgBlue")} ${ctx.pathname} ` +
10
- `${colorText(ctx.getStatus, "yellow")} ${colorText(`${elapsed.toFixed(2)}ms`, "magenta")}`);
13
+ `${colorText(n ? ctx.getStatus : 404, "yellow")} ${colorText(`${elapsed.toFixed(2)}ms`, "magenta")}`);
11
14
  return n;
12
15
  }
13
16
  catch (err) {
@@ -1,5 +1,5 @@
1
- import { Context, Middleware } from "../index.js";
2
- export type PaginationOptions = {
1
+ import { Context } from "../index.js";
2
+ export type PaginationOptions<DataKey extends string = "data", CountKey extends string = "total", Item = any> = {
3
3
  /**
4
4
  * 🔢 Default page number when not specified
5
5
  * @default 1
@@ -35,13 +35,13 @@ export type PaginationOptions = {
35
35
  * @default "total"
36
36
  * @example "totalCount" // Read from response.totalCount
37
37
  */
38
- countKey?: string;
38
+ countKey?: CountKey;
39
39
  /**
40
40
  * 📦 Key containing the data array in response
41
41
  * @default "data"
42
42
  * @example "items" // Process response.items array
43
43
  */
44
- dataKey?: string;
44
+ dataKey?: DataKey;
45
45
  /**
46
46
  * 🛠️ Function to fetch data dynamically
47
47
  * @param ctx - Request context
@@ -57,7 +57,9 @@ export type PaginationOptions = {
57
57
  limit: number;
58
58
  offset: number;
59
59
  }) => Promise<{
60
- [key: string]: any;
60
+ [K in DataKey]: Item[];
61
+ } & {
62
+ [K in CountKey]: number;
61
63
  }>;
62
64
  };
63
65
  export type PaginationBodyType = {
@@ -100,5 +102,5 @@ export type PaginationBodyType = {
100
102
  * }
101
103
  * }));
102
104
  */
103
- declare const paginationHandler: (options?: PaginationOptions) => Middleware;
105
+ declare const paginationHandler: <DataKey extends string = "data", CountKey extends string = "total", Item = any>(options?: PaginationOptions<DataKey, CountKey, Item>) => any;
104
106
  export { paginationHandler, paginationHandler as default };
@@ -1,5 +1,5 @@
1
1
  const paginationHandler = (options = {}) => {
2
- const { defaultPage = 1, defaultLimit = 10, maxLimit = 100, queryKeyPage = "page", queryKeyLimit = "limit", countKey = "total", dataKey = "data", getDataSource, } = options;
2
+ let { defaultPage = 1, defaultLimit = 10, maxLimit = 100, queryKeyPage = "page", queryKeyLimit = "limit", countKey = "total", dataKey = "data", getDataSource, } = options;
3
3
  return async function paginationHandler(ctx, next) {
4
4
  const rawPage = ctx.req.query[queryKeyPage];
5
5
  const rawLimit = ctx.req.query[queryKeyLimit];
@@ -1,6 +1,6 @@
1
1
  const poweredBy = (serverName) => {
2
2
  return function poweredBy(ctx, next) {
3
- ctx.setHeader("x-powered-by", serverName || "TezX");
3
+ ctx.headers.set("x-powered-by", serverName || "TezX");
4
4
  return next();
5
5
  };
6
6
  };
@@ -20,10 +20,6 @@ export type RateLimiterOptions = {
20
20
  * keyGenerator: (ctx) => ctx.user?.id || ctx.ip // Use user ID if authenticated
21
21
  */
22
22
  keyGenerator?: (ctx: Context) => string;
23
- /**
24
- // * ⚠️ (Future) Storage backend - currently memory only
25
- // * @todo Implement Redis storage
26
- // */
27
23
  /**
28
24
  * 🔄 Custom cache storage implementation (e.g., using `Map`, `Redis`, etc.).
29
25
  * By default, it uses a `Map<string, { count: number; resetTime: number }>`.
@@ -1,20 +1,33 @@
1
+ import { TezXError } from "../core/error.js";
1
2
  import { createRateLimitDefaultStorage, isRateLimit, } from "../utils/rateLimit.js";
2
3
  const rateLimiter = (options) => {
3
- const { maxRequests, windowMs, keyGenerator = (ctx) => `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`, storage = createRateLimitDefaultStorage(), onError = (ctx, retryAfter, error) => {
4
+ const { maxRequests, windowMs, keyGenerator = (ctx) => {
5
+ const xForwardedFor = ctx.req.header("x-forwarded-for");
6
+ if (xForwardedFor) {
7
+ const ip = xForwardedFor.split(",")[0].trim();
8
+ return ip;
9
+ }
10
+ const clientIp = ctx.req.header("client-ip");
11
+ if (clientIp)
12
+ return clientIp;
13
+ const addr = ctx.req.remoteAddress?.address || "unknown";
14
+ const port = ctx.req.remoteAddress?.port || "0";
15
+ return `${addr}:${port}`;
16
+ }, storage = createRateLimitDefaultStorage(), onError = (ctx, retryAfter, error) => {
4
17
  ctx.setStatus = 429;
5
- throw new Error(`Rate limit exceeded. Try again in ${retryAfter} seconds.`);
18
+ throw new TezXError(`Rate limit exceeded. Try again in ${retryAfter} seconds.`, 429);
6
19
  }, } = options;
7
20
  return async function rateLimiter(ctx, next) {
8
21
  const key = keyGenerator(ctx);
9
- const { check, entry } = isRateLimit(ctx, key, storage, maxRequests, windowMs);
22
+ const { check, entry } = isRateLimit(key, storage, maxRequests, windowMs);
10
23
  if (check) {
11
24
  const retryAfter = Math.ceil((entry.resetTime - Date.now()) / 1000);
12
- ctx.setHeader("Retry-After", retryAfter.toString());
25
+ ctx.headers.set("Retry-After", retryAfter.toString());
13
26
  return onError(ctx, retryAfter, new Error(`Rate limit exceeded. Retry after ${retryAfter} seconds.`));
14
27
  }
15
- ctx.setHeader("X-RateLimit-Limit", maxRequests.toString());
16
- ctx.setHeader("X-RateLimit-Remaining", (maxRequests - entry.count).toString());
17
- ctx.setHeader("X-RateLimit-Reset", entry.resetTime.toString());
28
+ ctx.headers.set("X-RateLimit-Limit", maxRequests.toString());
29
+ ctx.headers.set("X-RateLimit-Remaining", (maxRequests - entry.count).toString());
30
+ ctx.headers.set("X-RateLimit-Reset", entry.resetTime.toString());
18
31
  return await next();
19
32
  };
20
33
  };
@@ -14,4 +14,4 @@ import { Middleware } from "../types/index.js";
14
14
  * ```
15
15
  */
16
16
  declare const requestID: (headerName?: string, contextKey?: string) => Middleware;
17
- export { requestID, requestID as default };
17
+ export { requestID as default, requestID };
@@ -1,13 +1,10 @@
1
1
  import { generateUUID } from "../helper/index.js";
2
2
  const requestID = (headerName = "X-Request-ID", contextKey = "requestID") => {
3
3
  return function requestID(ctx, next) {
4
- let requestId = ctx.header(headerName);
5
- if (!requestId) {
6
- requestId = `req-${generateUUID()}`;
7
- }
4
+ let requestId = ctx.headers.get(headerName) ?? `req-${generateUUID()}`;
8
5
  ctx[contextKey] = requestId;
9
- ctx.setHeader(headerName, requestId);
6
+ ctx.headers.set(headerName, requestId);
10
7
  return next();
11
8
  };
12
9
  };
13
- export { requestID, requestID as default };
10
+ export { requestID as default, requestID };