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.
Files changed (234) hide show
  1. package/README.md +122 -89
  2. package/bun/getConnInfo.d.ts +21 -0
  3. package/bun/getConnInfo.js +9 -0
  4. package/bun/index.d.ts +10 -4
  5. package/bun/index.js +8 -4
  6. package/bun/ws.d.ts +48 -0
  7. package/bun/ws.js +58 -0
  8. package/cjs/bun/getConnInfo.js +12 -0
  9. package/cjs/bun/index.js +35 -7
  10. package/cjs/bun/ws.js +63 -0
  11. package/cjs/core/config.js +2 -12
  12. package/cjs/core/context.js +131 -379
  13. package/cjs/core/error.js +49 -0
  14. package/cjs/core/request.js +79 -131
  15. package/cjs/core/router.js +54 -387
  16. package/cjs/core/server.js +83 -202
  17. package/cjs/deno/env.js +4 -4
  18. package/cjs/deno/getConnInfo.js +18 -0
  19. package/cjs/deno/index.js +11 -18
  20. package/cjs/deno/serveStatic.js +53 -0
  21. package/cjs/deno/ws.js +39 -0
  22. package/cjs/helper/index.js +46 -10
  23. package/cjs/index.js +5 -7
  24. package/cjs/jwt/node.js +94 -0
  25. package/cjs/jwt/web.js +178 -0
  26. package/cjs/middleware/basic-auth.js +42 -0
  27. package/cjs/middleware/bearer-auth.js +34 -0
  28. package/cjs/middleware/cache-control.js +44 -0
  29. package/cjs/middleware/cors.js +11 -21
  30. package/cjs/middleware/detect-bot.js +57 -0
  31. package/cjs/middleware/i18n.js +73 -60
  32. package/cjs/middleware/index.js +8 -46
  33. package/cjs/middleware/logger.js +9 -4
  34. package/cjs/middleware/pagination.js +3 -2
  35. package/cjs/middleware/powered-by.js +3 -2
  36. package/cjs/middleware/rate-limiter.js +38 -0
  37. package/cjs/middleware/request-id.js +4 -5
  38. package/cjs/middleware/sanitize-headers.js +22 -0
  39. package/cjs/middleware/secure-headers copy.js +143 -0
  40. package/cjs/middleware/secure-headers.js +157 -0
  41. package/cjs/middleware/{xssProtection.js → xss-protection.js} +5 -8
  42. package/cjs/node/env.js +7 -7
  43. package/cjs/node/getConnInfo.js +16 -0
  44. package/cjs/node/index.js +17 -18
  45. package/cjs/node/mount-node.js +59 -0
  46. package/cjs/node/serveStatic.js +56 -0
  47. package/cjs/node/toWebRequest.js +25 -0
  48. package/cjs/node/ws.js +82 -0
  49. package/cjs/registry/RadixRouter.js +148 -0
  50. package/cjs/registry/index.js +17 -0
  51. package/cjs/types/headers.js +2 -0
  52. package/cjs/types/index.js +13 -0
  53. package/cjs/utils/buffer.js +17 -0
  54. package/cjs/utils/colors.js +2 -0
  55. package/cjs/utils/cookie.js +59 -0
  56. package/cjs/utils/file.js +136 -0
  57. package/cjs/utils/formData.js +60 -10
  58. package/cjs/utils/generateID.js +37 -0
  59. package/cjs/utils/low-level.js +115 -0
  60. package/cjs/utils/{staticFile.js → mimeTypes.js} +0 -87
  61. package/cjs/utils/rateLimit.js +41 -0
  62. package/cjs/utils/response.js +65 -0
  63. package/cjs/{core/environment.js → utils/runtime.js} +2 -1
  64. package/cjs/utils/url.js +65 -30
  65. package/core/config.d.ts +2 -7
  66. package/core/config.js +2 -12
  67. package/core/context.d.ts +209 -164
  68. package/core/context.js +131 -346
  69. package/core/error.d.ts +96 -0
  70. package/core/error.js +44 -0
  71. package/core/request.d.ts +67 -107
  72. package/core/request.js +78 -130
  73. package/core/router.d.ts +138 -133
  74. package/core/router.js +53 -352
  75. package/core/server.d.ts +99 -38
  76. package/core/server.js +83 -202
  77. package/deno/env.js +3 -3
  78. package/deno/getConnInfo.d.ts +21 -0
  79. package/deno/getConnInfo.js +15 -0
  80. package/deno/index.d.ts +9 -4
  81. package/deno/index.js +7 -4
  82. package/deno/serveStatic.d.ts +28 -0
  83. package/deno/serveStatic.js +49 -0
  84. package/deno/ws.d.ts +42 -0
  85. package/deno/ws.js +36 -0
  86. package/helper/index.d.ts +29 -15
  87. package/helper/index.js +27 -7
  88. package/index.d.ts +10 -8
  89. package/index.js +4 -5
  90. package/jwt/node.d.ts +39 -0
  91. package/jwt/node.js +87 -0
  92. package/jwt/web.d.ts +14 -0
  93. package/jwt/web.js +174 -0
  94. package/middleware/basic-auth.d.ts +56 -0
  95. package/middleware/basic-auth.js +38 -0
  96. package/middleware/bearer-auth.d.ts +53 -0
  97. package/middleware/bearer-auth.js +30 -0
  98. package/middleware/cache-control.d.ts +30 -0
  99. package/middleware/cache-control.js +40 -0
  100. package/middleware/cors.d.ts +30 -3
  101. package/middleware/cors.js +12 -22
  102. package/middleware/detect-bot.d.ts +113 -0
  103. package/middleware/detect-bot.js +53 -0
  104. package/middleware/i18n.d.ts +166 -73
  105. package/middleware/i18n.js +73 -60
  106. package/middleware/index.d.ts +8 -32
  107. package/middleware/index.js +8 -44
  108. package/middleware/logger.d.ts +5 -2
  109. package/middleware/logger.js +9 -4
  110. package/middleware/pagination.d.ts +9 -6
  111. package/middleware/pagination.js +3 -2
  112. package/middleware/powered-by.d.ts +2 -1
  113. package/middleware/powered-by.js +3 -2
  114. package/middleware/{rateLimiter.d.ts → rate-limiter.d.ts} +15 -9
  115. package/middleware/rate-limiter.js +34 -0
  116. package/middleware/request-id.d.ts +2 -1
  117. package/middleware/request-id.js +5 -6
  118. package/middleware/{sanitizeHeader.d.ts → sanitize-headers.d.ts} +5 -19
  119. package/middleware/sanitize-headers.js +18 -0
  120. package/middleware/secure-headers copy.d.ts +15 -0
  121. package/middleware/secure-headers copy.js +136 -0
  122. package/middleware/secure-headers.d.ts +132 -0
  123. package/middleware/secure-headers.js +153 -0
  124. package/middleware/{xssProtection.d.ts → xss-protection.d.ts} +2 -1
  125. package/middleware/xss-protection.js +19 -0
  126. package/node/env.js +4 -4
  127. package/node/getConnInfo.d.ts +21 -0
  128. package/node/getConnInfo.js +13 -0
  129. package/node/index.d.ts +13 -4
  130. package/node/index.js +11 -4
  131. package/node/mount-node.d.ts +11 -0
  132. package/node/mount-node.js +56 -0
  133. package/node/serveStatic.d.ts +36 -0
  134. package/node/serveStatic.js +52 -0
  135. package/node/toWebRequest.js +22 -0
  136. package/node/ws.d.ts +56 -0
  137. package/node/ws.js +46 -0
  138. package/package.json +39 -30
  139. package/registry/RadixRouter.d.ts +40 -0
  140. package/registry/RadixRouter.js +144 -0
  141. package/registry/index.d.ts +2 -0
  142. package/registry/index.js +1 -0
  143. package/types/headers.d.ts +2 -0
  144. package/types/headers.js +1 -0
  145. package/types/index.d.ts +318 -18
  146. package/types/index.js +12 -1
  147. package/utils/buffer.d.ts +1 -0
  148. package/utils/buffer.js +14 -0
  149. package/utils/colors.d.ts +24 -0
  150. package/utils/colors.js +2 -0
  151. package/utils/cookie.d.ts +55 -0
  152. package/utils/cookie.js +53 -0
  153. package/utils/file.d.ts +38 -0
  154. package/utils/file.js +96 -0
  155. package/utils/formData.d.ts +41 -1
  156. package/utils/formData.js +58 -9
  157. package/utils/generateID.d.ts +42 -0
  158. package/utils/generateID.js +32 -0
  159. package/utils/httpStatusMap.d.ts +14 -0
  160. package/utils/low-level.d.ts +58 -0
  161. package/utils/low-level.js +108 -0
  162. package/utils/mimeTypes.d.ts +4 -0
  163. package/utils/{staticFile.js → mimeTypes.js} +0 -53
  164. package/utils/rateLimit.d.ts +18 -0
  165. package/utils/rateLimit.js +37 -0
  166. package/utils/response.d.ts +18 -0
  167. package/utils/response.js +58 -0
  168. package/{core/environment.d.ts → utils/runtime.d.ts} +1 -0
  169. package/{core/environment.js → utils/runtime.js} +1 -0
  170. package/utils/url.d.ts +42 -14
  171. package/utils/url.js +61 -27
  172. package/bun/adapter.d.ts +0 -127
  173. package/bun/adapter.js +0 -97
  174. package/cjs/bun/adapter.js +0 -100
  175. package/cjs/core/MiddlewareConfigure.js +0 -68
  176. package/cjs/core/common.js +0 -15
  177. package/cjs/deno/adpater.js +0 -67
  178. package/cjs/helper/common.js +0 -17
  179. package/cjs/middleware/basicAuth.js +0 -71
  180. package/cjs/middleware/cacheControl.js +0 -90
  181. package/cjs/middleware/detectBot.js +0 -104
  182. package/cjs/middleware/detectLocale.js +0 -43
  183. package/cjs/middleware/lazyLoadModules.js +0 -73
  184. package/cjs/middleware/rateLimiter.js +0 -24
  185. package/cjs/middleware/requestTimeout.js +0 -42
  186. package/cjs/middleware/sanitizeHeader.js +0 -51
  187. package/cjs/middleware/secureHeaders.js +0 -42
  188. package/cjs/node/adapter.js +0 -138
  189. package/cjs/utils/regexRouter.js +0 -58
  190. package/cjs/utils/state.js +0 -34
  191. package/cjs/utils/toWebRequest.js +0 -35
  192. package/cjs/ws/deno.js +0 -20
  193. package/cjs/ws/index.js +0 -53
  194. package/cjs/ws/node.js +0 -65
  195. package/core/MiddlewareConfigure.d.ts +0 -15
  196. package/core/MiddlewareConfigure.js +0 -63
  197. package/core/common.d.ts +0 -21
  198. package/core/common.js +0 -11
  199. package/deno/adpater.d.ts +0 -38
  200. package/deno/adpater.js +0 -64
  201. package/helper/common.d.ts +0 -5
  202. package/helper/common.js +0 -14
  203. package/middleware/basicAuth.d.ts +0 -81
  204. package/middleware/basicAuth.js +0 -67
  205. package/middleware/cacheControl.d.ts +0 -48
  206. package/middleware/cacheControl.js +0 -53
  207. package/middleware/detectBot.d.ts +0 -121
  208. package/middleware/detectBot.js +0 -98
  209. package/middleware/detectLocale.d.ts +0 -55
  210. package/middleware/detectLocale.js +0 -39
  211. package/middleware/lazyLoadModules.d.ts +0 -72
  212. package/middleware/lazyLoadModules.js +0 -69
  213. package/middleware/rateLimiter.js +0 -20
  214. package/middleware/requestTimeout.d.ts +0 -25
  215. package/middleware/requestTimeout.js +0 -38
  216. package/middleware/sanitizeHeader.js +0 -47
  217. package/middleware/secureHeaders.d.ts +0 -78
  218. package/middleware/secureHeaders.js +0 -38
  219. package/middleware/xssProtection.js +0 -22
  220. package/node/adapter.d.ts +0 -46
  221. package/node/adapter.js +0 -102
  222. package/utils/regexRouter.d.ts +0 -66
  223. package/utils/regexRouter.js +0 -53
  224. package/utils/state.d.ts +0 -50
  225. package/utils/state.js +0 -30
  226. package/utils/staticFile.d.ts +0 -10
  227. package/utils/toWebRequest.js +0 -32
  228. package/ws/deno.d.ts +0 -6
  229. package/ws/deno.js +0 -16
  230. package/ws/index.d.ts +0 -180
  231. package/ws/index.js +0 -50
  232. package/ws/node.d.ts +0 -7
  233. package/ws/node.js +0 -28
  234. /package/{utils → node}/toWebRequest.d.ts +0 -0
@@ -0,0 +1,136 @@
1
+ import crypto from "crypto";
2
+ const joinSrc = (v) => typeof v === "string" ? v : v.join(" ");
3
+ const buildCSPString = (cspObj) => {
4
+ const parts = [];
5
+ for (const key in cspObj) {
6
+ parts.push(`${key} ${joinSrc(cspObj[key])}`);
7
+ }
8
+ return parts.join("; ");
9
+ };
10
+ const defaultPresets = {
11
+ strict: {
12
+ preset: "strict",
13
+ hsts: true,
14
+ hstsMaxAge: 63072000,
15
+ frameGuard: "DENY",
16
+ noSniff: true,
17
+ xssProtection: true,
18
+ referrerPolicy: "strict-origin-when-cross-origin",
19
+ permissionsPolicy: "geolocation=(), microphone=(), camera=(), usb=()",
20
+ csp: {
21
+ "default-src": ["'self'"],
22
+ "script-src": ["'self'"],
23
+ "style-src": ["'self'", "'unsafe-inline'"],
24
+ "img-src": ["'self'", "data:", "blob:"],
25
+ "font-src": ["'self'"],
26
+ "connect-src": ["'self'"],
27
+ "object-src": ["'none'"],
28
+ "frame-ancestors": ["'none'"],
29
+ },
30
+ cspReportOnly: false,
31
+ },
32
+ balanced: {
33
+ preset: "balanced",
34
+ hsts: true,
35
+ hstsMaxAge: 31536000,
36
+ frameGuard: "SAMEORIGIN",
37
+ noSniff: true,
38
+ xssProtection: true,
39
+ referrerPolicy: "no-referrer-when-downgrade",
40
+ permissionsPolicy: "geolocation=(), microphone=()",
41
+ csp: {
42
+ "default-src": ["'self'"],
43
+ "script-src": ["'self'", "https://cdn.jsdelivr.net"],
44
+ "style-src": [
45
+ "'self'",
46
+ "'unsafe-inline'",
47
+ "https://fonts.googleapis.com",
48
+ ],
49
+ "img-src": ["'self'", "data:", "https://images.example.com"],
50
+ "connect-src": ["'self'", "https://api.example.com"],
51
+ },
52
+ cspReportOnly: true,
53
+ },
54
+ dev: {
55
+ preset: "dev",
56
+ hsts: false,
57
+ frameGuard: "SAMEORIGIN",
58
+ noSniff: false,
59
+ xssProtection: false,
60
+ referrerPolicy: "no-referrer",
61
+ permissionsPolicy: "",
62
+ csp: {
63
+ "default-src": [
64
+ "'self'",
65
+ "'unsafe-inline'",
66
+ "'unsafe-eval'",
67
+ "http://localhost:3000",
68
+ ],
69
+ "img-src": ["'self'", "data:", "blob:"],
70
+ },
71
+ cspReportOnly: true,
72
+ },
73
+ };
74
+ const setHeader = (ctx, name, value) => {
75
+ if (typeof ctx.setHeader === "function")
76
+ ctx.setHeader(name, value);
77
+ else if (ctx.response?.setHeader)
78
+ ctx.response.setHeader(name, value);
79
+ else if (ctx.headersOut)
80
+ ctx.headersOut[name] = value;
81
+ };
82
+ export const secureHeaders = (userOpts = {}) => {
83
+ const preset = userOpts.preset ?? "balanced";
84
+ const base = {
85
+ ...(defaultPresets[preset] || defaultPresets.balanced),
86
+ ...userOpts,
87
+ };
88
+ const hstsHeader = base.hsts
89
+ ? `max-age=${base.hstsMaxAge || 31536000}; includeSubDomains; preload`
90
+ : "";
91
+ const frameHeader = base.frameGuard || "SAMEORIGIN";
92
+ const noSniffHeader = base.noSniff ? "nosniff" : "";
93
+ const xssHeader = base.xssProtection ? "1; mode=block" : "0";
94
+ const referrerHeader = base.referrerPolicy || "no-referrer";
95
+ const permissionsHeader = base.permissionsPolicy || "";
96
+ let cspStatic = null;
97
+ let cspNeedsNonce = !!base.cspUseNonce;
98
+ if (typeof base.csp === "string")
99
+ cspStatic = base.csp;
100
+ else if (base.csp && typeof base.csp === "object")
101
+ cspStatic = buildCSPString(base.csp);
102
+ if (base.ultraFastMode)
103
+ cspNeedsNonce = false;
104
+ const cspReportOnly = !!base.cspReportOnly;
105
+ return async (ctx, next) => {
106
+ try {
107
+ if (base.hsts)
108
+ setHeader(ctx, "Strict-Transport-Security", hstsHeader);
109
+ setHeader(ctx, "X-Frame-Options", frameHeader);
110
+ setHeader(ctx, "X-Content-Type-Options", noSniffHeader);
111
+ setHeader(ctx, "X-XSS-Protection", xssHeader);
112
+ setHeader(ctx, "Referrer-Policy", referrerHeader);
113
+ if (permissionsHeader)
114
+ setHeader(ctx, "Permissions-Policy", permissionsHeader);
115
+ if (cspNeedsNonce) {
116
+ const nonce = crypto.randomBytes(12).toString("base64");
117
+ let cspHeader = cspStatic || `default-src 'self'; script-src 'self' 'nonce-${nonce}'`;
118
+ if (cspReportOnly)
119
+ setHeader(ctx, "Content-Security-Policy-Report-Only", cspHeader);
120
+ else
121
+ setHeader(ctx, "Content-Security-Policy", cspHeader);
122
+ ctx.cspNonce = nonce;
123
+ }
124
+ else if (cspStatic) {
125
+ if (cspReportOnly)
126
+ setHeader(ctx, "Content-Security-Policy-Report-Only", cspStatic);
127
+ else
128
+ setHeader(ctx, "Content-Security-Policy", cspStatic);
129
+ }
130
+ return await next();
131
+ }
132
+ catch {
133
+ return await next();
134
+ }
135
+ };
136
+ };
@@ -0,0 +1,132 @@
1
+ import { Middleware } from "../types/index.js";
2
+ /**
3
+ * Options for HTTP Strict Transport Security (HSTS) header.
4
+ */
5
+ export interface HstsOptions {
6
+ /**
7
+ * Max age in seconds for the `Strict-Transport-Security` header.
8
+ * Example: 31536000 (1 year)
9
+ */
10
+ maxAge?: number;
11
+ /**
12
+ * Apply HSTS to subdomains by adding `includeSubDomains` directive.
13
+ * Default: false
14
+ */
15
+ includeSubDomains?: boolean;
16
+ /**
17
+ * Add `preload` directive for browser preload lists.
18
+ * Default: false
19
+ */
20
+ preload?: boolean;
21
+ /**
22
+ * Only apply HSTS on HTTPS requests.
23
+ * If true, HTTP requests will not receive the HSTS header.
24
+ * Default: false
25
+ */
26
+ hstsOnlyOnHttps?: boolean;
27
+ }
28
+ /**
29
+ * Options for the secureHeaders middleware.
30
+ */
31
+ export type SecureHeadersOptions = {
32
+ /**
33
+ * Built-in preset to use.
34
+ * - "strict": strongest defaults for production.
35
+ * - "balanced": reasonable defaults for most apps (report-only CSP by default).
36
+ * - "dev": permissive settings useful for local development.
37
+ *
38
+ * @default "balanced"
39
+ */
40
+ preset?: "strict" | "balanced" | "dev";
41
+ /**
42
+ * HSTS (HTTP Strict Transport Security) options.
43
+ * If provided, the middleware will set the `Strict-Transport-Security` header.
44
+ *
45
+ * @example
46
+ * { maxAge: 31536000, includeSubDomains: true, preload: true }
47
+ */
48
+ hsts?: HstsOptions;
49
+ /**
50
+ * Value for `X-Frame-Options` header.
51
+ * Common values: "DENY", "SAMEORIGIN".
52
+ *
53
+ * @default "SAMEORIGIN"
54
+ */
55
+ frameGuard?: "DENY" | "SAMEORIGIN" | string;
56
+ /**
57
+ * If true, sets `X-Content-Type-Options: nosniff`.
58
+ *
59
+ * @default true (depends on preset)
60
+ */
61
+ noSniff?: boolean;
62
+ /**
63
+ * If true, sets `X-XSS-Protection: 1; mode=block`.
64
+ * Note: modern browsers use CSP; this header is legacy but harmless.
65
+ *
66
+ * @default true (depends on preset)
67
+ */
68
+ xssProtection?: boolean;
69
+ /**
70
+ * Value for `Referrer-Policy` header.
71
+ * Examples: "no-referrer", "strict-origin-when-cross-origin".
72
+ *
73
+ * @default "no-referrer" (depends on preset)
74
+ */
75
+ referrerPolicy?: string;
76
+ /**
77
+ * Value for `Permissions-Policy` (formerly Feature-Policy).
78
+ * Example: 'geolocation=(), microphone=()'
79
+ *
80
+ * @default '' (empty string = not set)
81
+ */
82
+ permissionsPolicy?: string;
83
+ /**
84
+ * Content Security Policy (CSP).
85
+ * - Pass a raw header string to use it unchanged.
86
+ * - Or pass an object mapping directives to sources (object will be prebuilt at init).
87
+ *
88
+ * Example object:
89
+ * {
90
+ * "default-src": ["'self'"],
91
+ * "script-src": ["'self'", "https://cdn.example.com"]
92
+ * }
93
+ */
94
+ csp?: string | Record<string, string | string[]>;
95
+ /**
96
+ * If true, send `Content-Security-Policy-Report-Only` instead of enforcement header.
97
+ * Useful when first testing policies.
98
+ *
99
+ * @default false
100
+ */
101
+ cspReportOnly?: boolean;
102
+ /**
103
+ * If true, middleware will generate a per-request nonce (string) and inject it
104
+ * into the `script-src` directive so inline scripts with that nonce are allowed.
105
+ * Note: nonce generation allocates a small string per-request unless `ultraFastMode` is enabled.
106
+ *
107
+ * @default false
108
+ */
109
+ cspUseNonce?: boolean;
110
+ /**
111
+ * Ultra-fast mode disables per-request allocations (e.g., nonce generation).
112
+ * Use this in high-QPS environments where inline scripts are not required.
113
+ *
114
+ * @default false
115
+ */
116
+ ultraFastMode?: boolean;
117
+ };
118
+ /**
119
+ * secureHeaders middleware
120
+ *
121
+ * Precomputes static headers (HSTS, static CSP, X-Frame-Options, etc.) at
122
+ * middleware creation time. Optionally supports per-request CSP nonces
123
+ * (disabled in ultraFastMode).
124
+ *
125
+ * @template T,Path
126
+ * @param {SecureHeadersOptions} [userOpts={}] - configuration overrides
127
+ * @returns {Middleware<T,Path>} TezX-compatible middleware
128
+ *
129
+ * @example
130
+ * app.use(secureHeaders({ preset: 'strict', cspUseNonce: true }));
131
+ */
132
+ export declare const secureHeaders: <T extends Record<string, any> = {}, Path extends string = any>(userOpts?: SecureHeadersOptions) => Middleware<T, Path>;
@@ -0,0 +1,153 @@
1
+ import { generateRandomBase64, GlobalConfig } from "../helper/index.js";
2
+ const joinSrc = (v) => typeof v === "string" ? v : v.join(" ");
3
+ const buildCSPString = (cspObj) => {
4
+ const parts = [];
5
+ for (const key in cspObj)
6
+ parts.push(`${key} ${joinSrc(cspObj[key])}`);
7
+ return parts.join("; ");
8
+ };
9
+ export const secureHeaders = (userOpts = {}) => {
10
+ const defaultPresets = {
11
+ strict: {
12
+ preset: "strict",
13
+ hsts: { maxAge: 63072000, includeSubDomains: true, preload: true },
14
+ frameGuard: "DENY",
15
+ noSniff: true,
16
+ xssProtection: true,
17
+ referrerPolicy: "strict-origin-when-cross-origin",
18
+ permissionsPolicy: "geolocation=(), microphone=(), camera=(), usb=()",
19
+ csp: {
20
+ "default-src": ["'self'"],
21
+ "script-src": ["'self'"],
22
+ "style-src": ["'self'", "'unsafe-inline'"],
23
+ "img-src": ["'self'", "data:", "blob:"],
24
+ "font-src": ["'self'"],
25
+ "connect-src": ["'self'"],
26
+ "object-src": ["'none'"],
27
+ "frame-ancestors": ["'none'"],
28
+ },
29
+ cspReportOnly: false,
30
+ },
31
+ balanced: {
32
+ preset: "balanced",
33
+ hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
34
+ frameGuard: "SAMEORIGIN",
35
+ noSniff: true,
36
+ xssProtection: true,
37
+ referrerPolicy: "no-referrer-when-downgrade",
38
+ permissionsPolicy: "geolocation=(), microphone=()",
39
+ csp: {
40
+ "default-src": ["'self'"],
41
+ "script-src": ["'self'", "https://cdn.jsdelivr.net"],
42
+ "style-src": [
43
+ "'self'",
44
+ "'unsafe-inline'",
45
+ "https://fonts.googleapis.com",
46
+ ],
47
+ "img-src": ["'self'", "data:", "https://images.example.com"],
48
+ "connect-src": ["'self'", "https://api.example.com"],
49
+ },
50
+ cspReportOnly: true,
51
+ },
52
+ dev: {
53
+ preset: "dev",
54
+ hsts: undefined,
55
+ frameGuard: "SAMEORIGIN",
56
+ noSniff: false,
57
+ xssProtection: false,
58
+ referrerPolicy: "no-referrer",
59
+ permissionsPolicy: "",
60
+ csp: {
61
+ "default-src": [
62
+ "'self'",
63
+ "'unsafe-inline'",
64
+ "'unsafe-eval'",
65
+ "http://localhost:3000",
66
+ ],
67
+ "img-src": ["'self'", "data:", "blob:"],
68
+ },
69
+ cspReportOnly: true,
70
+ },
71
+ };
72
+ const preset = userOpts.preset ?? "balanced";
73
+ const base = {
74
+ ...(defaultPresets[preset] || defaultPresets.balanced),
75
+ ...userOpts,
76
+ };
77
+ const frameHeader = base.frameGuard || "SAMEORIGIN";
78
+ const xssHeader = base.xssProtection ? "1; mode=block" : "0";
79
+ const noSniffHeader = base.noSniff ? "nosniff" : "";
80
+ const permissionsHeader = base.permissionsPolicy || "";
81
+ const referrerHeader = base.referrerPolicy || "no-referrer";
82
+ const hstsParts = [`max-age=${base.hsts?.maxAge || 31536000}`];
83
+ if (base.hsts?.includeSubDomains)
84
+ hstsParts.push("includeSubDomains");
85
+ if (base.hsts?.preload)
86
+ hstsParts.push("preload");
87
+ const hstsHeader = hstsParts.join("; ");
88
+ let cspStatic = null;
89
+ let cspNeedsNonce = !!base.cspUseNonce;
90
+ if (typeof base.csp === "string")
91
+ cspStatic = base.csp;
92
+ else if (base.csp)
93
+ cspStatic = buildCSPString(base.csp);
94
+ const cspReportOnly = !!base.cspReportOnly;
95
+ const ultraFast = !!base.ultraFastMode;
96
+ if (cspNeedsNonce && ultraFast) {
97
+ GlobalConfig.debugging.warn("secureHeaders: ultraFastMode disables CSP nonce support. Nonce will not be used.");
98
+ }
99
+ if (ultraFast)
100
+ cspNeedsNonce = false;
101
+ return async (ctx, next) => {
102
+ try {
103
+ if (base.hsts) {
104
+ const proto = (ctx.req?.header("x-forwarded-proto") || "").toString();
105
+ if (!base.hsts.hstsOnlyOnHttps || proto.includes("https")) {
106
+ ctx.headers.set("Strict-Transport-Security", hstsHeader);
107
+ }
108
+ }
109
+ ctx.headers.set("X-Frame-Options", frameHeader);
110
+ ctx.headers.set("X-Content-Type-Options", noSniffHeader);
111
+ ctx.headers.set("X-XSS-Protection", xssHeader);
112
+ ctx.headers.set("Referrer-Policy", referrerHeader);
113
+ if (permissionsHeader)
114
+ ctx.headers.set("Permissions-Policy", permissionsHeader);
115
+ if (cspNeedsNonce) {
116
+ const nonce = generateRandomBase64();
117
+ let cspHeader = cspStatic;
118
+ if (!cspHeader) {
119
+ cspHeader = `default-src 'self'; script-src 'self' 'nonce-${nonce}'`;
120
+ }
121
+ if (typeof base.csp === "object") {
122
+ const idx = cspHeader.indexOf("script-src");
123
+ if (idx >= 0) {
124
+ const parts = [];
125
+ parts.push(cspHeader.slice(0, idx + 10));
126
+ parts.push(" 'nonce-" + nonce + "'");
127
+ parts.push(cspHeader.slice(idx + 10));
128
+ cspHeader = parts.join("");
129
+ }
130
+ else {
131
+ cspHeader += "; script-src 'self' 'nonce-" + nonce + "'";
132
+ }
133
+ }
134
+ ctx.cspNonce = nonce;
135
+ if (cspReportOnly)
136
+ ctx.headers.set("Content-Security-Policy-Report-Only", cspHeader);
137
+ else
138
+ ctx.headers.set("Content-Security-Policy", cspHeader);
139
+ }
140
+ else if (cspStatic) {
141
+ if (cspReportOnly)
142
+ ctx.headers.set("Content-Security-Policy-Report-Only", cspStatic);
143
+ else
144
+ ctx.headers.set("Content-Security-Policy", cspStatic);
145
+ }
146
+ return await next();
147
+ }
148
+ catch (err) {
149
+ console.error("secureHeaders middleware error", err);
150
+ return await next();
151
+ }
152
+ };
153
+ };
@@ -40,4 +40,5 @@ export type XSSProtectionOptions = {
40
40
  * fallbackCSP: "default-src 'self'"
41
41
  * }));
42
42
  */
43
- export declare const xssProtection: (options?: XSSProtectionOptions) => (ctx: Context, next: NextCallback) => Promise<any>;
43
+ declare const xssProtection: (options?: XSSProtectionOptions) => (ctx: Context, next: NextCallback) => Promise<void>;
44
+ export { xssProtection as default, xssProtection };
@@ -0,0 +1,19 @@
1
+ const xssProtection = (options = {}) => {
2
+ const { enabled = true, mode = "block", fallbackCSP = "default-src 'self'; script-src 'self';", } = options;
3
+ return async function xssProtection(ctx, next) {
4
+ const isEnabled = typeof enabled === "function" ? enabled(ctx) : enabled;
5
+ if (!isEnabled) {
6
+ return await next();
7
+ }
8
+ const xssHeaderValue = mode === "block" ? "1; mode=block" : "1";
9
+ ctx.setHeader("X-XSS-Protection", xssHeaderValue);
10
+ if (fallbackCSP) {
11
+ const existingCSP = ctx.req.header("content-security-policy");
12
+ if (!existingCSP) {
13
+ ctx.setHeader("Content-Security-Policy", fallbackCSP);
14
+ }
15
+ }
16
+ return await next();
17
+ };
18
+ };
19
+ export { xssProtection as default, xssProtection };
package/node/env.js CHANGED
@@ -1,11 +1,11 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { Environment } from "../core/environment.js";
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { TezXError } from "../core/error.js";
3
3
  import { colorText } from "../utils/colors.js";
4
+ import { runtime } from "../utils/runtime.js";
4
5
  function parseEnvFile(filePath, result) {
5
6
  try {
6
- let runtime = Environment.getEnvironment;
7
7
  if (runtime !== "bun" && runtime !== "node") {
8
- throw new Error(`Please use ${colorText(`import {loadEnv} from "tezx/${runtime}"`, "bgRed")} environment`);
8
+ throw new TezXError(`Please use ${colorText(`import {loadEnv} from "tezx/${runtime}"`, "bgRed")} environment`);
9
9
  }
10
10
  let fileExists = existsSync(filePath);
11
11
  if (!fileExists) {
@@ -0,0 +1,21 @@
1
+ import { Middleware } from "../types/index.js";
2
+ /**
3
+ * Middleware to extract and inject connection information into the request context.
4
+ *
5
+ * This middleware reads the socket's remote address information (like IP, port, and family)
6
+ * from the request object and attaches it to `ctx.req.remoteAddress`.
7
+ *
8
+ * @returns {Middleware<any>} The middleware function that sets `ctx.req.remoteAddress`.
9
+ *
10
+ * @example
11
+ * import { getConnInfo } from "tezx/node";
12
+ *
13
+ * app.use(getConnInfo());
14
+ *
15
+ * // Access later in route handler:
16
+ * router.get("/", (ctx) => {
17
+ * const ip = ctx.req.remoteAddress?.address;
18
+ * return new Response(`Your IP: ${ip}`);
19
+ * });
20
+ */
21
+ export declare function getConnInfo<T extends Record<string, any> = {}, Path extends string = any>(): Middleware<T, Path>;
@@ -0,0 +1,13 @@
1
+ export function getConnInfo() {
2
+ return (ctx, next) => {
3
+ let request = ctx.args?.[0];
4
+ if (request && request.socket) {
5
+ ctx.req.remoteAddress = {
6
+ family: request.socket.remoteFamily,
7
+ address: request.socket.remoteAddress,
8
+ port: request.socket.remotePort,
9
+ };
10
+ }
11
+ return next();
12
+ };
13
+ }
package/node/index.d.ts CHANGED
@@ -1,9 +1,18 @@
1
- import { nodeAdapter } from "./adapter.js";
2
1
  import { loadEnv } from "./env.js";
3
- export * from "./adapter.js";
4
- export * from "./env.js";
2
+ import { getConnInfo } from "./getConnInfo.js";
3
+ import { mountTezXOnNode } from "./mount-node.js";
4
+ import { serveStatic } from "./serveStatic.js";
5
+ import { toWebRequest } from "./toWebRequest.js";
6
+ import { upgradeWebSocket } from "./ws.js";
7
+ export type { nodeWebSocketOptions } from "./ws.js";
8
+ export type { ServeStatic, StaticServeOption, StaticFileArray, WebSocketCallback, WebSocketEvent, WebSocketOptions, } from "../types/index.js";
9
+ export { serveStatic, upgradeWebSocket, getConnInfo, toWebRequest, loadEnv, mountTezXOnNode, };
5
10
  declare const _default: {
6
- nodeAdapter: typeof nodeAdapter;
11
+ serveStatic: typeof serveStatic;
12
+ upgradeWebSocket: typeof upgradeWebSocket;
13
+ mountTezXOnNode: typeof mountTezXOnNode;
14
+ toWebRequest: typeof toWebRequest;
15
+ getConnInfo: typeof getConnInfo;
7
16
  loadEnv: typeof loadEnv;
8
17
  };
9
18
  export default _default;
package/node/index.js CHANGED
@@ -1,8 +1,15 @@
1
- import { nodeAdapter } from "./adapter.js";
2
1
  import { loadEnv } from "./env.js";
3
- export * from "./adapter.js";
4
- export * from "./env.js";
2
+ import { getConnInfo } from "./getConnInfo.js";
3
+ import { mountTezXOnNode } from "./mount-node.js";
4
+ import { serveStatic } from "./serveStatic.js";
5
+ import { toWebRequest } from "./toWebRequest.js";
6
+ import { upgradeWebSocket } from "./ws.js";
7
+ export { serveStatic, upgradeWebSocket, getConnInfo, toWebRequest, loadEnv, mountTezXOnNode, };
5
8
  export default {
6
- nodeAdapter,
9
+ serveStatic,
10
+ upgradeWebSocket,
11
+ mountTezXOnNode,
12
+ toWebRequest,
13
+ getConnInfo,
7
14
  loadEnv,
8
15
  };
@@ -0,0 +1,11 @@
1
+ import { TezX } from "../core/server.js";
2
+ /**
3
+ * Mounts a TezX app onto a native Node.js HTTP server.
4
+ *
5
+ * This bridges a TezX application (typically used in a Fetch API environment) with a native
6
+ * Node.js HTTP server by adapting the request/response interface and handling stream piping.
7
+ *
8
+ * @param {TezX} app - The TezX app instance that handles incoming requests and returns a Response object.
9
+ * @param {import('http').Server} server - A native Node.js HTTP server instance to attach the TezX handler to.
10
+ */
11
+ export declare function mountTezXOnNode(app: TezX, server: any): void;
@@ -0,0 +1,56 @@
1
+ import { Readable } from "node:stream";
2
+ import { GlobalConfig } from "../core/config.js";
3
+ import { toWebRequest } from "./toWebRequest.js";
4
+ function readableStreamToNodeStream(stream) {
5
+ const reader = stream.getReader();
6
+ return new Readable({
7
+ async read() {
8
+ try {
9
+ const { done, value } = await reader.read();
10
+ if (done) {
11
+ this.push(null);
12
+ }
13
+ else {
14
+ this.push(value);
15
+ }
16
+ }
17
+ catch (err) {
18
+ this.destroy(err);
19
+ }
20
+ },
21
+ destroy(err, callback) {
22
+ reader.cancel().then(() => callback(err), (cancelErr) => callback(cancelErr));
23
+ },
24
+ });
25
+ }
26
+ export function mountTezXOnNode(app, server) {
27
+ server.on("request", async (req, res) => {
28
+ try {
29
+ const request = toWebRequest(req, req.method);
30
+ const response = await app.serve(request, req, res, server);
31
+ if (res.writableEnded || res.headersSent)
32
+ return;
33
+ const { status = 200, statusText = "", headers = {}, body } = response;
34
+ const nodeHeaders = Object.fromEntries(headers instanceof Headers
35
+ ? headers.entries()
36
+ : Object.entries(headers));
37
+ res.writeHead(status, statusText, nodeHeaders);
38
+ if (!body) {
39
+ return res.end();
40
+ }
41
+ if (body instanceof Readable) {
42
+ return body.pipe(res);
43
+ }
44
+ if (typeof body.getReader === "function") {
45
+ return (Readable.fromWeb?.(body)?.pipe?.(res) ??
46
+ readableStreamToNodeStream(body).pipe(res));
47
+ }
48
+ res.end(body);
49
+ }
50
+ catch (err) {
51
+ res.statusCode = 500;
52
+ GlobalConfig.debugging?.error?.(err?.message || "Unknown error");
53
+ res.end(err?.message || "Internal Server Error");
54
+ }
55
+ });
56
+ }
@@ -0,0 +1,36 @@
1
+ import { ServeStatic, StaticFileArray, StaticServeOption } from "../types/index.js";
2
+ /**
3
+ * Registers static files for serving from a folder, optionally under a specific route.
4
+ *
5
+ * There are two usage patterns:
6
+ *
7
+ * 1. `serveStatic(route: string, folder: string, option?: StaticServeOption)`
8
+ * - Serves files in `folder` under the given `route`.
9
+ * - Example: `serveStatic("/assets", "./public/assets")`
10
+ *
11
+ * 2. `serveStatic(folder: string, option?: StaticServeOption)`
12
+ * - Serves files in `folder` under its own route (e.g., `./public` -> `/public`)
13
+ * - Example: `serveStatic("./public")`
14
+ *
15
+ * @param args - Either [route, folder, option] or [folder, option]
16
+ * @returns A list of static files and their associated route paths.
17
+ */
18
+ /**
19
+ * Universal ETag generator for Deno, Bun, and Node.js environments.
20
+ * Uses file size + modification time (mtimeMs) to create a unique hash-based ETag.
21
+ *
22
+ * @example
23
+ * const etag = await getETag("/path/to/file.txt");
24
+ * ctx.setHeader("ETag", etag);
25
+ */
26
+ export declare function serveStatic(route: string, folder: string, option?: StaticServeOption): ServeStatic;
27
+ export declare function serveStatic(folder: string, option?: StaticServeOption): ServeStatic;
28
+ /**
29
+ * Recursively collects files from a directory, applying filters.
30
+ *
31
+ * @param dir - Directory to search.
32
+ * @param basePath - Route base path for constructing public paths.
33
+ * @param option - Options including extensions filter.
34
+ * @returns Array of file objects with file system and public paths.
35
+ */
36
+ export declare function getFiles(dir: string, basePath?: string, option?: StaticServeOption): StaticFileArray;