tezx 1.0.74 → 1.0.76-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/adapter/bun.d.ts +29 -0
  2. package/adapter/deno.d.ts +30 -0
  3. package/adapter/index.d.ts +1 -1
  4. package/adapter/index.js +1 -1
  5. package/adapter/node/index.d.ts +47 -0
  6. package/adapter/{node.js → node/index.js} +2 -2
  7. package/cjs/adapter/index.js +1 -1
  8. package/cjs/adapter/{node.js → node/index.js} +2 -2
  9. package/cjs/core/context.js +13 -97
  10. package/cjs/core/environment.js +0 -8
  11. package/cjs/core/request.js +69 -25
  12. package/cjs/core/router.js +5 -0
  13. package/cjs/core/server.js +6 -5
  14. package/cjs/index.js +1 -1
  15. package/cjs/middleware/cors.js +1 -1
  16. package/cjs/middleware/sanitizeHeader.js +2 -2
  17. package/cjs/utils/formData.js +0 -235
  18. package/cjs/utils/httpStatusMap.js +68 -0
  19. package/cjs/utils/staticFile.js +4 -1
  20. package/cjs/utils/toWebRequest.js +35 -0
  21. package/core/context.d.ts +5 -6
  22. package/core/context.js +14 -98
  23. package/core/environment.d.ts +0 -2
  24. package/core/environment.js +0 -8
  25. package/core/request.d.ts +11 -32
  26. package/core/request.js +70 -26
  27. package/core/router.d.ts +29 -0
  28. package/core/router.js +5 -0
  29. package/core/server.js +5 -4
  30. package/index.js +1 -1
  31. package/middleware/cors.js +1 -1
  32. package/middleware/sanitizeHeader.js +2 -2
  33. package/package.json +1 -1
  34. package/utils/formData.d.ts +0 -5
  35. package/utils/formData.js +0 -231
  36. package/utils/httpStatusMap.d.ts +1 -0
  37. package/utils/httpStatusMap.js +65 -0
  38. package/utils/staticFile.js +4 -1
  39. package/utils/toWebRequest.d.ts +11 -0
  40. package/utils/toWebRequest.js +32 -0
  41. package/adapter/node.d.ts +0 -19
  42. package/cjs/core/header.js +0 -92
  43. package/core/header.d.ts +0 -77
  44. package/core/header.js +0 -88
package/core/request.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { UrlRef } from "../utils/url.js";
2
- import { HeadersParser } from "./header.js";
3
2
  import { TezXServeOptions } from "./server.js";
4
3
  export type FormDataOptions = {
5
4
  maxSize?: number;
@@ -20,7 +19,6 @@ export type ConnAddress = {
20
19
  };
21
20
  export type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS" | "PATCH" | "HEAD" | "ALL" | "TRACE" | "CONNECT" | string;
22
21
  export declare class Request {
23
- #private;
24
22
  /**
25
23
  * Full request URL including protocol and query string
26
24
  * @type {string}
@@ -57,31 +55,21 @@ export declare class Request {
57
55
  * ```
58
56
  */
59
57
  remoteAddress: NetAddr;
60
- constructor({ headers, params, req, options, urlRef, }: {
58
+ constructor({ params, method, req, options, }: {
59
+ method: string;
61
60
  req: any;
62
61
  params: Record<string, any>;
63
- headers: HeadersParser;
64
- urlRef: UrlRef;
65
62
  options: TezXServeOptions;
66
63
  });
67
64
  get headers(): {
68
65
  /**
69
66
  * Retrieves the first value of a specific header.
70
67
  * @param key - Header name to search for.
71
- * @returns The first header value or undefined if not found.
68
+ * @returns The first header value or null if not found.
72
69
  * @example
73
70
  * get('content-type') // returns 'application/json'
74
71
  */
75
- get: (key: string) => string | undefined;
76
- /**
77
- * Retrieves all values of a specific header.
78
- * If multiple values exist for a header, all will be returned as an array.
79
- * @param key - Header name to search for.
80
- * @returns An array of all header values associated with the key.
81
- * @example
82
- * getAll('accept-language') // returns ['en-US', 'fr-CA']
83
- */
84
- getAll: (key: string) => string | never[];
72
+ get: (key: string) => string | null;
85
73
  /**
86
74
  * Checks if a header exists in the request.
87
75
  * @param key - Header name to check for existence.
@@ -93,13 +81,13 @@ export declare class Request {
93
81
  /**
94
82
  * Returns an iterator over all header entries.
95
83
  * Each entry is a [key, value] pair where the value can be an array of strings.
96
- * @returns IterableIterator for iterating over header key-value pairs.
84
+ * @returns HeadersIterator for iterating over header key-value pairs.
97
85
  * @example
98
86
  * for (let [key, value] of headers.entries()) {
99
87
  * console.log(key, value);
100
88
  * }
101
89
  */
102
- entries: () => IterableIterator<[string, string[]]>;
90
+ entries: () => HeadersIterator<[string, string]>;
103
91
  /**
104
92
  * Returns an iterator over all header keys.
105
93
  * This allows iteration over the names of all headers in the request.
@@ -113,13 +101,13 @@ export declare class Request {
113
101
  /**
114
102
  * Returns an iterator over all header values.
115
103
  * This allows iteration over the values of all headers, with each value being an array of strings.
116
- * @returns IterableIterator of header values.
104
+ * @returns HeadersIterator<string> of header values.
117
105
  * @example
118
106
  * for (let value of headers.values()) {
119
107
  * console.log(value);
120
108
  * }
121
109
  */
122
- values: () => IterableIterator<string[]>;
110
+ values: () => HeadersIterator<string>;
123
111
  /**
124
112
  * Iterates over each header and executes a callback for every header found.
125
113
  * @param callback - Function to execute for each header. Receives the value array and key.
@@ -128,34 +116,25 @@ export declare class Request {
128
116
  * console.log(key, value);
129
117
  * });
130
118
  */
131
- forEach: (callback: (value: string[], key: string) => void) => void;
119
+ forEach: (callbackfn: (value: string, key: string, parent: Headers) => void) => void;
132
120
  /**
133
121
  * Converts headers to a JSON-safe plain object (only single string values).
134
122
  * Multi-value headers are joined by commas.
135
123
  * @returns A record of headers with string values.
136
124
  */
137
125
  toJSON(): Record<string, string>;
138
- /**
139
- * Converts all headers into a plain JavaScript object.
140
- * Single-value headers are represented as a string, and multi-value headers as an array.
141
- * @returns A plain object with header names as keys and their values as strings or arrays.
142
- * @example
143
- * const headersObject = headers.toObject();
144
- * console.log(headersObject);
145
- */
146
- toObject: () => Record<string, string | string[]>;
147
126
  };
148
127
  /**
149
128
  * Parses the request body as plain text.
150
129
  * @returns {Promise<string>} The text content of the request body.
151
130
  */
152
- text(): Promise<string>;
131
+ text(): Promise<any>;
153
132
  /**
154
133
  * Parses the request body as JSON.
155
134
  * @returns {Promise<Record<string, any>>} The parsed JSON object.
156
135
  * If the Content-Type is not 'application/json', it returns an empty object.
157
136
  */
158
- json(): Promise<Record<string, any>>;
137
+ json(): Promise<any>;
159
138
  /**
160
139
  * Parses the request body based on Content-Type.
161
140
  * Supports:
package/core/request.js CHANGED
@@ -1,7 +1,6 @@
1
- import { parseJsonBody, parseMultipartBody, parseTextBody, parseUrlEncodedBody, } from "../utils/formData.js";
2
- import { HeadersParser } from "./header.js";
1
+ import { sanitized } from "../utils/formData.js";
2
+ import { urlParse } from "../utils/url.js";
3
3
  export class Request {
4
- #headers = new HeadersParser();
5
4
  url;
6
5
  method;
7
6
  urlRef = {
@@ -17,25 +16,23 @@ export class Request {
17
16
  rawRequest;
18
17
  params = {};
19
18
  remoteAddress = {};
20
- constructor({ headers, params, req, options, urlRef, }) {
19
+ constructor({ params, method, req, options, }) {
20
+ let parse = urlParse(req.url);
21
+ let url = parse?.href;
21
22
  this.remoteAddress = options?.connInfo?.remoteAddr;
22
- this.#headers = headers;
23
- this.url = urlRef.href || "";
24
- this.urlRef = urlRef;
25
- this.method = req?.method?.toUpperCase();
23
+ this.url = url || "";
24
+ this.urlRef = parse;
25
+ this.method = method;
26
26
  this.params = params;
27
27
  this.rawRequest = req;
28
- this.query = urlRef.query;
28
+ this.query = parse.query;
29
29
  }
30
30
  get headers() {
31
- let requestHeaders = this.#headers;
31
+ let requestHeaders = this.rawRequest.headers;
32
32
  return {
33
33
  get: function get(key) {
34
34
  return requestHeaders.get(key.toLowerCase());
35
35
  },
36
- getAll: function getAll(key) {
37
- return requestHeaders.get(key.toLowerCase()) || [];
38
- },
39
36
  has: function has(key) {
40
37
  return requestHeaders.has(key.toLowerCase());
41
38
  },
@@ -48,47 +45,94 @@ export class Request {
48
45
  values: function values() {
49
46
  return requestHeaders.values();
50
47
  },
51
- forEach: function forEach(callback) {
52
- return requestHeaders.forEach(callback);
48
+ forEach: function forEach(callbackfn) {
49
+ return requestHeaders.forEach(callbackfn);
53
50
  },
54
51
  toJSON() {
55
52
  return requestHeaders.toJSON();
56
53
  },
57
- toObject: function toObject() {
58
- return requestHeaders.toObject();
59
- },
60
54
  };
61
55
  }
62
56
  async text() {
63
- return await parseTextBody(this.rawRequest);
57
+ return await this.rawRequest.text();
64
58
  }
65
59
  async json() {
66
- const contentType = this.#headers.get("content-type") || "";
60
+ const contentType = this.rawRequest.headers.get("content-type") || "";
67
61
  if (contentType.includes("application/json")) {
68
- return await parseJsonBody(this.rawRequest);
62
+ return await this.rawRequest.json();
69
63
  }
70
64
  else {
71
65
  return {};
72
66
  }
73
67
  }
74
68
  async formData(options) {
75
- const contentType = this.#headers.get("content-type") || "";
69
+ const contentType = this.rawRequest.headers.get("content-type") || "";
76
70
  if (!contentType) {
77
71
  throw Error("Invalid Content-Type");
78
72
  }
79
73
  if (contentType.includes("application/json")) {
80
- return await parseJsonBody(this.rawRequest);
74
+ return await this.rawRequest.json();
81
75
  }
82
76
  else if (contentType.includes("application/x-www-form-urlencoded")) {
83
- return parseUrlEncodedBody(this.rawRequest);
77
+ const formData = await this.rawRequest.formData();
78
+ const result = {};
79
+ for (const [key, value] of formData.entries()) {
80
+ result[key] = value;
81
+ }
82
+ return result;
84
83
  }
85
84
  else if (contentType.includes("multipart/form-data")) {
86
85
  const boundaryMatch = contentType.match(/boundary=([^;]+)/);
87
86
  if (!boundaryMatch) {
88
87
  throw new Error("Boundary not found in multipart/form-data");
89
88
  }
90
- const boundary = boundaryMatch[1];
91
- return await parseMultipartBody(this.rawRequest, boundary, options);
89
+ const formData = await this.rawRequest.formData();
90
+ const result = {};
91
+ for (const [key, value] of formData.entries()) {
92
+ let val = value;
93
+ if (val instanceof File && typeof options == "object") {
94
+ let filename = val.name;
95
+ if (options?.sanitized) {
96
+ filename = `${Date.now()}-${sanitized(filename)}`;
97
+ }
98
+ if (Array.isArray(options?.allowedTypes) &&
99
+ !options.allowedTypes?.includes(val.type)) {
100
+ throw new Error(`Invalid file type: "${val.type}". Allowed types: ${options.allowedTypes.join(", ")}`);
101
+ }
102
+ if (typeof options?.maxSize !== "undefined" &&
103
+ val.size > options.maxSize) {
104
+ throw new Error(`File size exceeds the limit: ${val.size} bytes (Max: ${options.maxSize} bytes)`);
105
+ }
106
+ if (typeof options?.maxFiles != "undefined" && options.maxFiles == 0) {
107
+ throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
108
+ }
109
+ val = new File([await val.arrayBuffer()], filename, {
110
+ type: val.type,
111
+ });
112
+ }
113
+ if (result[key]) {
114
+ if (Array.isArray(result[key])) {
115
+ if (val instanceof File &&
116
+ typeof options?.maxFiles != "undefined" &&
117
+ result[key]?.length >= options.maxFiles) {
118
+ throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
119
+ }
120
+ result[key].push(val);
121
+ }
122
+ else {
123
+ if (val instanceof File &&
124
+ typeof options?.maxFiles != "undefined" &&
125
+ options.maxFiles == 1) {
126
+ throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
127
+ }
128
+ result[key] = [result[key], val];
129
+ }
130
+ }
131
+ else {
132
+ result[key] = val;
133
+ }
134
+ }
135
+ return result;
92
136
  }
93
137
  else {
94
138
  return {};
package/core/router.d.ts CHANGED
@@ -79,6 +79,35 @@ export declare class Router<T extends Record<string, any> = {}> extends Middlewa
79
79
  get(path: string, callback: Callback<T>): this;
80
80
  get(path: string, middleware: Middleware<T>, callback: Callback<T>): this;
81
81
  get(path: string, middlewares: Middleware<T>[], callback: Callback<T>): this;
82
+ /**
83
+ * Registers a Server-Sent Events (SSE) route handler for the given path.
84
+ *
85
+ * This method sets up an HTTP GET route that sends real-time updates to the client
86
+ * over a persistent HTTP connection using the SSE protocol.
87
+ *
88
+ * ### Example:
89
+ * ```ts
90
+ * app.sse("/events", async (ctx) => {
91
+ * const stream = new ReadableStream({
92
+ * start(controller) {
93
+ * controller.enqueue(new TextEncoder().encode("data: Hello\n\n"));
94
+ * },
95
+ * });
96
+ *
97
+ * return ctx.send(stream, {
98
+ * headers: {
99
+ * "Content-Type": "text/event-stream",
100
+ * "Cache-Control": "no-cache",
101
+ * "Connection": "keep-alive",
102
+ * },
103
+ * });
104
+ * });
105
+ * ```
106
+ *
107
+ * @param {string} path - The route path for SSE (e.g. `/events`).
108
+ * @param {(ctx: Context) => any} handler - A handler function that returns a streamed response.
109
+ */
110
+ sse(path: string, handler: (ctx: Context) => any): void;
82
111
  /**
83
112
  * Registers a POST route with optional middleware(s)
84
113
  * @param path - URL path pattern
package/core/router.js CHANGED
@@ -59,6 +59,11 @@ export class Router extends MiddlewareConfigure {
59
59
  this.#registerRoute("GET", path, ...args);
60
60
  return this;
61
61
  }
62
+ sse(path, handler) {
63
+ this.get(path, async (ctx) => {
64
+ return handler(ctx);
65
+ });
66
+ }
62
67
  post(path, ...args) {
63
68
  this.#registerRoute("POST", path, ...args);
64
69
  return this;
package/core/server.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { COLORS } from "../utils/colors.js";
2
+ import { httpStatusMap } from "../utils/httpStatusMap.js";
2
3
  import { useParams } from "../utils/params.js";
3
4
  import { GlobalConfig } from "./config.js";
4
- import { Context, httpStatusMap } from "./context.js";
5
+ import { Context } from "./context.js";
5
6
  import { Router } from "./router.js";
6
7
  export class TezX extends Router {
7
8
  #onPathResolve;
@@ -194,12 +195,12 @@ export class TezX extends Router {
194
195
  }
195
196
  let finalResponse = () => {
196
197
  return (ctx) => {
197
- if (response?.headers) {
198
- ctx.headers.add(response.headers);
198
+ for (const [key, value] of response?.headers.entries()) {
199
+ ctx.headers.set(key, value);
199
200
  }
200
201
  const statusText = response?.statusText || httpStatusMap[response?.status] || "";
201
202
  const status = response.status || ctx.getStatus;
202
- let headers = ctx.headers.toObject();
203
+ let headers = ctx.headers.toJSON();
203
204
  return new Response(response.body, {
204
205
  status,
205
206
  statusText,
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Router } from "./core/router.js";
2
2
  export { TezX } from "./core/server.js";
3
3
  export { useParams } from "./utils/params.js";
4
- export let version = "1.0.74";
4
+ export let version = "1.0.76-beta";
@@ -38,7 +38,7 @@ export function cors(option = {}) {
38
38
  if (ctx.req.method === "OPTIONS") {
39
39
  return new Response(null, {
40
40
  status: 204,
41
- headers: ctx.headers.toObject(),
41
+ headers: ctx.headers.toJSON(),
42
42
  });
43
43
  }
44
44
  return await next();
@@ -28,9 +28,9 @@ export const sanitizeHeaders = (options = {}) => {
28
28
  GlobalConfig.debugging.warn(`⚠️ All values for "${normalizedKey}" invalid - removed`);
29
29
  continue;
30
30
  }
31
- sanitizedHeaders.set(normalizedKey, sanitizedValues);
31
+ sanitizedHeaders.set(normalizedKey, sanitizedValues?.join(", "));
32
32
  }
33
- ctx.headers.clear().add([...sanitizedHeaders.entries()]);
33
+ ctx.headers = new Headers(sanitizedHeaders);
34
34
  return await next();
35
35
  };
36
36
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tezx",
3
- "version": "1.0.74",
3
+ "version": "1.0.76-beta",
4
4
  "description": "TezX is a high-performance, lightweight JavaScript framework designed for speed, scalability, and flexibility. It enables efficient routing, middleware management, and static file serving with minimal configuration. Fully compatible with Node.js, Deno, and Bun.",
5
5
  "main": "cjs/index.js",
6
6
  "module": "index.js",
@@ -1,6 +1 @@
1
- import { FormDataOptions } from "../core/request.js";
2
- export declare function parseJsonBody(req: any): Promise<Record<string, any>>;
3
- export declare function parseTextBody(req: any): Promise<string>;
4
- export declare function parseUrlEncodedBody(req: any): Promise<Record<string, any>>;
5
1
  export declare function sanitized(title: string): string;
6
- export declare function parseMultipartBody(req: any, boundary: string, options?: FormDataOptions): Promise<Record<string, any>>;
package/utils/formData.js CHANGED
@@ -1,90 +1,3 @@
1
- import { GlobalConfig } from "../core/config.js";
2
- export async function parseJsonBody(req) {
3
- const runtime = GlobalConfig.adapter;
4
- if (runtime === "node") {
5
- return new Promise((resolve, reject) => {
6
- let body = "";
7
- req.on("data", (chunk) => {
8
- body += chunk.toString();
9
- });
10
- req.on("end", () => {
11
- try {
12
- resolve(JSON.parse(body));
13
- }
14
- catch (error) {
15
- reject(new Error("Invalid JSON format"));
16
- }
17
- });
18
- });
19
- }
20
- else if (runtime === "deno" || runtime === "bun") {
21
- return await req.json();
22
- }
23
- else {
24
- throw new Error("Unsupported environment for multipart parsing");
25
- }
26
- }
27
- export async function parseTextBody(req) {
28
- const runtime = GlobalConfig.adapter;
29
- if (runtime === "node") {
30
- return new Promise((resolve, reject) => {
31
- let body = "";
32
- req.on("data", (chunk) => {
33
- body += chunk.toString();
34
- });
35
- req.on("end", () => {
36
- try {
37
- resolve(body);
38
- }
39
- catch (error) {
40
- reject(new Error("Invalid JSON format"));
41
- }
42
- });
43
- });
44
- }
45
- else if (runtime === "deno" || runtime === "bun") {
46
- return await req.text();
47
- }
48
- else {
49
- throw new Error("Unsupported environment for multipart parsing");
50
- }
51
- }
52
- export async function parseUrlEncodedBody(req) {
53
- const runtime = GlobalConfig.adapter;
54
- if (runtime === "node") {
55
- return new Promise((resolve, reject) => {
56
- let body = "";
57
- req.on("data", (chunk) => {
58
- body += chunk.toString("binary");
59
- });
60
- req.on("end", () => {
61
- try {
62
- const pairs = body.split("&");
63
- const formData = {};
64
- pairs.forEach((pair) => {
65
- const [key, value] = pair.split("=");
66
- formData[decodeURIComponent(key)] = decodeURIComponent(value || "");
67
- });
68
- resolve(formData);
69
- }
70
- catch {
71
- reject(new Error("Invalid x-www-form-urlencoded format"));
72
- }
73
- });
74
- });
75
- }
76
- else if (runtime === "deno" || runtime === "bun") {
77
- const formData = await req.formData();
78
- const result = {};
79
- for (const [key, value] of formData.entries()) {
80
- result[key] = value;
81
- }
82
- return result;
83
- }
84
- else {
85
- throw new Error("Unsupported environment for multipart parsing");
86
- }
87
- }
88
1
  export function sanitized(title) {
89
2
  const base = title
90
3
  .toLowerCase()
@@ -95,147 +8,3 @@ export function sanitized(title) {
95
8
  .replace(/^-+|-+$/g, "");
96
9
  return base;
97
10
  }
98
- export async function parseMultipartBody(req, boundary, options) {
99
- const runtime = GlobalConfig.adapter;
100
- if (runtime === "node") {
101
- return new Promise((resolve, reject) => {
102
- let body = "";
103
- req.on("data", (chunk) => {
104
- body += chunk.toString("binary");
105
- });
106
- req.on("end", () => {
107
- try {
108
- const formDataField = {};
109
- const formDataFieldParts = body.split("----------------------------");
110
- formDataFieldParts.forEach((part) => {
111
- const match = part.match(/name="(.*)"\r\n\r\n(.*)\r\n/);
112
- if (match && match.length === 3) {
113
- const name = match[1];
114
- const value = match[2];
115
- if (formDataField[name]) {
116
- if (Array.isArray(formDataField[name])) {
117
- formDataField[name].push(value);
118
- }
119
- else {
120
- formDataField[name] = [formDataField[name], value];
121
- }
122
- }
123
- else {
124
- formDataField[name] = value;
125
- }
126
- }
127
- });
128
- const parts = body.split(`--${boundary}`);
129
- for (const part of parts) {
130
- if (part.includes("filename")) {
131
- const filenameMatch = part.match(/filename="([^"]+)"/);
132
- const fieldNameMatch = part.match(/name="([^"]+)"/);
133
- const contentTypeMatch = part.match(/Content-Type: ([^\r\n]+)/);
134
- if (filenameMatch && fieldNameMatch && contentTypeMatch) {
135
- let filename = filenameMatch[1];
136
- const fieldName = fieldNameMatch[1];
137
- const contentType = contentTypeMatch[1];
138
- if (options?.sanitized) {
139
- filename = `${Date.now()}-${sanitized(filename)}`;
140
- }
141
- if (Array.isArray(options?.allowedTypes) &&
142
- !options.allowedTypes?.includes(contentType)) {
143
- reject(new Error(`Invalid file type: "${contentType}". Allowed types: ${options.allowedTypes.join(", ")}`));
144
- }
145
- const fileContentStartIndex = part.indexOf("\r\n\r\n") + 4;
146
- const fileContent = Buffer.from(part.substring(fileContentStartIndex), "binary");
147
- const arrayBuffer = fileContent.buffer.slice(fileContent.byteOffset, fileContent.byteOffset + fileContent.byteLength);
148
- if (typeof options?.maxSize !== "undefined" &&
149
- fileContent.byteLength > options.maxSize) {
150
- reject(new Error(`File size exceeds the limit: ${fileContent.byteLength} bytes (Max: ${options.maxSize} bytes)`));
151
- }
152
- const file = new File([arrayBuffer], filename, {
153
- type: contentType,
154
- });
155
- if (typeof options?.maxFiles != "undefined" &&
156
- options.maxFiles == 0) {
157
- reject(new Error(`Field "${fieldName}" exceeds the maximum allowed file count of ${options.maxFiles}.`));
158
- }
159
- if (formDataField[fieldName]) {
160
- if (Array.isArray(formDataField[fieldName])) {
161
- const existingFiles = formDataField[fieldName].filter((f) => f instanceof File);
162
- if (typeof options?.maxFiles != "undefined" &&
163
- existingFiles.length >= options.maxFiles) {
164
- reject(new Error(`Field "${fieldName}" exceeds the maximum allowed file count of ${options.maxFiles}.`));
165
- }
166
- formDataField[fieldName].push(file);
167
- }
168
- else {
169
- if (formDataField[fieldName] instanceof File &&
170
- typeof options?.maxFiles != "undefined" &&
171
- options.maxFiles == 1) {
172
- reject(new Error(`Field "${fieldName}" exceeds the maximum allowed file count of ${options.maxFiles}.`));
173
- }
174
- formDataField[fieldName] = [formDataField[fieldName], file];
175
- }
176
- }
177
- else {
178
- formDataField[fieldName] = file;
179
- }
180
- }
181
- }
182
- }
183
- resolve(formDataField);
184
- }
185
- catch { }
186
- });
187
- });
188
- }
189
- else if (runtime === "deno" || runtime === "bun") {
190
- const formData = await req.formData();
191
- const result = {};
192
- for (const [key, value] of formData.entries()) {
193
- let val = value;
194
- if (val instanceof File && typeof options == "object") {
195
- let filename = val.name;
196
- if (options?.sanitized) {
197
- filename = `${Date.now()}-${sanitized(filename)}`;
198
- }
199
- if (Array.isArray(options?.allowedTypes) &&
200
- !options.allowedTypes?.includes(val.type)) {
201
- throw new Error(`Invalid file type: "${val.type}". Allowed types: ${options.allowedTypes.join(", ")}`);
202
- }
203
- if (typeof options?.maxSize !== "undefined" &&
204
- val.size > options.maxSize) {
205
- throw new Error(`File size exceeds the limit: ${val.size} bytes (Max: ${options.maxSize} bytes)`);
206
- }
207
- if (typeof options?.maxFiles != "undefined" && options.maxFiles == 0) {
208
- throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
209
- }
210
- val = new File([await val.arrayBuffer()], filename, {
211
- type: val.type,
212
- });
213
- }
214
- if (result[key]) {
215
- if (Array.isArray(result[key])) {
216
- if (val instanceof File &&
217
- typeof options?.maxFiles != "undefined" &&
218
- result[key]?.length >= options.maxFiles) {
219
- throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
220
- }
221
- result[key].push(val);
222
- }
223
- else {
224
- if (val instanceof File &&
225
- typeof options?.maxFiles != "undefined" &&
226
- options.maxFiles == 1) {
227
- throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
228
- }
229
- result[key] = [result[key], val];
230
- }
231
- }
232
- else {
233
- result[key] = val;
234
- }
235
- }
236
- return result;
237
- }
238
- else {
239
- throw new Error("Unsupported environment for multipart parsing");
240
- }
241
- }
@@ -0,0 +1 @@
1
+ export declare const httpStatusMap: Record<number, string>;