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
package/utils/file.js ADDED
@@ -0,0 +1,96 @@
1
+ import { TezXError } from "../core/error.js";
2
+ import { runtime } from "./runtime.js";
3
+ export async function fileExists(path) {
4
+ switch (runtime) {
5
+ case "node": {
6
+ const { access } = await import("node:fs/promises");
7
+ try {
8
+ await access(path);
9
+ return true;
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ }
15
+ case "bun":
16
+ return Bun.file(path).exists();
17
+ case "deno":
18
+ try {
19
+ await Deno.stat(path);
20
+ return true;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ default:
26
+ return false;
27
+ }
28
+ }
29
+ export async function getFileBuffer(path) {
30
+ switch (runtime) {
31
+ case "node": {
32
+ const { readFile } = await import("node:fs/promises");
33
+ return readFile(path);
34
+ }
35
+ case "bun": {
36
+ return new Uint8Array(await Bun.file(path).arrayBuffer());
37
+ }
38
+ case "deno":
39
+ return Deno.readFile(path);
40
+ default:
41
+ throw new TezXError("Unsupported runtime environment");
42
+ }
43
+ }
44
+ export async function readStream(path) {
45
+ switch (runtime) {
46
+ case "node": {
47
+ const { createReadStream } = await import("node:fs");
48
+ const { Readable } = await import("node:stream");
49
+ return Readable.toWeb(createReadStream(path));
50
+ }
51
+ case "bun": {
52
+ return Bun.file(path).stream();
53
+ }
54
+ case "deno": {
55
+ return (await Deno.open(path, { read: true })).readable;
56
+ }
57
+ default:
58
+ throw new TezXError("Unsupported runtime environment");
59
+ }
60
+ }
61
+ export async function fileSize(path) {
62
+ switch (runtime) {
63
+ case "node": {
64
+ const { stat } = await import("node:fs/promises");
65
+ const st = await stat(path);
66
+ return { size: st.size, mtime: st.mtime };
67
+ }
68
+ case "bun": {
69
+ const st = await Bun.file(path).stat();
70
+ return { size: st.size, mtime: st.mtime };
71
+ }
72
+ case "deno": {
73
+ const st = await Deno.stat(path);
74
+ return {
75
+ size: st.size,
76
+ mtime: st.mtime ?? new Date(),
77
+ };
78
+ }
79
+ default:
80
+ throw new TezXError("Unsupported runtime: " + runtime);
81
+ }
82
+ }
83
+ export async function etagDigest(algo = "MD5", data) {
84
+ const encoded = typeof data === "string" ? new TextEncoder().encode(data) : data;
85
+ if (runtime === "bun") {
86
+ return Bun.hash(data, 256).toString(16);
87
+ }
88
+ if (globalThis?.crypto?.subtle) {
89
+ const buffer = await crypto.subtle.digest(algo, encoded);
90
+ return Array.from(new Uint8Array(buffer))
91
+ .map((b) => b.toString(16).padStart(2, "0"))
92
+ .join("");
93
+ }
94
+ const { createHash } = await import("node:crypto");
95
+ return createHash(algo).update(encoded).digest("hex");
96
+ }
@@ -1 +1,41 @@
1
- export declare function sanitized(title: string): string;
1
+ import { Context } from "../index.js";
2
+ import { FormDataOptions } from "../types/index.js";
3
+ /**
4
+ * Parses and validates multipart/form-data from the request context.
5
+ *
6
+ * This helper extracts form fields and files from the request's FormData,
7
+ * applies validation rules defined in the `options`, and returns a structured
8
+ * object with key-value pairs, where values can be strings, Files, or arrays of them.
9
+ *
10
+ * Validation includes:
11
+ * - File MIME type whitelist
12
+ * - Maximum file size per file
13
+ * - Maximum number of files per field
14
+ * - Maximum combined size of all uploaded files
15
+ * - Maximum length of individual text fields
16
+ * - Optional sanitization of text fields (if enabled)
17
+ *
18
+ * Repeated form field keys will result in an array of values.
19
+ *
20
+ * @param {Context} ctx - The HTTP request context containing the FormData.
21
+ * @param {FormDataOptions} [options] - Configuration options to control parsing and validation.
22
+ * @throws {Error} Throws if any validation rule is violated (e.g., file too large, too many files).
23
+ * @returns {Promise<Record<string, string | File | (string | File)[]>>} A Promise resolving to an object mapping form field names
24
+ * to their respective values. Values are strings or File objects, or arrays if multiple values per key exist.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { useFormData } from "tezx/helper";
29
+ *
30
+ * const formData = await useFormData(ctx, {
31
+ * allowedTypes: ["image/jpeg", "image/png"],
32
+ * maxSize: 5 * 1024 * 1024, // 5MB per file
33
+ * maxFiles: 3,
34
+ * maxTotalSize: 15 * 1024 * 1024, // 15MB total
35
+ * maxFieldSize: 1000,
36
+ * sanitized: true
37
+ * });
38
+ * ```
39
+ */
40
+ export declare function useFormData<T extends Record<string, string | File | (string | File)[]>>(ctx: Context, options?: FormDataOptions): Promise<T>;
41
+ export declare function processFile(file: File, key: string, options: FormDataOptions, totalGetter: () => number): Promise<File>;
package/utils/formData.js CHANGED
@@ -1,10 +1,59 @@
1
- export function sanitized(title) {
2
- const base = title
3
- .toLowerCase()
4
- .trim()
5
- .replace(/[_\s]+/g, "-")
6
- .replace(/[^a-z0-9-.]+/g, "")
7
- .replace(/--+/g, "-")
8
- .replace(/^-+|-+$/g, "");
9
- return base;
1
+ import { sanitized } from "./low-level.js";
2
+ export async function useFormData(ctx, options) {
3
+ const fd = await ctx.req.formData();
4
+ const result = {};
5
+ let totalFileBytes = 0;
6
+ for (const [key, originalVal] of fd.entries()) {
7
+ let val = originalVal;
8
+ if (val instanceof File && options) {
9
+ val = await processFile(val, key, options, () => totalFileBytes);
10
+ totalFileBytes += val.size;
11
+ if (typeof options.maxTotalSize === "number" &&
12
+ totalFileBytes > options.maxTotalSize) {
13
+ throw new Error(`Total file bytes exceeded maxTotalSize=${options.maxTotalSize}`);
14
+ }
15
+ }
16
+ else if (typeof val === "string" &&
17
+ options?.maxFieldSize &&
18
+ val.length > options.maxFieldSize) {
19
+ throw new Error(`Field "${key}" length ${val.length} exceeds maxFieldSize=${options.maxFieldSize}`);
20
+ }
21
+ if (key in result) {
22
+ if (!Array.isArray(result[key])) {
23
+ result[key] = [result[key]];
24
+ }
25
+ result[key].push(val);
26
+ }
27
+ else {
28
+ result[key] = val;
29
+ }
30
+ if (val instanceof File && typeof options?.maxFiles === "number") {
31
+ const filesForKey = Array.isArray(result[key])
32
+ ? result[key].filter((v) => v instanceof File).length
33
+ : result[key] instanceof File
34
+ ? 1
35
+ : 0;
36
+ if (filesForKey > options.maxFiles) {
37
+ throw new Error(`Field "${key}" exceeds maxFiles (${options.maxFiles})`);
38
+ }
39
+ }
40
+ }
41
+ return result;
42
+ }
43
+ export async function processFile(file, key, options, totalGetter) {
44
+ let name = file.name;
45
+ if (options.sanitized) {
46
+ name = `${Date.now()}-${sanitized(name)}`;
47
+ }
48
+ if (Array.isArray(options.allowedTypes) &&
49
+ !options.allowedTypes.includes(file.type)) {
50
+ throw new Error(`Field "${key}": invalid file type "${file.type}". Allowed: ${options.allowedTypes.join(", ")}`);
51
+ }
52
+ if (typeof options.maxSize === "number" && file.size > options.maxSize) {
53
+ throw new Error(`Field "${key}": file size ${file.size} > maxSize ${options.maxSize}`);
54
+ }
55
+ if (name !== file.name) {
56
+ return new File([await file.arrayBuffer()], name, { type: file.type });
57
+ }
58
+ return file;
10
59
  }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Generate a fast, unique ID string using timestamp and random values.
3
+ *
4
+ * **Note:** This function is generally fast, but `generateUUID()` using
5
+ * `crypto.randomUUID()` is even faster and more standardized in modern environments.
6
+ *
7
+ * Use `generateUUID()` if you want a UUID v4 string that is cryptographically secure
8
+ * and ultra-fast (native implementation).
9
+ *
10
+ * Format: [timestamp in hex]-[random 12 hex chars]
11
+ *
12
+ * @returns {string} Unique ID string
13
+ */
14
+ export declare function generateID(): string;
15
+ /**
16
+ * Generate a cryptographically secure UUID (version 4).
17
+ *
18
+ * This uses the Web Crypto API's `crypto.randomUUID()` method to generate a
19
+ * unique identifier. The UUID conforms to RFC4122 version 4 standard.
20
+ *
21
+ * @returns {string} A unique UUID string (e.g., "e7b2bfb2-86f5-4a6d-9e7f-b5121a2b8e7d").
22
+ *
23
+ * @runtime Supported in:
24
+ * - ✅ Bun (native Web Crypto, extremely fast)
25
+ * - ✅ Deno (native Web Crypto, fast)
26
+ * - ✅ Node.js (v15+): via global `crypto.randomUUID()`
27
+ * ⚠️ In Node.js v14 or below, this will throw an error unless you polyfill `crypto`
28
+ *
29
+ * @example
30
+ * const uuid = generateUUID();
31
+ * console.log(uuid); // "a63e47b6-6a3b-4d53-90b6-8db2f2d07943"
32
+ */
33
+ export declare function generateUUID(): string;
34
+ /**
35
+ * Generate a short nonce (JS-only, low-GC) by sampling the BASE64 charset.
36
+ * This function intentionally uses Math.random for speed and low allocation.
37
+ * If you require cryptographic randomness, swap with crypto.randomFillSync.
38
+ *
39
+ * @param {number} [length=16] - nonce length in characters
40
+ * @returns {string} generated nonce string
41
+ */
42
+ export declare function generateRandomBase64(length?: number): string;
@@ -0,0 +1,32 @@
1
+ export function generateID() {
2
+ const timestamp = Date.now().toString(16);
3
+ let randomHex = "";
4
+ if (typeof crypto !== "undefined" &&
5
+ typeof crypto.getRandomValues === "function") {
6
+ const array = new Uint8Array(6);
7
+ crypto.getRandomValues(array);
8
+ for (let i = 0; i < array.length; i++) {
9
+ randomHex += array[i].toString(16).padStart(2, "0");
10
+ }
11
+ }
12
+ else {
13
+ randomHex = Math.floor(Math.random() * 0xffffffffffff)
14
+ .toString(16)
15
+ .padStart(12, "0");
16
+ }
17
+ return `${timestamp}-${randomHex}`;
18
+ }
19
+ export function generateUUID() {
20
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
21
+ return crypto.randomUUID();
22
+ return generateID();
23
+ }
24
+ export function generateRandomBase64(length = 16) {
25
+ let result = "";
26
+ const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
27
+ for (let i = 0; i < length; i++) {
28
+ const idx = Math.floor(Math.random() * 64);
29
+ result += BASE64[idx];
30
+ }
31
+ return result;
32
+ }
@@ -1 +1,15 @@
1
+ /**
2
+ * A mapping of HTTP status codes to their standard reason phrases.
3
+ *
4
+ * This object maps common HTTP status codes to their
5
+ * respective textual descriptions, useful for setting
6
+ * or interpreting HTTP response status texts.
7
+ *
8
+ * @constant {Record<number, string>}
9
+ *
10
+ * @example
11
+ * httpStatusMap[200]; // "OK"
12
+ * httpStatusMap[404]; // "Not Found"
13
+ * httpStatusMap[418]; // "I'm a Teapot"
14
+ */
1
15
  export declare const httpStatusMap: Record<number, string>;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Normalizes an HTTP header key to "Title-Case" format.
3
+ * For example, `"content-type"` becomes `"Content-Type"`.
4
+ *
5
+ * @param {string} key - The HTTP header key to normalize.
6
+ * @returns {string} The normalized header key in Title-Case format.
7
+ *
8
+ * @example
9
+ * normalizeHeaderKey("content-type"); // → "Content-Type"
10
+ * normalizeHeaderKey("x-custom-header"); // → "X-Custom-Header"
11
+ */
12
+ export declare function normalizeHeaderKey(key: string): string;
13
+ /**
14
+ * Extracts the file extension from a given file path or filename.
15
+ * The returned extension is always lowercase.
16
+ *
17
+ * @param {string} filePath - The full file path or filename.
18
+ * @returns {string} The file extension without the dot, or an empty string if none found.
19
+ *
20
+ * @example
21
+ * extensionExtract("image.PNG"); // → "png"
22
+ * extensionExtract("/path/to/archive.tar.gz"); // → "gz"
23
+ * extensionExtract("filename"); // → ""
24
+ */
25
+ export declare function extensionExtract(filePath: string): string;
26
+ /**
27
+ * Combines a base path and a relative path, then splits the combined path into segments,
28
+ * filtering out any `".."` segments to sanitize the path.
29
+ *
30
+ * @param {string} basePath - The base path (e.g., "/api").
31
+ * @param {string} path - The relative or additional path to combine (e.g., "users/123").
32
+ * @param {string[]} [out] - Optional array to append the segments to.
33
+ * @returns {string[]} Array of sanitized path segments (without `".."`).
34
+ *
35
+ * @example
36
+ * sanitizePathSplitBasePath("/api", "users/../admin"); // returns ["api", "admin"]
37
+ */
38
+ export declare function sanitizePathSplitBasePath(basePath: string, path: string, out?: string[]): string[];
39
+ /**
40
+ * Splits a path string into segments, ignoring `".."` segments to sanitize the path.
41
+ *
42
+ * @param {string} path - The path string to split (e.g., "/api/users/../admin").
43
+ * @param {string[]} [out] - Optional array to append the segments to.
44
+ * @returns {string[]} Array of sanitized path segments (without `".."`).
45
+ *
46
+ * @example
47
+ * sanitizePathSplit("/api/users/../admin"); // returns ["api", "users", "admin"]
48
+ */
49
+ export declare function sanitizePathSplit(path: string, out?: string[]): string[];
50
+ /**
51
+ * Ultra-fast string slugifier.
52
+ * Uses low-level string operations to reduce RegExp overhead.
53
+ * Falls back to minimal replacements if environment lacks full RegExp support.
54
+ *
55
+ * @param {string} title - Input string to slugify
56
+ * @returns {string} - Slugified, URL-safe string
57
+ */
58
+ export declare function sanitized(title: string): string;
@@ -0,0 +1,108 @@
1
+ export function normalizeHeaderKey(key) {
2
+ let result = "";
3
+ let upperNext = true;
4
+ for (let i = 0; i < key.length; i++) {
5
+ const ch = key.charCodeAt(i);
6
+ if (ch === 45) {
7
+ result += "-";
8
+ upperNext = true;
9
+ }
10
+ else {
11
+ result += upperNext
12
+ ? String.fromCharCode(ch >= 97 && ch <= 122 ? ch - 32 : ch)
13
+ : String.fromCharCode(ch >= 65 && ch <= 90 ? ch + 32 : ch);
14
+ upperNext = false;
15
+ }
16
+ }
17
+ return result;
18
+ }
19
+ export function extensionExtract(filePath) {
20
+ let lastDot = -1;
21
+ for (let i = filePath.length - 1; i >= 0; i--) {
22
+ if (filePath[i] === ".") {
23
+ lastDot = i;
24
+ break;
25
+ }
26
+ }
27
+ if (lastDot === -1 || lastDot === filePath.length - 1)
28
+ return "";
29
+ let ext = "";
30
+ for (let i = lastDot + 1; i < filePath.length; i++) {
31
+ let c = filePath.charCodeAt(i);
32
+ if (c >= 65 && c <= 90) {
33
+ c += 32;
34
+ }
35
+ ext += String.fromCharCode(c);
36
+ }
37
+ return ext;
38
+ }
39
+ export function sanitizePathSplitBasePath(basePath, path, out) {
40
+ const combined = `${basePath}/${path}`;
41
+ const parts = out ?? [];
42
+ let segStart = 0;
43
+ let i = 0;
44
+ const len = combined.length;
45
+ while (i < len) {
46
+ const code = combined.charCodeAt(i);
47
+ if (code === 47 || code === 92) {
48
+ if (segStart < i) {
49
+ const seg = combined.slice(segStart, i);
50
+ if (seg !== "..")
51
+ parts.push(seg);
52
+ }
53
+ segStart = i + 1;
54
+ }
55
+ i++;
56
+ }
57
+ if (segStart < len) {
58
+ const seg = combined.slice(segStart, len);
59
+ if (seg !== "..")
60
+ parts.push(seg);
61
+ }
62
+ return parts;
63
+ }
64
+ export function sanitizePathSplit(path, out) {
65
+ const parts = out ?? [];
66
+ let segStart = 0;
67
+ let i = 0;
68
+ const len = path.length;
69
+ while (i < len) {
70
+ const code = path.charCodeAt(i);
71
+ if (code === 47 || code === 92) {
72
+ if (segStart < i) {
73
+ const seg = path.slice(segStart, i);
74
+ if (seg !== "..")
75
+ parts.push(seg);
76
+ }
77
+ segStart = i + 1;
78
+ }
79
+ i++;
80
+ }
81
+ if (segStart < len) {
82
+ const seg = path.slice(segStart, len);
83
+ if (seg !== "..")
84
+ parts.push(seg);
85
+ }
86
+ return parts;
87
+ }
88
+ export function sanitized(title) {
89
+ const len = title.length;
90
+ let result = "";
91
+ let dash = false;
92
+ for (let i = 0; i < len; i++) {
93
+ let ch = title.charCodeAt(i);
94
+ if (ch >= 65 && ch <= 90)
95
+ ch += 32;
96
+ if ((ch >= 97 && ch <= 122) || (ch >= 48 && ch <= 57) || ch === 46) {
97
+ result += String.fromCharCode(ch);
98
+ dash = false;
99
+ }
100
+ else if (ch === 32 || ch === 95 || ch === 45) {
101
+ if (!dash && result.length > 0) {
102
+ result += "-";
103
+ dash = true;
104
+ }
105
+ }
106
+ }
107
+ return result.endsWith("-") ? result.slice(0, -1) : result;
108
+ }
@@ -0,0 +1,4 @@
1
+ export declare const mimeTypes: {
2
+ [key: string]: string;
3
+ };
4
+ export declare const defaultMimeType = "application/octet-stream";
@@ -1,4 +1,3 @@
1
- import { Environment } from "../core/environment.js";
2
1
  export const mimeTypes = {
3
2
  html: "text/html",
4
3
  htm: "text/html",
@@ -103,55 +102,3 @@ export const mimeTypes = {
103
102
  gcode: "text/x.gcode",
104
103
  };
105
104
  export const defaultMimeType = "application/octet-stream";
106
- export async function getFiles(dir, basePath = "/", ref, option) {
107
- const files = [];
108
- const runtime = Environment.getEnvironment;
109
- if (runtime == "deno") {
110
- for await (const entry of Deno.readDir(dir)) {
111
- const path = `${dir}/${entry.name}`;
112
- if (entry.isDirectory) {
113
- files.push(...(await getFiles(path, `${basePath}/${entry.name}`, ref, option)));
114
- }
115
- else {
116
- const x = `${basePath}/${entry.name}`;
117
- files.push({
118
- file: path,
119
- path: x.replace(/\\/g, "/"),
120
- });
121
- }
122
- }
123
- }
124
- else {
125
- const fs = await import("node:fs/promises");
126
- const path = await import("node:path");
127
- const entries = await fs.readdir(dir, { withFileTypes: true });
128
- for (const entry of entries) {
129
- const fullPath = path.join(dir, entry.name);
130
- if (entry.isDirectory()) {
131
- files.push(...(await getFiles(fullPath, `${basePath}/${entry.name}`, ref, option)));
132
- }
133
- else {
134
- const path = `${basePath}/${entry.name}`;
135
- files.push({
136
- file: fullPath,
137
- path: path.replace(/\\/g, "/"),
138
- });
139
- }
140
- }
141
- }
142
- files.forEach((r) => {
143
- ref.get(r.path, (ctx) => {
144
- if (option.cacheControl) {
145
- ctx.headers.set("Cache-Control", option.cacheControl);
146
- }
147
- if (option.headers) {
148
- for (const key in option.headers) {
149
- let value = option.headers?.[key];
150
- ctx.headers.set(key, value);
151
- }
152
- }
153
- return ctx.sendFile(r.file);
154
- });
155
- });
156
- return files;
157
- }
@@ -0,0 +1,18 @@
1
+ export declare function createRateLimitDefaultStorage(): {
2
+ get: (key: string) => {
3
+ count: number;
4
+ resetTime: number;
5
+ } | undefined;
6
+ set: (key: string, value: {
7
+ count: number;
8
+ resetTime: number;
9
+ }) => Map<string, {
10
+ count: number;
11
+ resetTime: number;
12
+ }>;
13
+ clearExpired: () => void;
14
+ };
15
+ export declare function isRateLimit(key: string, store: any, maxRequests: number, windowMs: number): {
16
+ check: boolean;
17
+ entry: any;
18
+ };
@@ -0,0 +1,37 @@
1
+ export function createRateLimitDefaultStorage() {
2
+ const store = new Map();
3
+ return {
4
+ get: (key) => store.get(key),
5
+ set: (key, value) => store.set(key, value),
6
+ clearExpired: () => {
7
+ const now = Date.now();
8
+ for (const [key, entry] of store.entries()) {
9
+ if (now >= entry.resetTime) {
10
+ store.delete(key);
11
+ }
12
+ }
13
+ },
14
+ };
15
+ }
16
+ export function isRateLimit(key, store, maxRequests, windowMs) {
17
+ store?.clearExpired();
18
+ const now = Date.now();
19
+ let entry = store.get(key) || { count: 0, resetTime: now + windowMs };
20
+ if (now < entry.resetTime) {
21
+ entry.count++;
22
+ if (entry.count > maxRequests) {
23
+ return {
24
+ check: true,
25
+ entry: entry,
26
+ };
27
+ }
28
+ }
29
+ else {
30
+ entry = { count: 1, resetTime: now + windowMs };
31
+ }
32
+ store.set(key, entry);
33
+ return {
34
+ check: false,
35
+ entry: entry,
36
+ };
37
+ }
@@ -0,0 +1,18 @@
1
+ import { Context } from "../core/context.js";
2
+ import { TezXError } from "../core/error.js";
3
+ import { HttpBaseResponse } from "../types/index.js";
4
+ export declare let notFoundResponse: (ctx: Context) => HttpBaseResponse;
5
+ export declare function handleErrorResponse(err: TezXError | undefined, ctx: Context): Promise<HttpBaseResponse>;
6
+ /**
7
+ * Converts a template literal array and values into a single string.
8
+ * If a plain string is passed, it returns the string itself.
9
+ *
10
+ * @param input - A string or a template literal array.
11
+ * @param values - Values for template literal placeholders.
12
+ * @returns Concatenated string.
13
+ */
14
+ export declare function toString(input: string | readonly string[], values: any[]): string;
15
+ export declare function determineContentTypeBody(body: any): {
16
+ type: string;
17
+ body: any;
18
+ };
@@ -0,0 +1,58 @@
1
+ import { GlobalConfig } from "../core/config.js";
2
+ import { TezXError } from "../core/error.js";
3
+ export let notFoundResponse = (ctx) => {
4
+ const { method, pathname } = ctx;
5
+ return ctx.text(`${method}: '${pathname}' could not find\n`, {
6
+ status: 404,
7
+ });
8
+ };
9
+ export async function handleErrorResponse(err = TezXError.internal(), ctx) {
10
+ if (err instanceof TezXError) {
11
+ GlobalConfig.debugging.error(err.details ?? err?.message);
12
+ return ctx
13
+ .status(err.statusCode ?? 500)
14
+ .send(err.details ?? err?.message ?? "Internal Server Error");
15
+ }
16
+ return await handleErrorResponse(TezXError.internal(), ctx);
17
+ }
18
+ export function toString(input, values) {
19
+ if (typeof input === "string") {
20
+ return input;
21
+ }
22
+ let result = "";
23
+ for (let i = 0; i < input.length; i++) {
24
+ result += input[i];
25
+ if (i < values.length)
26
+ result += values[i];
27
+ }
28
+ return result;
29
+ }
30
+ export function determineContentTypeBody(body) {
31
+ if (typeof body === "string" ||
32
+ typeof body === "number" ||
33
+ typeof body === "boolean") {
34
+ return { type: "text/plain; charset=utf-8", body: String(body) };
35
+ }
36
+ if (body instanceof Uint8Array || body instanceof ArrayBuffer) {
37
+ return { type: "application/octet-stream", body };
38
+ }
39
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(body)) {
40
+ return { type: "application/octet-stream", body };
41
+ }
42
+ if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream) {
43
+ return { type: "application/octet-stream", body };
44
+ }
45
+ if (typeof Blob !== "undefined" && body instanceof Blob) {
46
+ return { type: body.type || "application/octet-stream", body };
47
+ }
48
+ if (typeof body === "object" && typeof body?.pipe === "function") {
49
+ return { type: "application/octet-stream", body };
50
+ }
51
+ if (typeof body === "object") {
52
+ return {
53
+ type: "application/json; charset=utf-8",
54
+ body: JSON.stringify(body),
55
+ };
56
+ }
57
+ return { type: "text/plain; charset=utf-8", body: String(body ?? "") };
58
+ }
@@ -1,3 +1,4 @@
1
1
  export declare class Environment {
2
2
  static get getEnvironment(): "node" | "bun" | "deno" | "unknown";
3
3
  }
4
+ export declare const runtime: "node" | "bun" | "deno" | "unknown";
@@ -9,3 +9,4 @@ export class Environment {
9
9
  return "unknown";
10
10
  }
11
11
  }
12
+ export const runtime = Environment.getEnvironment;