tezx 2.0.11 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -89
- package/bun/getConnInfo.d.ts +21 -0
- package/bun/getConnInfo.js +9 -0
- package/bun/index.d.ts +10 -4
- package/bun/index.js +8 -4
- package/bun/ws.d.ts +48 -0
- package/bun/ws.js +58 -0
- package/cjs/bun/getConnInfo.js +12 -0
- package/cjs/bun/index.js +35 -7
- package/cjs/bun/ws.js +63 -0
- package/cjs/core/config.js +2 -12
- package/cjs/core/context.js +131 -379
- package/cjs/core/error.js +49 -0
- package/cjs/core/request.js +79 -131
- package/cjs/core/router.js +54 -387
- package/cjs/core/server.js +83 -202
- package/cjs/deno/env.js +4 -4
- package/cjs/deno/getConnInfo.js +18 -0
- package/cjs/deno/index.js +11 -18
- package/cjs/deno/serveStatic.js +53 -0
- package/cjs/deno/ws.js +39 -0
- package/cjs/helper/index.js +46 -10
- package/cjs/index.js +5 -7
- package/cjs/jwt/node.js +94 -0
- package/cjs/jwt/web.js +178 -0
- package/cjs/middleware/basic-auth.js +42 -0
- package/cjs/middleware/bearer-auth.js +34 -0
- package/cjs/middleware/cache-control.js +44 -0
- package/cjs/middleware/cors.js +11 -21
- package/cjs/middleware/detect-bot.js +57 -0
- package/cjs/middleware/i18n.js +73 -60
- package/cjs/middleware/index.js +8 -46
- package/cjs/middleware/logger.js +9 -4
- package/cjs/middleware/pagination.js +3 -2
- package/cjs/middleware/powered-by.js +3 -2
- package/cjs/middleware/rate-limiter.js +38 -0
- package/cjs/middleware/request-id.js +4 -5
- package/cjs/middleware/sanitize-headers.js +22 -0
- package/cjs/middleware/secure-headers copy.js +143 -0
- package/cjs/middleware/secure-headers.js +157 -0
- package/cjs/middleware/{xssProtection.js → xss-protection.js} +5 -8
- package/cjs/node/env.js +7 -7
- package/cjs/node/getConnInfo.js +16 -0
- package/cjs/node/index.js +17 -18
- package/cjs/node/mount-node.js +59 -0
- package/cjs/node/serveStatic.js +56 -0
- package/cjs/node/toWebRequest.js +25 -0
- package/cjs/node/ws.js +82 -0
- package/cjs/registry/RadixRouter.js +148 -0
- package/cjs/registry/index.js +17 -0
- package/cjs/types/headers.js +2 -0
- package/cjs/types/index.js +13 -0
- package/cjs/utils/buffer.js +17 -0
- package/cjs/utils/colors.js +2 -0
- package/cjs/utils/cookie.js +59 -0
- package/cjs/utils/file.js +136 -0
- package/cjs/utils/formData.js +60 -10
- package/cjs/utils/generateID.js +37 -0
- package/cjs/utils/low-level.js +115 -0
- package/cjs/utils/{staticFile.js → mimeTypes.js} +0 -87
- package/cjs/utils/rateLimit.js +41 -0
- package/cjs/utils/response.js +65 -0
- package/cjs/{core/environment.js → utils/runtime.js} +2 -1
- package/cjs/utils/url.js +65 -30
- package/core/config.d.ts +2 -7
- package/core/config.js +2 -12
- package/core/context.d.ts +209 -164
- package/core/context.js +131 -346
- package/core/error.d.ts +96 -0
- package/core/error.js +44 -0
- package/core/request.d.ts +67 -107
- package/core/request.js +78 -130
- package/core/router.d.ts +138 -133
- package/core/router.js +53 -352
- package/core/server.d.ts +99 -38
- package/core/server.js +83 -202
- package/deno/env.js +3 -3
- package/deno/getConnInfo.d.ts +21 -0
- package/deno/getConnInfo.js +15 -0
- package/deno/index.d.ts +9 -4
- package/deno/index.js +7 -4
- package/deno/serveStatic.d.ts +28 -0
- package/deno/serveStatic.js +49 -0
- package/deno/ws.d.ts +42 -0
- package/deno/ws.js +36 -0
- package/helper/index.d.ts +29 -15
- package/helper/index.js +27 -7
- package/index.d.ts +10 -8
- package/index.js +4 -5
- package/jwt/node.d.ts +39 -0
- package/jwt/node.js +87 -0
- package/jwt/web.d.ts +14 -0
- package/jwt/web.js +174 -0
- package/middleware/basic-auth.d.ts +56 -0
- package/middleware/basic-auth.js +38 -0
- package/middleware/bearer-auth.d.ts +53 -0
- package/middleware/bearer-auth.js +30 -0
- package/middleware/cache-control.d.ts +30 -0
- package/middleware/cache-control.js +40 -0
- package/middleware/cors.d.ts +30 -3
- package/middleware/cors.js +12 -22
- package/middleware/detect-bot.d.ts +113 -0
- package/middleware/detect-bot.js +53 -0
- package/middleware/i18n.d.ts +166 -73
- package/middleware/i18n.js +73 -60
- package/middleware/index.d.ts +8 -32
- package/middleware/index.js +8 -44
- package/middleware/logger.d.ts +5 -2
- package/middleware/logger.js +9 -4
- package/middleware/pagination.d.ts +9 -6
- package/middleware/pagination.js +3 -2
- package/middleware/powered-by.d.ts +2 -1
- package/middleware/powered-by.js +3 -2
- package/middleware/{rateLimiter.d.ts → rate-limiter.d.ts} +15 -9
- package/middleware/rate-limiter.js +34 -0
- package/middleware/request-id.d.ts +2 -1
- package/middleware/request-id.js +5 -6
- package/middleware/{sanitizeHeader.d.ts → sanitize-headers.d.ts} +5 -19
- package/middleware/sanitize-headers.js +18 -0
- package/middleware/secure-headers copy.d.ts +15 -0
- package/middleware/secure-headers copy.js +136 -0
- package/middleware/secure-headers.d.ts +132 -0
- package/middleware/secure-headers.js +153 -0
- package/middleware/{xssProtection.d.ts → xss-protection.d.ts} +2 -1
- package/middleware/xss-protection.js +19 -0
- package/node/env.js +4 -4
- package/node/getConnInfo.d.ts +21 -0
- package/node/getConnInfo.js +13 -0
- package/node/index.d.ts +13 -4
- package/node/index.js +11 -4
- package/node/mount-node.d.ts +11 -0
- package/node/mount-node.js +56 -0
- package/node/serveStatic.d.ts +36 -0
- package/node/serveStatic.js +52 -0
- package/node/toWebRequest.js +22 -0
- package/node/ws.d.ts +56 -0
- package/node/ws.js +46 -0
- package/package.json +39 -30
- package/registry/RadixRouter.d.ts +40 -0
- package/registry/RadixRouter.js +144 -0
- package/registry/index.d.ts +2 -0
- package/registry/index.js +1 -0
- package/types/headers.d.ts +2 -0
- package/types/headers.js +1 -0
- package/types/index.d.ts +318 -18
- package/types/index.js +12 -1
- package/utils/buffer.d.ts +1 -0
- package/utils/buffer.js +14 -0
- package/utils/colors.d.ts +24 -0
- package/utils/colors.js +2 -0
- package/utils/cookie.d.ts +55 -0
- package/utils/cookie.js +53 -0
- package/utils/file.d.ts +38 -0
- package/utils/file.js +96 -0
- package/utils/formData.d.ts +41 -1
- package/utils/formData.js +58 -9
- package/utils/generateID.d.ts +42 -0
- package/utils/generateID.js +32 -0
- package/utils/httpStatusMap.d.ts +14 -0
- package/utils/low-level.d.ts +58 -0
- package/utils/low-level.js +108 -0
- package/utils/mimeTypes.d.ts +4 -0
- package/utils/{staticFile.js → mimeTypes.js} +0 -53
- package/utils/rateLimit.d.ts +18 -0
- package/utils/rateLimit.js +37 -0
- package/utils/response.d.ts +18 -0
- package/utils/response.js +58 -0
- package/{core/environment.d.ts → utils/runtime.d.ts} +1 -0
- package/{core/environment.js → utils/runtime.js} +1 -0
- package/utils/url.d.ts +42 -14
- package/utils/url.js +61 -27
- package/bun/adapter.d.ts +0 -127
- package/bun/adapter.js +0 -97
- package/cjs/bun/adapter.js +0 -100
- package/cjs/core/MiddlewareConfigure.js +0 -68
- package/cjs/core/common.js +0 -15
- package/cjs/deno/adpater.js +0 -67
- package/cjs/helper/common.js +0 -17
- package/cjs/middleware/basicAuth.js +0 -71
- package/cjs/middleware/cacheControl.js +0 -90
- package/cjs/middleware/detectBot.js +0 -104
- package/cjs/middleware/detectLocale.js +0 -43
- package/cjs/middleware/lazyLoadModules.js +0 -73
- package/cjs/middleware/rateLimiter.js +0 -24
- package/cjs/middleware/requestTimeout.js +0 -42
- package/cjs/middleware/sanitizeHeader.js +0 -51
- package/cjs/middleware/secureHeaders.js +0 -42
- package/cjs/node/adapter.js +0 -138
- package/cjs/utils/regexRouter.js +0 -58
- package/cjs/utils/state.js +0 -34
- package/cjs/utils/toWebRequest.js +0 -35
- package/cjs/ws/deno.js +0 -20
- package/cjs/ws/index.js +0 -53
- package/cjs/ws/node.js +0 -65
- package/core/MiddlewareConfigure.d.ts +0 -15
- package/core/MiddlewareConfigure.js +0 -63
- package/core/common.d.ts +0 -21
- package/core/common.js +0 -11
- package/deno/adpater.d.ts +0 -38
- package/deno/adpater.js +0 -64
- package/helper/common.d.ts +0 -5
- package/helper/common.js +0 -14
- package/middleware/basicAuth.d.ts +0 -81
- package/middleware/basicAuth.js +0 -67
- package/middleware/cacheControl.d.ts +0 -48
- package/middleware/cacheControl.js +0 -53
- package/middleware/detectBot.d.ts +0 -121
- package/middleware/detectBot.js +0 -98
- package/middleware/detectLocale.d.ts +0 -55
- package/middleware/detectLocale.js +0 -39
- package/middleware/lazyLoadModules.d.ts +0 -72
- package/middleware/lazyLoadModules.js +0 -69
- package/middleware/rateLimiter.js +0 -20
- package/middleware/requestTimeout.d.ts +0 -25
- package/middleware/requestTimeout.js +0 -38
- package/middleware/sanitizeHeader.js +0 -47
- package/middleware/secureHeaders.d.ts +0 -78
- package/middleware/secureHeaders.js +0 -38
- package/middleware/xssProtection.js +0 -22
- package/node/adapter.d.ts +0 -46
- package/node/adapter.js +0 -102
- package/utils/regexRouter.d.ts +0 -66
- package/utils/regexRouter.js +0 -53
- package/utils/state.d.ts +0 -50
- package/utils/state.js +0 -30
- package/utils/staticFile.d.ts +0 -10
- package/utils/toWebRequest.js +0 -32
- package/ws/deno.d.ts +0 -6
- package/ws/deno.js +0 -16
- package/ws/index.d.ts +0 -180
- package/ws/index.js +0 -50
- package/ws/node.d.ts +0 -7
- package/ws/node.js +0 -28
- /package/{utils → node}/toWebRequest.d.ts +0 -0
package/middleware/i18n.js
CHANGED
|
@@ -1,75 +1,88 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const { loadTranslations, defaultCacheDuration = 3600000,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { TezXError } from "../index.js";
|
|
2
|
+
const i18n = (options) => {
|
|
3
|
+
const { loadTranslations, defaultCacheDuration = 3600000, detectLanguage, defaultLanguage = "en", translationFunctionKey = "t", formatMessage = (msg, vars = {}) => {
|
|
4
|
+
if (vars && msg.indexOf("{{") !== -1) {
|
|
5
|
+
let out = "";
|
|
6
|
+
let i = 0;
|
|
7
|
+
while (i < msg.length) {
|
|
8
|
+
const startVar = msg.indexOf("{{", i);
|
|
9
|
+
if (startVar === -1) {
|
|
10
|
+
out += msg.slice(i);
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
const endVar = msg.indexOf("}}", startVar + 2);
|
|
14
|
+
if (endVar === -1) {
|
|
15
|
+
out += msg.slice(i);
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
out += msg.slice(i, startVar);
|
|
19
|
+
const keyVar = msg.slice(startVar + 2, endVar).trim();
|
|
20
|
+
out += vars[keyVar] !== undefined ? String(vars[keyVar]) : "";
|
|
21
|
+
i = endVar + 2;
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
return msg;
|
|
26
|
+
}, isCacheValid = (cached) => cached.expiresAt > Date.now(), cacheTranslations = false, cacheStorage: externalCache = null, } = options;
|
|
27
|
+
let localCache = null;
|
|
28
|
+
if (cacheTranslations && !externalCache)
|
|
29
|
+
localCache = new Map();
|
|
11
30
|
return async function i18n(ctx, next) {
|
|
12
31
|
try {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let translations = null;
|
|
20
|
-
let selectedLanguage = defaultLanguage;
|
|
21
|
-
for (const lang of languageChain) {
|
|
22
|
-
const normalizedLang = lang.split("-")[0].toLowerCase();
|
|
23
|
-
if (cacheTranslations && translationCache[normalizedLang]) {
|
|
24
|
-
const cached = translationCache[normalizedLang];
|
|
25
|
-
if (isCacheValid(cached, normalizedLang)) {
|
|
32
|
+
const lang = detectLanguage(ctx) ?? defaultLanguage;
|
|
33
|
+
let translations = undefined;
|
|
34
|
+
if (cacheTranslations) {
|
|
35
|
+
if (externalCache) {
|
|
36
|
+
const cached = await externalCache.get(lang);
|
|
37
|
+
if (cached && isCacheValid(cached, lang))
|
|
26
38
|
translations = cached.translations;
|
|
27
|
-
selectedLanguage = lang;
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
delete translationCache[normalizedLang];
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
const { translations: loadedTranslations, expiresAt } = await loadTranslations(normalizedLang);
|
|
34
|
-
let expirationTime = Date.now() + defaultCacheDuration;
|
|
35
|
-
if (expiresAt instanceof Date) {
|
|
36
|
-
expirationTime = expiresAt.getTime();
|
|
37
|
-
}
|
|
38
|
-
else if (typeof expiresAt === "number") {
|
|
39
|
-
expirationTime = expiresAt;
|
|
40
|
-
}
|
|
41
|
-
translations = loadedTranslations;
|
|
42
|
-
selectedLanguage = lang;
|
|
43
|
-
if (cacheTranslations) {
|
|
44
|
-
translationCache[normalizedLang] = {
|
|
45
|
-
translations,
|
|
46
|
-
expiresAt: expirationTime,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
break;
|
|
50
39
|
}
|
|
51
|
-
|
|
52
|
-
|
|
40
|
+
else if (localCache?.get(lang)) {
|
|
41
|
+
const cached = localCache.get(lang);
|
|
42
|
+
if (isCacheValid(cached, lang))
|
|
43
|
+
translations = cached.translations;
|
|
44
|
+
else
|
|
45
|
+
localCache.delete(lang);
|
|
53
46
|
}
|
|
54
47
|
}
|
|
55
48
|
if (!translations) {
|
|
56
|
-
|
|
49
|
+
translations = await loadTranslations(lang);
|
|
50
|
+
const expiresAt = Date.now() + defaultCacheDuration;
|
|
51
|
+
if (cacheTranslations) {
|
|
52
|
+
if (externalCache) {
|
|
53
|
+
await externalCache.set(lang, { translations, expiresAt });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
localCache?.set(lang, { translations, expiresAt });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
57
59
|
}
|
|
58
|
-
ctx[translationFunctionKey] = (key,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
ctx[translationFunctionKey] = function translate(key, vars) {
|
|
61
|
+
let acc = translations;
|
|
62
|
+
let start = 0;
|
|
63
|
+
for (let i = 0; i <= key.length; i++) {
|
|
64
|
+
const c = key.charCodeAt(i);
|
|
65
|
+
if (c === 46 || i === key.length) {
|
|
66
|
+
const segment = key.slice(start, i);
|
|
67
|
+
if (acc && typeof acc === "object") {
|
|
68
|
+
acc = acc[segment];
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
acc = undefined;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
start = i + 1;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
let msg = typeof acc === "string" ? acc : key;
|
|
78
|
+
return formatMessage(msg, vars);
|
|
64
79
|
};
|
|
65
|
-
ctx.language =
|
|
66
|
-
ctx.languageChain = languageChain;
|
|
80
|
+
ctx.language = lang;
|
|
67
81
|
return await next();
|
|
68
82
|
}
|
|
69
83
|
catch (error) {
|
|
70
|
-
|
|
71
|
-
ctx.setStatus = 500;
|
|
72
|
-
throw error;
|
|
84
|
+
throw new TezXError(error?.message ?? "i18n failed", 500, error?.stack);
|
|
73
85
|
}
|
|
74
86
|
};
|
|
75
87
|
};
|
|
88
|
+
export { i18n as default, i18n };
|
package/middleware/index.d.ts
CHANGED
|
@@ -1,38 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from "./cacheControl.js";
|
|
1
|
+
export * from "./basic-auth.js";
|
|
2
|
+
export * from "./bearer-auth.js";
|
|
3
|
+
export * from "./cache-control.js";
|
|
5
4
|
export * from "./cors.js";
|
|
6
|
-
export
|
|
7
|
-
export type { DetectBotReason } from "./detectBot.js";
|
|
8
|
-
export * from "./detectLocale.js";
|
|
5
|
+
export * from "./detect-bot.js";
|
|
9
6
|
export * from "./i18n.js";
|
|
10
|
-
export * from "./lazyLoadModules.js";
|
|
11
7
|
export * from "./logger.js";
|
|
12
8
|
export * from "./pagination.js";
|
|
13
9
|
export * from "./powered-by.js";
|
|
14
|
-
export * from "./
|
|
10
|
+
export * from "./rate-limiter.js";
|
|
15
11
|
export * from "./request-id.js";
|
|
16
|
-
export * from "./
|
|
17
|
-
export * from "./
|
|
18
|
-
export * from "./
|
|
19
|
-
export * from "./xssProtection.js";
|
|
20
|
-
declare const _default: {
|
|
21
|
-
basicAuth: (options: import("./basicAuth.js").DynamicBasicAuthOptions) => import("../index.js").Middleware;
|
|
22
|
-
cacheControl: (options: import("./cacheControl.js").CacheOptions) => import("../index.js").Middleware;
|
|
23
|
-
cors: typeof cors;
|
|
24
|
-
detectBot: (options?: import("./detectBot.js").DetectBotOptions) => import("../index.js").Middleware;
|
|
25
|
-
detectLocale: (options: import("./detectLocale.js").DetectLocaleOptions) => import("../index.js").Middleware;
|
|
26
|
-
i18n: (options: import("./i18n.js").I18nOptions) => import("../index.js").Middleware;
|
|
27
|
-
lazyLoadModules: <T = any>(options: import("./lazyLoadModules.js").LazyLoadOptions<T>) => import("../index.js").Middleware;
|
|
28
|
-
logger: typeof logger;
|
|
29
|
-
paginationHandler: (options?: import("./pagination.js").PaginationOptions) => import("../index.js").Middleware;
|
|
30
|
-
poweredBy: (serverName?: string) => import("../index.js").Middleware;
|
|
31
|
-
rateLimiter: (options: import("./rateLimiter.js").RateLimiterOptions) => import("../index.js").Middleware;
|
|
32
|
-
requestID: (headerName?: string, contextKey?: string) => import("../index.js").Middleware;
|
|
33
|
-
requestTimeout: (options: import("./requestTimeout.js").TimeoutOptions) => import("../index.js").Middleware;
|
|
34
|
-
sanitizeHeaders: (options?: import("./sanitizeHeader.js").SanitizeHeadersOptions) => import("../index.js").Middleware;
|
|
35
|
-
secureHeaders: (options?: import("./secureHeaders.js").SecurityHeaderOptions) => import("../index.js").Middleware;
|
|
36
|
-
xssProtection: (options?: import("./xssProtection.js").XSSProtectionOptions) => (ctx: import("../index.js").BaseContext, next: import("../index.js").NextCallback) => Promise<any>;
|
|
37
|
-
};
|
|
38
|
-
export default _default;
|
|
12
|
+
export * from "./sanitize-headers.js";
|
|
13
|
+
export * from "./secure-headers.js";
|
|
14
|
+
export * from "./xss-protection.js";
|
package/middleware/index.js
CHANGED
|
@@ -1,50 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { detectBot } from "./detectBot.js";
|
|
5
|
-
import { detectLocale } from "./detectLocale.js";
|
|
6
|
-
import { i18n } from "./i18n.js";
|
|
7
|
-
import { lazyLoadModules } from "./lazyLoadModules.js";
|
|
8
|
-
import { logger } from "./logger.js";
|
|
9
|
-
import { paginationHandler } from "./pagination.js";
|
|
10
|
-
import { poweredBy } from "./powered-by.js";
|
|
11
|
-
import { rateLimiter } from "./rateLimiter.js";
|
|
12
|
-
import { requestID } from "./request-id.js";
|
|
13
|
-
import { requestTimeout } from "./requestTimeout.js";
|
|
14
|
-
import { sanitizeHeaders } from "./sanitizeHeader.js";
|
|
15
|
-
import { secureHeaders } from "./secureHeaders.js";
|
|
16
|
-
import { xssProtection } from "./xssProtection.js";
|
|
17
|
-
export * from "./basicAuth.js";
|
|
18
|
-
export * from "./cacheControl.js";
|
|
1
|
+
export * from "./basic-auth.js";
|
|
2
|
+
export * from "./bearer-auth.js";
|
|
3
|
+
export * from "./cache-control.js";
|
|
19
4
|
export * from "./cors.js";
|
|
20
|
-
export
|
|
21
|
-
export * from "./detectLocale.js";
|
|
5
|
+
export * from "./detect-bot.js";
|
|
22
6
|
export * from "./i18n.js";
|
|
23
|
-
export * from "./lazyLoadModules.js";
|
|
24
7
|
export * from "./logger.js";
|
|
25
8
|
export * from "./pagination.js";
|
|
26
9
|
export * from "./powered-by.js";
|
|
27
|
-
export * from "./
|
|
10
|
+
export * from "./rate-limiter.js";
|
|
28
11
|
export * from "./request-id.js";
|
|
29
|
-
export * from "./
|
|
30
|
-
export * from "./
|
|
31
|
-
export * from "./
|
|
32
|
-
export * from "./xssProtection.js";
|
|
33
|
-
export default {
|
|
34
|
-
basicAuth,
|
|
35
|
-
cacheControl,
|
|
36
|
-
cors,
|
|
37
|
-
detectBot,
|
|
38
|
-
detectLocale,
|
|
39
|
-
i18n,
|
|
40
|
-
lazyLoadModules,
|
|
41
|
-
logger,
|
|
42
|
-
paginationHandler,
|
|
43
|
-
poweredBy,
|
|
44
|
-
rateLimiter,
|
|
45
|
-
requestID,
|
|
46
|
-
requestTimeout,
|
|
47
|
-
sanitizeHeaders,
|
|
48
|
-
secureHeaders,
|
|
49
|
-
xssProtection,
|
|
50
|
-
};
|
|
12
|
+
export * from "./sanitize-headers.js";
|
|
13
|
+
export * from "./secure-headers.js";
|
|
14
|
+
export * from "./xss-protection.js";
|
package/middleware/logger.d.ts
CHANGED
|
@@ -7,9 +7,12 @@ import { Middleware } from "../types/index.js";
|
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```ts
|
|
10
|
-
* import { logger } from 'tezx';
|
|
10
|
+
* import { logger } from 'tezx/logger';
|
|
11
11
|
*
|
|
12
12
|
* app.use(logger());
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
declare function logger(options?: {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
}): Middleware;
|
|
18
|
+
export { logger as default, logger };
|
package/middleware/logger.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
|
+
import { TezXError } from "../core/error.js";
|
|
1
2
|
import { colorText } from "../utils/colors.js";
|
|
2
|
-
|
|
3
|
+
function logger(options = { enabled: true }) {
|
|
3
4
|
return async function logger(ctx, next) {
|
|
4
5
|
try {
|
|
6
|
+
if (!options?.enabled) {
|
|
7
|
+
return next();
|
|
8
|
+
}
|
|
5
9
|
console.log(`${colorText("<--", "bold")} ${colorText(ctx.method, "bgMagenta")} ${ctx.pathname}`);
|
|
6
10
|
const startTime = performance.now();
|
|
7
|
-
let n = await next();
|
|
11
|
+
let n = (await next());
|
|
8
12
|
const elapsed = performance.now() - startTime;
|
|
9
13
|
console.log(`${colorText("-->", "bold")} ${colorText(ctx.method, "bgBlue")} ${ctx.pathname} ` +
|
|
10
|
-
`${colorText(ctx.getStatus, "yellow")} ${colorText(`${elapsed.toFixed(2)}ms`, "magenta")}`);
|
|
14
|
+
`${colorText(n ? ctx.getStatus : 404, "yellow")} ${colorText(`${elapsed.toFixed(2)}ms`, "magenta")}`);
|
|
11
15
|
return n;
|
|
12
16
|
}
|
|
13
17
|
catch (err) {
|
|
14
18
|
console.error(`${colorText("Error:", "red")}`, err.stack);
|
|
15
|
-
throw new
|
|
19
|
+
throw new TezXError(err.stack);
|
|
16
20
|
}
|
|
17
21
|
};
|
|
18
22
|
}
|
|
23
|
+
export { logger as default, logger };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Context
|
|
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?:
|
|
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?:
|
|
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
|
-
[
|
|
60
|
+
[K in DataKey]: Item[];
|
|
61
|
+
} & {
|
|
62
|
+
[K in CountKey]: number;
|
|
61
63
|
}>;
|
|
62
64
|
};
|
|
63
65
|
export type PaginationBodyType = {
|
|
@@ -100,4 +102,5 @@ export type PaginationBodyType = {
|
|
|
100
102
|
* }
|
|
101
103
|
* }));
|
|
102
104
|
*/
|
|
103
|
-
|
|
105
|
+
declare const paginationHandler: <DataKey extends string = "data", CountKey extends string = "total", Item = any>(options?: PaginationOptions<DataKey, CountKey, Item>) => any;
|
|
106
|
+
export { paginationHandler, paginationHandler as default };
|
package/middleware/pagination.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const paginationHandler = (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];
|
|
@@ -47,3 +47,4 @@ export const paginationHandler = (options = {}) => {
|
|
|
47
47
|
return await next();
|
|
48
48
|
};
|
|
49
49
|
};
|
|
50
|
+
export { paginationHandler, paginationHandler as default };
|
|
@@ -13,4 +13,5 @@ import { Middleware } from "../types/index.js";
|
|
|
13
13
|
* app.use(poweredBy("MyServer"));
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
declare const poweredBy: (serverName?: string) => Middleware;
|
|
17
|
+
export { poweredBy, poweredBy as default };
|
package/middleware/powered-by.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
const poweredBy = (serverName) => {
|
|
2
2
|
return function poweredBy(ctx, next) {
|
|
3
|
-
ctx.
|
|
3
|
+
ctx.headers.set("x-powered-by", serverName || "TezX");
|
|
4
4
|
return next();
|
|
5
5
|
};
|
|
6
6
|
};
|
|
7
|
+
export { poweredBy, poweredBy as default };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Context } from "../core/context.js";
|
|
2
|
-
import {
|
|
2
|
+
import { TezXError } from "../core/error.js";
|
|
3
|
+
import { HttpBaseResponse, Middleware } from "../types/index.js";
|
|
3
4
|
export type RateLimiterOptions = {
|
|
4
5
|
/**
|
|
5
6
|
* 🔴 Maximum allowed requests in the time window
|
|
@@ -20,10 +21,6 @@ export type RateLimiterOptions = {
|
|
|
20
21
|
* keyGenerator: (ctx) => ctx.user?.id || ctx.ip // Use user ID if authenticated
|
|
21
22
|
*/
|
|
22
23
|
keyGenerator?: (ctx: Context) => string;
|
|
23
|
-
/**
|
|
24
|
-
// * ⚠️ (Future) Storage backend - currently memory only
|
|
25
|
-
// * @todo Implement Redis storage
|
|
26
|
-
// */
|
|
27
24
|
/**
|
|
28
25
|
* 🔄 Custom cache storage implementation (e.g., using `Map`, `Redis`, etc.).
|
|
29
26
|
* By default, it uses a `Map<string, { count: number; resetTime: number }>`.
|
|
@@ -45,17 +42,25 @@ export type RateLimiterOptions = {
|
|
|
45
42
|
* @example
|
|
46
43
|
* onError: (ctx, retryAfter) => {
|
|
47
44
|
* ctx.status = 429;
|
|
48
|
-
* throw new
|
|
45
|
+
* throw new TezXError( `Rate limit exceeded. Try again in ${retryAfter} seconds.`);
|
|
49
46
|
* }
|
|
50
47
|
*/
|
|
51
|
-
onError?: (ctx: Context, retryAfter: number, error:
|
|
48
|
+
onError?: (ctx: Context, retryAfter: number, error: TezXError) => HttpBaseResponse;
|
|
52
49
|
};
|
|
53
50
|
/**
|
|
54
51
|
* 🚦 Rate limiting middleware for request throttling
|
|
55
52
|
*
|
|
56
53
|
* Enforces maximum request limits per client with sliding window.
|
|
57
54
|
* Currently supports in-memory storage only (Redis coming soon).
|
|
58
|
-
*
|
|
55
|
+
* @requires
|
|
56
|
+
* *
|
|
57
|
+
```ts
|
|
58
|
+
import { getConnInfo } from "tezx/bun";
|
|
59
|
+
// or
|
|
60
|
+
import { getConnInfo } from "tezx/deno";
|
|
61
|
+
// or
|
|
62
|
+
import { getConnInfo } from "tezx/node";
|
|
63
|
+
```
|
|
59
64
|
* @param {RateLimiterOptions} options - Configuration
|
|
60
65
|
* @returns {Middleware} Middleware function
|
|
61
66
|
*
|
|
@@ -73,4 +78,5 @@ export type RateLimiterOptions = {
|
|
|
73
78
|
* keyGenerator: (ctx) => ctx.user?.id || ctx.ip
|
|
74
79
|
* }));
|
|
75
80
|
*/
|
|
76
|
-
|
|
81
|
+
declare const rateLimiter: (options: RateLimiterOptions) => Middleware;
|
|
82
|
+
export { rateLimiter, rateLimiter as default };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { TezXError } from "../core/error.js";
|
|
2
|
+
import { createRateLimitDefaultStorage, isRateLimit, } from "../utils/rateLimit.js";
|
|
3
|
+
const rateLimiter = (options) => {
|
|
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) => {
|
|
17
|
+
ctx.setStatus = 429;
|
|
18
|
+
throw new TezXError(`Rate limit exceeded. Try again in ${retryAfter} seconds.`, 429);
|
|
19
|
+
}, } = options;
|
|
20
|
+
return async function rateLimiter(ctx, next) {
|
|
21
|
+
const key = keyGenerator(ctx);
|
|
22
|
+
const { check, entry } = isRateLimit(key, storage, maxRequests, windowMs);
|
|
23
|
+
if (check) {
|
|
24
|
+
const retryAfter = Math.ceil((entry.resetTime - Date.now()) / 1000);
|
|
25
|
+
ctx.headers.set("Retry-After", retryAfter.toString());
|
|
26
|
+
return onError(ctx, retryAfter, new TezXError(`Rate limit exceeded. Retry after ${retryAfter} seconds.`));
|
|
27
|
+
}
|
|
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());
|
|
31
|
+
return await next();
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export { rateLimiter, rateLimiter as default };
|
|
@@ -13,4 +13,5 @@ import { Middleware } from "../types/index.js";
|
|
|
13
13
|
* app.use(requestID());
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
declare const requestID: (headerName?: string, contextKey?: string) => Middleware;
|
|
17
|
+
export { requestID as default, requestID };
|
package/middleware/request-id.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { generateUUID } from "../helper/index.js";
|
|
2
|
+
const requestID = (headerName = "X-Request-ID", contextKey = "requestID") => {
|
|
3
3
|
return function requestID(ctx, next) {
|
|
4
|
-
|
|
5
|
-
ctx.headers?.get(headerName);
|
|
6
|
-
const requestId = existingID || `req-${generateID()}`;
|
|
4
|
+
let requestId = ctx.headers.get(headerName) ?? `req-${generateUUID()}`;
|
|
7
5
|
ctx[contextKey] = requestId;
|
|
8
|
-
ctx.
|
|
6
|
+
ctx.headers.set(headerName, requestId);
|
|
9
7
|
return next();
|
|
10
8
|
};
|
|
11
9
|
};
|
|
10
|
+
export { requestID as default, requestID };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Middleware } from "../types/index.js";
|
|
1
|
+
import { Middleware, ResHeaderKey } from "../types/index.js";
|
|
2
2
|
export type SanitizeHeadersOptions = {
|
|
3
3
|
/**
|
|
4
4
|
* 🟢 Whitelist of allowed headers (case-insensitive)
|
|
@@ -6,29 +6,14 @@ export type SanitizeHeadersOptions = {
|
|
|
6
6
|
* @example
|
|
7
7
|
* whitelist: ['content-type', 'authorization'] // Only allow these headers
|
|
8
8
|
*/
|
|
9
|
-
whitelist?:
|
|
9
|
+
whitelist?: ResHeaderKey[];
|
|
10
10
|
/**
|
|
11
11
|
* 🔴 Blacklist of disallowed headers (case-insensitive)
|
|
12
12
|
* @default [] (block none if empty)
|
|
13
13
|
* @example
|
|
14
14
|
* blacklist: ['x-powered-by', 'server'] // Block server info headers
|
|
15
15
|
*/
|
|
16
|
-
blacklist?:
|
|
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;
|
|
16
|
+
blacklist?: ResHeaderKey[];
|
|
32
17
|
};
|
|
33
18
|
/**
|
|
34
19
|
* 🧼 Middleware to sanitize HTTP headers for security and compliance
|
|
@@ -49,4 +34,5 @@ export type SanitizeHeadersOptions = {
|
|
|
49
34
|
* normalizeKeys: true
|
|
50
35
|
* }));
|
|
51
36
|
*/
|
|
52
|
-
|
|
37
|
+
declare const sanitizeHeaders: (options?: SanitizeHeadersOptions) => Middleware;
|
|
38
|
+
export { sanitizeHeaders as default, sanitizeHeaders };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const sanitizeHeaders = (options = {}) => {
|
|
2
|
+
const { whitelist = [], blacklist = [] } = options;
|
|
3
|
+
const normalizedWhitelist = whitelist.map((h) => h.toLowerCase());
|
|
4
|
+
const normalizedBlacklist = blacklist.map((h) => h.toLowerCase());
|
|
5
|
+
let lWhite = normalizedWhitelist.length;
|
|
6
|
+
return async function sanitizeHeaders(ctx, next) {
|
|
7
|
+
await next();
|
|
8
|
+
for (const key of ctx.headers.keys()) {
|
|
9
|
+
if (lWhite > 0 && !normalizedWhitelist.includes(key)) {
|
|
10
|
+
ctx.headers.delete(key);
|
|
11
|
+
}
|
|
12
|
+
if (normalizedBlacklist.includes(key)) {
|
|
13
|
+
ctx.headers.delete(key);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export { sanitizeHeaders as default, sanitizeHeaders };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type SecureHeadersOptions = {
|
|
2
|
+
preset?: "strict" | "balanced" | "dev";
|
|
3
|
+
hsts?: boolean;
|
|
4
|
+
hstsMaxAge?: number;
|
|
5
|
+
frameGuard?: "DENY" | "SAMEORIGIN" | string;
|
|
6
|
+
noSniff?: boolean;
|
|
7
|
+
xssProtection?: boolean;
|
|
8
|
+
referrerPolicy?: string;
|
|
9
|
+
permissionsPolicy?: string;
|
|
10
|
+
csp?: string | Record<string, string | string[]>;
|
|
11
|
+
cspReportOnly?: boolean;
|
|
12
|
+
cspUseNonce?: boolean;
|
|
13
|
+
ultraFastMode?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare const secureHeaders: <T extends Record<string, any> = {}, Path extends string = any>(userOpts?: SecureHeadersOptions) => (ctx: any, next: () => Promise<any>) => Promise<any>;
|