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/core/request.d.ts CHANGED
@@ -1,136 +1,96 @@
1
- import { ExtractParamsFromPath, FormDataOptions, HTTPMethod, NetAddr, PathType } from "../types/index.js";
2
- import type { UrlRef } from "../utils/url.js";
3
- import { TezXServeOptions } from "./server.js";
4
- export declare class Request<Path extends PathType = any> {
1
+ import { ExtractParamsFromPath, HTTPMethod, NetAddr, ReqHeaderKey, RequestHeaders } from "../types/index.js";
2
+ /**
3
+ * A wrapper around the raw HTTP request that provides convenient access to URL, headers, body parsing, and route parameters.
4
+ *
5
+ * @template Path - The route path string used to extract dynamic route parameters.
6
+ */
7
+ export declare class TezXRequest<Path extends string = any> {
8
+ #private;
5
9
  /**
6
- * Full request URL including protocol and query string
10
+ * Full request URL including protocol and query string.
7
11
  * @type {string}
8
12
  */
9
13
  readonly url: string;
10
14
  /**
11
- * HTTP request method (GET, POST, PUT, DELETE, etc.)
15
+ * HTTP request method (e.g., GET, POST, PUT, DELETE).
12
16
  * @type {HTTPMethod}
13
17
  */
14
18
  readonly method: HTTPMethod;
15
19
  /**
16
- * Request path without query parameters
20
+ * Request path without query parameters.
17
21
  * @type {string}
18
22
  */
19
23
  readonly pathname: string;
20
- /** Parsed URL reference containing components like query parameters, pathname, etc. */
21
- readonly urlRef: UrlRef;
22
- /** Query parameters extracted from the URL */
23
- readonly query: Record<string, any>;
24
- protected rawRequest: any;
25
24
  /**
26
- * Retrieve a parameter by name.
27
- * @param name - The parameter name.
28
- * @returns The parameter value if found, or undefined.
25
+ * Route parameters extracted from the path.
26
+ * @type {ExtractParamsFromPath<Path>}
29
27
  */
30
28
  readonly params: ExtractParamsFromPath<Path>;
31
29
  /**
32
- * Represents the remote address details of a connected client.
30
+ * Remote address details of the connected client.
31
+ * @requires injectRemoteAddress middleware.
32
+ * @typedef {Object} NetAddr
33
+ * @property {string} [transport] - Transport protocol (e.g., "tcp", "udp").
34
+ * @property {"IPv4" | "IPv6" | "Unix"} [family] - Address family.
35
+ * @property {string} [hostname] - Hostname or IP address.
36
+ * @property {number} [port] - Port number.
37
+ * @type {NetAddr}
38
+ * @default {}
39
+ */
40
+ remoteAddress: NetAddr;
41
+ /**
42
+ * Creates an instance of TezXRequest.
33
43
  *
34
- * @property {TransportType} [transport] - The transport protocol used (e.g., `"tcp"`, `"udp"`).
35
- * @property {"IPv4" | "IPv6" | "Unix"} [family] - The address family, indicating whether the connection is over IPv4, IPv6, or a Unix socket.
36
- * @property {string} [hostname] - The hostname or IP address of the remote client.
37
- * @property {number} [port] - The remote client's port number.
38
- * @default {{}}
44
+ * @param {Request} req - The raw Request object.
45
+ * @param {string} method - The HTTP method.
46
+ * @param {string} pathname - The request path without query parameters.
47
+ * @param {ExtractParamsFromPath<Path>} params - Route parameters extracted from the path.
48
+ */
49
+ constructor(req: Request, method: string, pathname: string, params: ExtractParamsFromPath<Path>);
50
+ /**
51
+ * Gets a single header value by name, or all headers as an object if no name is provided.
39
52
  *
40
- * @example
41
- * ```typescript
42
- * ctx.req.remoteAddress
43
- * ```
53
+ * @param {ReqHeaderKey} [header] - The header name.
54
+ * @returns {string | undefined | RequestHeaders} The header value or map of all headers.
44
55
  */
45
- remoteAddress: NetAddr;
46
- constructor({ params, method, req, options, }: {
47
- method: string;
48
- req: any;
49
- params: Record<string, any>;
50
- options: TezXServeOptions;
51
- });
52
- get headers(): {
53
- /**
54
- * Retrieves the first value of a specific header.
55
- * @param key - Header name to search for.
56
- * @returns The first header value or null if not found.
57
- * @example
58
- * get('content-type') // returns 'application/json'
59
- */
60
- get: (key: string) => string | null;
61
- /**
62
- * Checks if a header exists in the request.
63
- * @param key - Header name to check for existence.
64
- * @returns True if the header exists, false otherwise.
65
- * @example
66
- * has('Authorization') // returns true if 'Authorization' header exists
67
- */
68
- has: (key: string) => boolean;
69
- /**
70
- * Returns an iterator over all header entries.
71
- * Each entry is a [key, value] pair where the value can be an array of strings.
72
- * @returns HeadersIterator for iterating over header key-value pairs.
73
- * @example
74
- * for (let [key, value] of headers.entries()) {
75
- * console.log(key, value);
76
- * }
77
- */
78
- entries: () => HeadersIterator<[string, string]>;
79
- /**
80
- * Returns an iterator over all header keys.
81
- * This allows iteration over the names of all headers in the request.
82
- * @returns HeadersIterator of header names.
83
- * @example
84
- * for (let key of headers.keys()) {
85
- * console.log(key);
86
- * }
87
- */
88
- keys: () => HeadersIterator<string>;
89
- /**
90
- * Returns an iterator over all header values.
91
- * This allows iteration over the values of all headers, with each value being an array of strings.
92
- * @returns HeadersIterator<string> of header values.
93
- * @example
94
- * for (let value of headers.values()) {
95
- * console.log(value);
96
- * }
97
- */
98
- values: () => HeadersIterator<string>;
99
- /**
100
- * Iterates over each header and executes a callback for every header found.
101
- * @param callback - Function to execute for each header. Receives the value array and key.
102
- * @example
103
- * headers.forEach((key, value) => {
104
- * console.log(key, value);
105
- * });
106
- */
107
- forEach: (callbackfn: (value: string, key: string, parent: Headers) => void) => void;
108
- /**
109
- * Converts headers to a JSON-safe plain object (only single string values).
110
- * Multi-value headers are joined by commas.
111
- * @returns A record of headers with string values.
112
- */
113
- toJSON(): Record<string, string>;
114
- };
56
+ header(): RequestHeaders;
57
+ header(header: ReqHeaderKey): string | undefined;
58
+ /**
59
+ * Parses the query string from the URL into a key-value object.
60
+ *
61
+ * @returns {Record<string, any>} Query parameters.
62
+ */
63
+ get query(): Record<string, string | string[]>;
115
64
  /**
116
65
  * Parses the request body as plain text.
66
+ *
117
67
  * @returns {Promise<string>} The text content of the request body.
118
68
  */
119
- text(): Promise<any>;
69
+ text(): Promise<string>;
120
70
  /**
121
71
  * Parses the request body as JSON.
122
- * @returns {Promise<Record<string, any>>} The parsed JSON object.
123
- * If the Content-Type is not 'application/json', it returns an empty object.
72
+ *
73
+ * @template T - Expected type of parsed JSON object.
74
+ * @returns {Promise<T>} Parsed JSON or empty object if not application/json.
124
75
  */
125
- json(): Promise<any>;
76
+ json<T extends Record<string, any>>(): Promise<T>;
126
77
  /**
127
- * Parses the request body based on Content-Type.
128
- * Supports:
129
- * - application/json → JSON parsing
130
- * - application/x-www-form-urlencoded → URL-encoded form parsing
131
- * - multipart/form-data → Multipart form-data parsing (for file uploads)
132
- * @returns {Promise<Record<string, any>>} The parsed form data as an object.
133
- * @throws {Error} If the Content-Type is missing or invalid.
78
+ * Parses and returns the form data from the incoming HTTP request.
79
+ *
80
+ * Supports:
81
+ * - `application/x-www-form-urlencoded`
82
+ * - `multipart/form-data`
83
+ *
84
+ * ⚠️ Throws:
85
+ * - If the `Content-Type` header is missing.
86
+ * - If the body has already been read/consumed elsewhere.
87
+ *
88
+ * 🧠 Internally caches the parsed `FormData` to prevent multiple reads.
89
+ *
90
+ * @returns {Promise<FormData>}
91
+ * A promise that resolves to a native `FormData` object.
92
+ *
93
+ * @throws {Error} If the content type is missing or the request body has already been consumed.
134
94
  */
135
- formData(options?: FormDataOptions): Promise<Record<string, any>>;
95
+ formData(): Promise<FormData>;
136
96
  }
package/core/request.js CHANGED
@@ -1,148 +1,96 @@
1
- import { sanitized } from "../utils/formData.js";
2
- import { urlParse } from "../utils/url.js";
3
- export class Request {
1
+ import { url2query } from "../utils/url.js";
2
+ import { TezXError } from "./error.js";
3
+ export class TezXRequest {
4
4
  url;
5
5
  method;
6
6
  pathname;
7
- urlRef = {
8
- protocol: undefined,
9
- origin: undefined,
10
- hostname: undefined,
11
- port: undefined,
12
- href: undefined,
13
- query: {},
14
- pathname: "/",
15
- };
16
- query;
17
- rawRequest;
7
+ #rawRequest;
18
8
  params = {};
19
9
  remoteAddress = {};
20
- constructor({ params, method, req, options, }) {
21
- let parse = urlParse(req.url);
22
- let url = parse?.href;
23
- this.remoteAddress = options?.connInfo?.remoteAddr;
24
- this.url = url || "";
25
- this.urlRef = parse;
10
+ #bodyConsumed = false;
11
+ #rawBodyArrayBuffer;
12
+ #cachedText;
13
+ #cachedJSON;
14
+ #cachedFormObject;
15
+ #headersCache;
16
+ #queryCache;
17
+ constructor(req, method, pathname, params) {
18
+ this.url = req.url;
19
+ this.params = params ?? {};
26
20
  this.method = method;
27
- this.params = params;
28
- this.rawRequest = req;
29
- this.pathname = parse.pathname;
30
- this.query = parse.query;
21
+ this.#rawRequest = req;
22
+ this.pathname = pathname ?? "/";
31
23
  }
32
- get headers() {
33
- let requestHeaders = this.rawRequest.headers;
34
- return {
35
- get: function get(key) {
36
- return requestHeaders.get(key.toLowerCase());
37
- },
38
- has: function has(key) {
39
- return requestHeaders.has(key.toLowerCase());
40
- },
41
- entries: function entries() {
42
- return requestHeaders.entries();
43
- },
44
- keys: function keys() {
45
- return requestHeaders.keys();
46
- },
47
- values: function values() {
48
- return requestHeaders.values();
49
- },
50
- forEach: function forEach(callbackfn) {
51
- return requestHeaders.forEach(callbackfn);
52
- },
53
- toJSON() {
54
- const obj = {};
55
- for (const [key, value] of requestHeaders.entries()) {
56
- obj[key] = value;
57
- }
58
- return obj;
59
- },
60
- };
24
+ header(header) {
25
+ if (header) {
26
+ return this.#rawRequest.headers.get(header.toLowerCase());
27
+ }
28
+ if (this.#headersCache)
29
+ return this.#headersCache;
30
+ const obj = {};
31
+ this.#rawRequest.headers.forEach((value, key) => {
32
+ obj[key.toLowerCase()] = value;
33
+ });
34
+ this.#headersCache = obj;
35
+ return this.#headersCache;
36
+ }
37
+ get query() {
38
+ return (this.#queryCache ??= url2query(this.url));
39
+ }
40
+ async #ensureRawBuffer() {
41
+ if (this.#bodyConsumed)
42
+ return;
43
+ this.#rawBodyArrayBuffer = await this.#rawRequest.arrayBuffer();
44
+ this.#bodyConsumed = true;
61
45
  }
62
46
  async text() {
63
- return await this.rawRequest.text();
47
+ if (this.#cachedText !== undefined)
48
+ return this.#cachedText;
49
+ await this.#ensureRawBuffer();
50
+ this.#cachedText = new TextDecoder().decode(this.#rawBodyArrayBuffer);
51
+ return this.#cachedText;
52
+ }
53
+ get #contentType() {
54
+ const ct = this.#rawRequest.headers.get("content-type");
55
+ if (!ct)
56
+ return "";
57
+ return ct.split(";")[0].trim().toLowerCase();
64
58
  }
65
59
  async json() {
66
- const contentType = this.rawRequest.headers.get("content-type") || "";
67
- if (contentType.includes("application/json")) {
68
- return await this.rawRequest.json();
69
- }
70
- else {
60
+ if (this.#cachedJSON !== undefined)
61
+ return this.#cachedJSON;
62
+ if (this.#contentType !== "application/json")
71
63
  return {};
72
- }
73
- }
74
- async formData(options) {
75
- const contentType = this.rawRequest.headers.get("content-type") || "";
76
- if (!contentType) {
77
- throw Error("Invalid Content-Type");
78
- }
79
- if (contentType.includes("application/json")) {
80
- return await this.rawRequest.json();
81
- }
82
- else if (contentType.includes("application/x-www-form-urlencoded")) {
83
- const formData = await this.rawRequest.formData();
84
- const result = {};
85
- for (const [key, value] of formData.entries()) {
86
- result[key] = value;
87
- }
88
- return result;
89
- }
90
- else if (contentType.includes("multipart/form-data")) {
91
- const boundaryMatch = contentType.match(/boundary=([^;]+)/);
92
- if (!boundaryMatch) {
93
- throw new Error("Boundary not found in multipart/form-data");
64
+ try {
65
+ if (!this.#bodyConsumed) {
66
+ this.#cachedJSON = await this.#rawRequest.json();
67
+ this.#bodyConsumed = true;
94
68
  }
95
- const formData = await this.rawRequest.formData();
96
- const result = {};
97
- for (const [key, value] of formData.entries()) {
98
- let val = value;
99
- if (val instanceof File && typeof options == "object") {
100
- let filename = val.name;
101
- if (options?.sanitized) {
102
- filename = `${Date.now()}-${sanitized(filename)}`;
103
- }
104
- if (Array.isArray(options?.allowedTypes) &&
105
- !options.allowedTypes?.includes(val.type)) {
106
- throw new Error(`Invalid file type: "${val.type}". Allowed types: ${options.allowedTypes.join(", ")}`);
107
- }
108
- if (typeof options?.maxSize !== "undefined" &&
109
- val.size > options.maxSize) {
110
- throw new Error(`File size exceeds the limit: ${val.size} bytes (Max: ${options.maxSize} bytes)`);
111
- }
112
- if (typeof options?.maxFiles != "undefined" &&
113
- options.maxFiles == 0) {
114
- throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
115
- }
116
- val = new File([await val.arrayBuffer()], filename, {
117
- type: val.type,
118
- });
119
- }
120
- if (result[key]) {
121
- if (Array.isArray(result[key])) {
122
- if (val instanceof File &&
123
- typeof options?.maxFiles != "undefined" &&
124
- result[key]?.length >= options.maxFiles) {
125
- throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
126
- }
127
- result[key].push(val);
128
- }
129
- else {
130
- if (val instanceof File &&
131
- typeof options?.maxFiles != "undefined" &&
132
- options.maxFiles == 1) {
133
- throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
134
- }
135
- result[key] = [result[key], val];
136
- }
137
- }
138
- else {
139
- result[key] = val;
140
- }
69
+ else {
70
+ const txt = await this.text();
71
+ this.#cachedJSON = txt ? JSON.parse(txt) : {};
141
72
  }
142
- return result;
143
73
  }
144
- else {
145
- return {};
74
+ catch {
75
+ this.#cachedJSON = {};
76
+ }
77
+ return this.#cachedJSON;
78
+ }
79
+ async formData() {
80
+ if (this.#cachedFormObject)
81
+ return this.#cachedFormObject;
82
+ const ct = this.#contentType;
83
+ if (!ct)
84
+ throw new TezXError("Missing Content-Type");
85
+ if (ct === "application/x-www-form-urlencoded" ||
86
+ ct === "multipart/form-data") {
87
+ if (this.#bodyConsumed) {
88
+ throw new TezXError("Multipart body already consumed elsewhere");
89
+ }
90
+ this.#cachedFormObject = (await this.#rawRequest.formData());
91
+ this.#bodyConsumed = true;
92
+ return this.#cachedFormObject;
146
93
  }
94
+ return this.#cachedFormObject;
147
95
  }
148
96
  }