tezx 1.0.74 → 1.0.75-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
@@ -1,97 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseJsonBody = parseJsonBody;
4
- exports.parseTextBody = parseTextBody;
5
- exports.parseUrlEncodedBody = parseUrlEncodedBody;
6
3
  exports.sanitized = sanitized;
7
- exports.parseMultipartBody = parseMultipartBody;
8
- const config_js_1 = require("../core/config.js");
9
- async function parseJsonBody(req) {
10
- const runtime = config_js_1.GlobalConfig.adapter;
11
- if (runtime === "node") {
12
- return new Promise((resolve, reject) => {
13
- let body = "";
14
- req.on("data", (chunk) => {
15
- body += chunk.toString();
16
- });
17
- req.on("end", () => {
18
- try {
19
- resolve(JSON.parse(body));
20
- }
21
- catch (error) {
22
- reject(new Error("Invalid JSON format"));
23
- }
24
- });
25
- });
26
- }
27
- else if (runtime === "deno" || runtime === "bun") {
28
- return await req.json();
29
- }
30
- else {
31
- throw new Error("Unsupported environment for multipart parsing");
32
- }
33
- }
34
- async function parseTextBody(req) {
35
- const runtime = config_js_1.GlobalConfig.adapter;
36
- if (runtime === "node") {
37
- return new Promise((resolve, reject) => {
38
- let body = "";
39
- req.on("data", (chunk) => {
40
- body += chunk.toString();
41
- });
42
- req.on("end", () => {
43
- try {
44
- resolve(body);
45
- }
46
- catch (error) {
47
- reject(new Error("Invalid JSON format"));
48
- }
49
- });
50
- });
51
- }
52
- else if (runtime === "deno" || runtime === "bun") {
53
- return await req.text();
54
- }
55
- else {
56
- throw new Error("Unsupported environment for multipart parsing");
57
- }
58
- }
59
- async function parseUrlEncodedBody(req) {
60
- const runtime = config_js_1.GlobalConfig.adapter;
61
- if (runtime === "node") {
62
- return new Promise((resolve, reject) => {
63
- let body = "";
64
- req.on("data", (chunk) => {
65
- body += chunk.toString("binary");
66
- });
67
- req.on("end", () => {
68
- try {
69
- const pairs = body.split("&");
70
- const formData = {};
71
- pairs.forEach((pair) => {
72
- const [key, value] = pair.split("=");
73
- formData[decodeURIComponent(key)] = decodeURIComponent(value || "");
74
- });
75
- resolve(formData);
76
- }
77
- catch {
78
- reject(new Error("Invalid x-www-form-urlencoded format"));
79
- }
80
- });
81
- });
82
- }
83
- else if (runtime === "deno" || runtime === "bun") {
84
- const formData = await req.formData();
85
- const result = {};
86
- for (const [key, value] of formData.entries()) {
87
- result[key] = value;
88
- }
89
- return result;
90
- }
91
- else {
92
- throw new Error("Unsupported environment for multipart parsing");
93
- }
94
- }
95
4
  function sanitized(title) {
96
5
  const base = title
97
6
  .toLowerCase()
@@ -102,147 +11,3 @@ function sanitized(title) {
102
11
  .replace(/^-+|-+$/g, "");
103
12
  return base;
104
13
  }
105
- async function parseMultipartBody(req, boundary, options) {
106
- const runtime = config_js_1.GlobalConfig.adapter;
107
- if (runtime === "node") {
108
- return new Promise((resolve, reject) => {
109
- let body = "";
110
- req.on("data", (chunk) => {
111
- body += chunk.toString("binary");
112
- });
113
- req.on("end", () => {
114
- try {
115
- const formDataField = {};
116
- const formDataFieldParts = body.split("----------------------------");
117
- formDataFieldParts.forEach((part) => {
118
- const match = part.match(/name="(.*)"\r\n\r\n(.*)\r\n/);
119
- if (match && match.length === 3) {
120
- const name = match[1];
121
- const value = match[2];
122
- if (formDataField[name]) {
123
- if (Array.isArray(formDataField[name])) {
124
- formDataField[name].push(value);
125
- }
126
- else {
127
- formDataField[name] = [formDataField[name], value];
128
- }
129
- }
130
- else {
131
- formDataField[name] = value;
132
- }
133
- }
134
- });
135
- const parts = body.split(`--${boundary}`);
136
- for (const part of parts) {
137
- if (part.includes("filename")) {
138
- const filenameMatch = part.match(/filename="([^"]+)"/);
139
- const fieldNameMatch = part.match(/name="([^"]+)"/);
140
- const contentTypeMatch = part.match(/Content-Type: ([^\r\n]+)/);
141
- if (filenameMatch && fieldNameMatch && contentTypeMatch) {
142
- let filename = filenameMatch[1];
143
- const fieldName = fieldNameMatch[1];
144
- const contentType = contentTypeMatch[1];
145
- if (options?.sanitized) {
146
- filename = `${Date.now()}-${sanitized(filename)}`;
147
- }
148
- if (Array.isArray(options?.allowedTypes) &&
149
- !options.allowedTypes?.includes(contentType)) {
150
- reject(new Error(`Invalid file type: "${contentType}". Allowed types: ${options.allowedTypes.join(", ")}`));
151
- }
152
- const fileContentStartIndex = part.indexOf("\r\n\r\n") + 4;
153
- const fileContent = Buffer.from(part.substring(fileContentStartIndex), "binary");
154
- const arrayBuffer = fileContent.buffer.slice(fileContent.byteOffset, fileContent.byteOffset + fileContent.byteLength);
155
- if (typeof options?.maxSize !== "undefined" &&
156
- fileContent.byteLength > options.maxSize) {
157
- reject(new Error(`File size exceeds the limit: ${fileContent.byteLength} bytes (Max: ${options.maxSize} bytes)`));
158
- }
159
- const file = new File([arrayBuffer], filename, {
160
- type: contentType,
161
- });
162
- if (typeof options?.maxFiles != "undefined" &&
163
- options.maxFiles == 0) {
164
- reject(new Error(`Field "${fieldName}" exceeds the maximum allowed file count of ${options.maxFiles}.`));
165
- }
166
- if (formDataField[fieldName]) {
167
- if (Array.isArray(formDataField[fieldName])) {
168
- const existingFiles = formDataField[fieldName].filter((f) => f instanceof File);
169
- if (typeof options?.maxFiles != "undefined" &&
170
- existingFiles.length >= options.maxFiles) {
171
- reject(new Error(`Field "${fieldName}" exceeds the maximum allowed file count of ${options.maxFiles}.`));
172
- }
173
- formDataField[fieldName].push(file);
174
- }
175
- else {
176
- if (formDataField[fieldName] instanceof File &&
177
- typeof options?.maxFiles != "undefined" &&
178
- options.maxFiles == 1) {
179
- reject(new Error(`Field "${fieldName}" exceeds the maximum allowed file count of ${options.maxFiles}.`));
180
- }
181
- formDataField[fieldName] = [formDataField[fieldName], file];
182
- }
183
- }
184
- else {
185
- formDataField[fieldName] = file;
186
- }
187
- }
188
- }
189
- }
190
- resolve(formDataField);
191
- }
192
- catch { }
193
- });
194
- });
195
- }
196
- else if (runtime === "deno" || runtime === "bun") {
197
- const formData = await req.formData();
198
- const result = {};
199
- for (const [key, value] of formData.entries()) {
200
- let val = value;
201
- if (val instanceof File && typeof options == "object") {
202
- let filename = val.name;
203
- if (options?.sanitized) {
204
- filename = `${Date.now()}-${sanitized(filename)}`;
205
- }
206
- if (Array.isArray(options?.allowedTypes) &&
207
- !options.allowedTypes?.includes(val.type)) {
208
- throw new Error(`Invalid file type: "${val.type}". Allowed types: ${options.allowedTypes.join(", ")}`);
209
- }
210
- if (typeof options?.maxSize !== "undefined" &&
211
- val.size > options.maxSize) {
212
- throw new Error(`File size exceeds the limit: ${val.size} bytes (Max: ${options.maxSize} bytes)`);
213
- }
214
- if (typeof options?.maxFiles != "undefined" && options.maxFiles == 0) {
215
- throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
216
- }
217
- val = new File([await val.arrayBuffer()], filename, {
218
- type: val.type,
219
- });
220
- }
221
- if (result[key]) {
222
- if (Array.isArray(result[key])) {
223
- if (val instanceof File &&
224
- typeof options?.maxFiles != "undefined" &&
225
- result[key]?.length >= options.maxFiles) {
226
- throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
227
- }
228
- result[key].push(val);
229
- }
230
- else {
231
- if (val instanceof File &&
232
- typeof options?.maxFiles != "undefined" &&
233
- options.maxFiles == 1) {
234
- throw new Error(`Field "${key}" exceeds the maximum allowed file count of ${options.maxFiles}.`);
235
- }
236
- result[key] = [result[key], val];
237
- }
238
- }
239
- else {
240
- result[key] = val;
241
- }
242
- }
243
- return result;
244
- }
245
- else {
246
- throw new Error("Unsupported environment for multipart parsing");
247
- }
248
- }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.httpStatusMap = void 0;
4
+ exports.httpStatusMap = {
5
+ 100: "Continue",
6
+ 101: "Switching Protocols",
7
+ 102: "Processing",
8
+ 103: "Early Hints",
9
+ 200: "OK",
10
+ 201: "Created",
11
+ 202: "Accepted",
12
+ 203: "Non-Authoritative Information",
13
+ 204: "No Content",
14
+ 205: "Reset Content",
15
+ 206: "Partial Content",
16
+ 207: "Multi-Status",
17
+ 208: "Already Reported",
18
+ 226: "IM Used",
19
+ 300: "Multiple Choices",
20
+ 301: "Moved Permanently",
21
+ 302: "Found",
22
+ 303: "See Other",
23
+ 304: "Not Modified",
24
+ 305: "Use Proxy",
25
+ 306: "Switch Proxy",
26
+ 307: "Temporary Redirect",
27
+ 308: "Permanent Redirect",
28
+ 400: "Bad Request",
29
+ 401: "Unauthorized",
30
+ 402: "Payment Required",
31
+ 403: "Forbidden",
32
+ 404: "Not Found",
33
+ 405: "Method Not Allowed",
34
+ 406: "Not Acceptable",
35
+ 407: "Proxy Authentication Required",
36
+ 408: "Request Timeout",
37
+ 409: "Conflict",
38
+ 410: "Gone",
39
+ 411: "Length Required",
40
+ 412: "Precondition Failed",
41
+ 413: "Payload Too Large",
42
+ 414: "URI Too Long",
43
+ 415: "Unsupported Media Type",
44
+ 416: "Range Not Satisfiable",
45
+ 417: "Expectation Failed",
46
+ 418: "I'm a Teapot",
47
+ 421: "Misdirected Request",
48
+ 422: "Unprocessable Entity",
49
+ 423: "Locked",
50
+ 424: "Failed Dependency",
51
+ 425: "Too Early",
52
+ 426: "Upgrade Required",
53
+ 428: "Precondition Required",
54
+ 429: "Too Many Requests",
55
+ 431: "Request Header Fields Too Large",
56
+ 451: "Unavailable For Legal Reasons",
57
+ 500: "Internal Server Error",
58
+ 501: "Not Implemented",
59
+ 502: "Bad Gateway",
60
+ 503: "Service Unavailable",
61
+ 504: "Gateway Timeout",
62
+ 505: "HTTP Version Not Supported",
63
+ 506: "Variant Also Negotiates",
64
+ 507: "Insufficient Storage",
65
+ 508: "Loop Detected",
66
+ 510: "Not Extended",
67
+ 511: "Network Authentication Required",
68
+ };
@@ -149,7 +149,10 @@ async function getFiles(dir, basePath = "/", ref, option) {
149
149
  ctx.headers.set("Cache-Control", option.cacheControl);
150
150
  }
151
151
  if (option.headers) {
152
- ctx.headers.add(option.headers);
152
+ for (const key in option.headers) {
153
+ let value = option.headers?.[key];
154
+ ctx.headers.set(key, value);
155
+ }
153
156
  }
154
157
  return ctx.sendFile(r.file);
155
158
  });
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toWebRequest = toWebRequest;
4
+ const node_stream_1 = require("node:stream");
5
+ function toWebRequest(req, method = "GET") {
6
+ const headers = {};
7
+ for (const [key, value] of Object.entries(req.headers)) {
8
+ if (Array.isArray(value)) {
9
+ headers[key] = value.join(", ");
10
+ }
11
+ else if (typeof value === "string") {
12
+ headers[key] = value;
13
+ }
14
+ }
15
+ const isEncrypted = (req.socket && req.socket.encrypted) || false;
16
+ const protocol = isEncrypted ? "https:" : "http:";
17
+ let host = "localhost";
18
+ const hostHeader = req.headers.host;
19
+ if (typeof hostHeader === "string") {
20
+ host = hostHeader;
21
+ }
22
+ const urlStr = req.url ?? "/";
23
+ const fullUrl = new URL(urlStr, `${protocol}//${host}`);
24
+ const hasBody = !["GET", "HEAD"].includes(method.toUpperCase());
25
+ const body = hasBody ? node_stream_1.Readable.toWeb(req) : undefined;
26
+ const abortController = new AbortController();
27
+ req?.once("close", () => abortController.abort());
28
+ return new Request(fullUrl.href, {
29
+ method,
30
+ headers,
31
+ body,
32
+ signal: abortController.signal,
33
+ duplex: hasBody ? "half" : undefined,
34
+ });
35
+ }
package/core/context.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { State } from "../utils/state.js";
2
- import { HeadersParser } from "./header.js";
3
- import { HTTPMethod, Request } from "./request.js";
2
+ import { HTTPMethod, Request as RequestParser } from "./request.js";
4
3
  import { TezXServeOptions } from "./server.js";
5
4
  export interface CookieOptions {
6
5
  expires?: Date;
@@ -11,11 +10,11 @@ export interface CookieOptions {
11
10
  httpOnly?: boolean;
12
11
  sameSite?: "Strict" | "Lax" | "None";
13
12
  }
14
- export declare const httpStatusMap: Record<number, string>;
15
13
  export type ResponseHeaders = Record<string, string>;
16
14
  export declare class Context<T extends Record<string, any> = {}> {
17
15
  #private;
18
16
  [key: string]: any;
17
+ rawRequest: Request;
19
18
  /**
20
19
  * Environment variables and configuration
21
20
  * @type {object}
@@ -23,9 +22,9 @@ export declare class Context<T extends Record<string, any> = {}> {
23
22
  env: Record<string, any> & T;
24
23
  /**
25
24
  * Parser for handling and manipulating HTTP headers
26
- * @type {HeadersParser}
25
+ * @type {Headers}
27
26
  */
28
- headers: HeadersParser;
27
+ headers: Headers;
29
28
  /**
30
29
  * Request path without query parameters
31
30
  * @type {string}
@@ -193,7 +192,7 @@ export declare class Context<T extends Record<string, any> = {}> {
193
192
  * // Access route params
194
193
  * const id = request.params.get('id');
195
194
  */
196
- get req(): Request;
195
+ get req(): RequestParser;
197
196
  protected set params(params: Record<string, any>);
198
197
  /**
199
198
  * Set response body to be passed between middlewares or returned as final output.
package/core/context.js CHANGED
@@ -1,79 +1,13 @@
1
1
  import { State } from "../utils/state.js";
2
2
  import { defaultMimeType, mimeTypes } from "../utils/staticFile.js";
3
- import { urlParse } from "../utils/url.js";
3
+ import { toWebRequest } from "../utils/toWebRequest.js";
4
4
  import { GlobalConfig } from "./config.js";
5
5
  import { EnvironmentDetector } from "./environment.js";
6
- import { HeadersParser } from "./header.js";
7
- import { Request } from "./request.js";
8
- export const httpStatusMap = {
9
- 100: "Continue",
10
- 101: "Switching Protocols",
11
- 102: "Processing",
12
- 103: "Early Hints",
13
- 200: "OK",
14
- 201: "Created",
15
- 202: "Accepted",
16
- 203: "Non-Authoritative Information",
17
- 204: "No Content",
18
- 205: "Reset Content",
19
- 206: "Partial Content",
20
- 207: "Multi-Status",
21
- 208: "Already Reported",
22
- 226: "IM Used",
23
- 300: "Multiple Choices",
24
- 301: "Moved Permanently",
25
- 302: "Found",
26
- 303: "See Other",
27
- 304: "Not Modified",
28
- 305: "Use Proxy",
29
- 306: "Switch Proxy",
30
- 307: "Temporary Redirect",
31
- 308: "Permanent Redirect",
32
- 400: "Bad Request",
33
- 401: "Unauthorized",
34
- 402: "Payment Required",
35
- 403: "Forbidden",
36
- 404: "Not Found",
37
- 405: "Method Not Allowed",
38
- 406: "Not Acceptable",
39
- 407: "Proxy Authentication Required",
40
- 408: "Request Timeout",
41
- 409: "Conflict",
42
- 410: "Gone",
43
- 411: "Length Required",
44
- 412: "Precondition Failed",
45
- 413: "Payload Too Large",
46
- 414: "URI Too Long",
47
- 415: "Unsupported Media Type",
48
- 416: "Range Not Satisfiable",
49
- 417: "Expectation Failed",
50
- 418: "I'm a Teapot",
51
- 421: "Misdirected Request",
52
- 422: "Unprocessable Entity",
53
- 423: "Locked",
54
- 424: "Failed Dependency",
55
- 425: "Too Early",
56
- 426: "Upgrade Required",
57
- 428: "Precondition Required",
58
- 429: "Too Many Requests",
59
- 431: "Request Header Fields Too Large",
60
- 451: "Unavailable For Legal Reasons",
61
- 500: "Internal Server Error",
62
- 501: "Not Implemented",
63
- 502: "Bad Gateway",
64
- 503: "Service Unavailable",
65
- 504: "Gateway Timeout",
66
- 505: "HTTP Version Not Supported",
67
- 506: "Variant Also Negotiates",
68
- 507: "Insufficient Storage",
69
- 508: "Loop Detected",
70
- 510: "Not Extended",
71
- 511: "Network Authentication Required",
72
- };
6
+ import { Request as RequestParser } from "./request.js";
73
7
  export class Context {
74
- #rawRequest;
8
+ rawRequest;
75
9
  env = {};
76
- headers = new HeadersParser();
10
+ headers = new Headers();
77
11
  pathname;
78
12
  url;
79
13
  method;
@@ -82,30 +16,20 @@ export class Context {
82
16
  #params = {};
83
17
  resBody;
84
18
  #body;
85
- #urlRef;
86
- #requestHeaders;
87
19
  #options;
88
20
  constructor(req, options) {
89
21
  this.#options = options;
90
- this.#rawRequest = req;
91
22
  this.method = req?.method?.toUpperCase();
92
- this.#requestHeaders = new HeadersParser(req?.headers);
93
23
  if (GlobalConfig.adapter == "node") {
94
- let encrypted = req?.socket?.encrypted;
95
- const protocol = typeof encrypted === "boolean"
96
- ? encrypted
97
- ? "https"
98
- : "http"
99
- : "http";
100
- const host = EnvironmentDetector.getHost(this.#requestHeaders);
101
- const path = req.url || "/";
102
- this.url = `${protocol}://${host}${path}`;
24
+ let request = toWebRequest(req, this.method);
25
+ this.url = request.url;
26
+ this.rawRequest = request;
103
27
  }
104
28
  else {
105
29
  this.url = req.url;
30
+ this.rawRequest = req;
106
31
  }
107
- this.#urlRef = urlParse(this.url);
108
- this.pathname = this.#urlRef.pathname;
32
+ this.pathname = this.req.urlRef.pathname;
109
33
  }
110
34
  header(key, value, options) {
111
35
  let append = options?.append;
@@ -118,16 +42,9 @@ export class Context {
118
42
  return this;
119
43
  }
120
44
  get cookies() {
121
- const c = this.#requestHeaders.getAll("cookie");
45
+ const c = this.req.headers.get("cookie");
122
46
  let cookies = {};
123
- if (Array.isArray(c) && c.length != 0) {
124
- const cookieHeader = c.join("; ").split(";");
125
- for (const pair of cookieHeader) {
126
- const [key, value] = pair?.trim()?.split("=");
127
- cookies[key] = decodeURIComponent(value);
128
- }
129
- }
130
- else if (typeof c == "string") {
47
+ if (typeof c == "string") {
131
48
  const cookieHeader = c.split(";");
132
49
  for (const pair of cookieHeader) {
133
50
  const [key, value] = pair?.trim()?.split("=");
@@ -418,10 +335,9 @@ export class Context {
418
335
  return response;
419
336
  }
420
337
  get req() {
421
- return new Request({
422
- headers: this.#requestHeaders,
423
- req: this.#rawRequest,
424
- urlRef: this.#urlRef,
338
+ return new RequestParser({
339
+ method: this.method,
340
+ req: this.rawRequest,
425
341
  options: this.#options,
426
342
  params: this.#params,
427
343
  });
@@ -1,5 +1,3 @@
1
- import { HeadersParser } from "./header.js";
2
1
  export declare class EnvironmentDetector {
3
2
  static get getEnvironment(): "node" | "bun" | "deno" | "unknown";
4
- static getHost(headers: HeadersParser): string;
5
3
  }
@@ -8,12 +8,4 @@ export class EnvironmentDetector {
8
8
  return "node";
9
9
  return "unknown";
10
10
  }
11
- static getHost(headers) {
12
- try {
13
- return headers?.get("host") || "unknown";
14
- }
15
- catch (error) {
16
- throw new Error("Failed to get host.");
17
- }
18
- }
19
11
  }