tezx 1.0.21 → 1.0.22

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.
package/context.js ADDED
@@ -0,0 +1,424 @@
1
+ import { EnvironmentDetector } from "./environment";
2
+ import { HeadersParser } from "./header";
3
+ import { Request } from "./request";
4
+ import { State } from "./utils/state";
5
+ import { defaultMimeType, mimeTypes } from "./utils/staticFile";
6
+ export const httpStatusMap = {
7
+ 100: "Continue",
8
+ 101: "Switching Protocols",
9
+ 102: "Processing",
10
+ 103: "Early Hints",
11
+ 200: "OK",
12
+ 201: "Created",
13
+ 202: "Accepted",
14
+ 203: "Non-Authoritative Information",
15
+ 204: "No Content",
16
+ 205: "Reset Content",
17
+ 206: "Partial Content",
18
+ 207: "Multi-Status",
19
+ 208: "Already Reported",
20
+ 226: "IM Used",
21
+ 300: "Multiple Choices",
22
+ 301: "Moved Permanently",
23
+ 302: "Found",
24
+ 303: "See Other",
25
+ 304: "Not Modified",
26
+ 305: "Use Proxy",
27
+ 306: "Switch Proxy",
28
+ 307: "Temporary Redirect",
29
+ 308: "Permanent Redirect",
30
+ 400: "Bad Request",
31
+ 401: "Unauthorized",
32
+ 402: "Payment Required",
33
+ 403: "Forbidden",
34
+ 404: "Not Found",
35
+ 405: "Method Not Allowed",
36
+ 406: "Not Acceptable",
37
+ 407: "Proxy Authentication Required",
38
+ 408: "Request Timeout",
39
+ 409: "Conflict",
40
+ 410: "Gone",
41
+ 411: "Length Required",
42
+ 412: "Precondition Failed",
43
+ 413: "Payload Too Large",
44
+ 414: "URI Too Long",
45
+ 415: "Unsupported Media Type",
46
+ 416: "Range Not Satisfiable",
47
+ 417: "Expectation Failed",
48
+ 418: "I'm a Teapot",
49
+ 421: "Misdirected Request",
50
+ 422: "Unprocessable Entity",
51
+ 423: "Locked",
52
+ 424: "Failed Dependency",
53
+ 425: "Too Early",
54
+ 426: "Upgrade Required",
55
+ 428: "Precondition Required",
56
+ 429: "Too Many Requests",
57
+ 431: "Request Header Fields Too Large",
58
+ 451: "Unavailable For Legal Reasons",
59
+ 500: "Internal Server Error",
60
+ 501: "Not Implemented",
61
+ 502: "Bad Gateway",
62
+ 503: "Service Unavailable",
63
+ 504: "Gateway Timeout",
64
+ 505: "HTTP Version Not Supported",
65
+ 506: "Variant Also Negotiates",
66
+ 507: "Insufficient Storage",
67
+ 508: "Loop Detected",
68
+ 510: "Not Extended",
69
+ 511: "Network Authentication Required",
70
+ };
71
+ export class Context {
72
+ #rawRequest;
73
+ env = {};
74
+ headers = new HeadersParser();
75
+ res;
76
+ pathname;
77
+ url;
78
+ method;
79
+ #status = 200;
80
+ state = new State();
81
+ #params = {};
82
+ #localAddress = {};
83
+ #remoteAddress = {};
84
+ constructor(req, connInfo) {
85
+ this.#rawRequest = req;
86
+ this.#remoteAddress = connInfo.remoteAddr;
87
+ this.#localAddress = connInfo.localAddr;
88
+ this.method = req?.method?.toUpperCase();
89
+ this.pathname = this.req.urlRef.pathname;
90
+ this.url = this.req.url;
91
+ }
92
+ header(key, value) {
93
+ this.headers.set(key, value);
94
+ return this;
95
+ }
96
+ get cookies() {
97
+ const c = this.headers.getAll("cookie");
98
+ let cookies = {};
99
+ if (Array.isArray(c) && c.length != 0) {
100
+ const cookieHeader = c.join("; ").split(";");
101
+ for (const pair of cookieHeader) {
102
+ const [key, value] = pair?.trim()?.split("=");
103
+ cookies[key] = decodeURIComponent(value);
104
+ }
105
+ }
106
+ else if (typeof c == "string") {
107
+ const cookieHeader = c.split(";");
108
+ for (const pair of cookieHeader) {
109
+ const [key, value] = pair?.trim()?.split("=");
110
+ cookies[key] = decodeURIComponent(value);
111
+ }
112
+ }
113
+ return {
114
+ get: (cookie) => {
115
+ return cookies?.[cookie];
116
+ },
117
+ all: () => {
118
+ return cookies;
119
+ },
120
+ delete: (name, options) => {
121
+ const value = "";
122
+ const cookieOptions = {
123
+ ...options,
124
+ expires: new Date(0),
125
+ };
126
+ const cookieHeader = `${name}=${value};${serializeOptions(cookieOptions)}`;
127
+ this.headers.set("Set-Cookie", cookieHeader);
128
+ },
129
+ set: (name, value, options) => {
130
+ const cookieHeader = `${name}=${value};${serializeOptions(options || {})}`;
131
+ this.headers.set("Set-Cookie", cookieHeader);
132
+ },
133
+ };
134
+ }
135
+ json(body, ...args) {
136
+ let status = this.#status;
137
+ let headers = {
138
+ "Content-Type": "application/json; charset=utf-8",
139
+ };
140
+ if (typeof args[0] === "number") {
141
+ status = args[0];
142
+ if (typeof args[1] === "object") {
143
+ headers = { ...headers, ...args[1] };
144
+ }
145
+ }
146
+ else if (typeof args[0] === "object") {
147
+ headers = { ...headers, ...args[0] };
148
+ }
149
+ return this.#handleResponse(JSON.stringify(body), {
150
+ status: status,
151
+ headers: headers,
152
+ });
153
+ }
154
+ send(body, ...args) {
155
+ let status = this.#status;
156
+ let headers = {};
157
+ if (typeof args[0] === "number") {
158
+ status = args[0];
159
+ if (typeof args[1] === "object") {
160
+ headers = args[1];
161
+ }
162
+ }
163
+ else if (typeof args[0] === "object") {
164
+ headers = args[0];
165
+ }
166
+ if (!headers["Content-Type"]) {
167
+ if (typeof body === "string") {
168
+ headers["Content-Type"] = "text/plain;";
169
+ }
170
+ else if (typeof body === "object" && body !== null) {
171
+ headers["Content-Type"] = "application/json;";
172
+ body = JSON.stringify(body);
173
+ }
174
+ else {
175
+ headers["Content-Type"] = "application/octet-stream";
176
+ }
177
+ }
178
+ return this.#handleResponse(body, {
179
+ status: status,
180
+ headers,
181
+ });
182
+ }
183
+ html(data, ...args) {
184
+ let status = this.#status;
185
+ let headers = {
186
+ "Content-Type": "text/html; charset=utf-8",
187
+ };
188
+ if (typeof args[0] === "number") {
189
+ status = args[0];
190
+ if (typeof args[1] === "object") {
191
+ headers = { ...headers, ...args[1] };
192
+ }
193
+ }
194
+ else if (typeof args[0] === "object") {
195
+ headers = { ...headers, ...args[0] };
196
+ }
197
+ return this.#handleResponse(data, {
198
+ status: status,
199
+ headers: headers,
200
+ });
201
+ }
202
+ text(data, ...args) {
203
+ let status = this.#status;
204
+ let headers = {
205
+ "Content-Type": "text/plain; charset=utf-8",
206
+ };
207
+ if (typeof args[0] === "number") {
208
+ status = args[0];
209
+ if (typeof args[1] === "object") {
210
+ headers = { ...headers, ...args[1] };
211
+ }
212
+ }
213
+ else if (typeof args[0] === "object") {
214
+ headers = { ...headers, ...args[0] };
215
+ }
216
+ return this.#handleResponse(data, {
217
+ status: status,
218
+ headers: headers,
219
+ });
220
+ }
221
+ xml(data, ...args) {
222
+ let status = this.#status;
223
+ let headers = {
224
+ "Content-Type": "application/xml; charset=utf-8",
225
+ };
226
+ if (typeof args[0] === "number") {
227
+ status = args[0];
228
+ if (typeof args[1] === "object") {
229
+ headers = { ...headers, ...args[1] };
230
+ }
231
+ }
232
+ else if (typeof args[0] === "object") {
233
+ headers = { ...headers, ...args[0] };
234
+ }
235
+ return this.#handleResponse(data, {
236
+ status: status,
237
+ headers: headers,
238
+ });
239
+ }
240
+ status = (status) => {
241
+ this.#status = status;
242
+ return this;
243
+ };
244
+ set setStatus(status) {
245
+ this.#status = status;
246
+ }
247
+ get getStatus() {
248
+ return this.#status;
249
+ }
250
+ redirect(url, status = 302) {
251
+ return new Response(null, {
252
+ status: status,
253
+ headers: { Location: url },
254
+ });
255
+ }
256
+ async download(filePath, fileName) {
257
+ try {
258
+ let fileExists = false;
259
+ const runtime = EnvironmentDetector.getEnvironment;
260
+ if (runtime === "node") {
261
+ const { existsSync } = await import("fs");
262
+ fileExists = existsSync(filePath);
263
+ }
264
+ else if (runtime === "bun") {
265
+ fileExists = Bun.file(filePath).exists();
266
+ }
267
+ else if (runtime === "deno") {
268
+ try {
269
+ await Deno.stat(filePath);
270
+ fileExists = true;
271
+ }
272
+ catch {
273
+ fileExists = false;
274
+ }
275
+ }
276
+ if (!fileExists) {
277
+ throw Error("File not found");
278
+ }
279
+ let fileBuffer;
280
+ if (runtime === "node") {
281
+ const { readFileSync } = await import("fs");
282
+ fileBuffer = await readFileSync(filePath);
283
+ }
284
+ else if (runtime === "bun") {
285
+ fileBuffer = await Bun.file(filePath)
286
+ .arrayBuffer()
287
+ .then((buf) => new Uint8Array(buf));
288
+ }
289
+ else if (runtime === "deno") {
290
+ fileBuffer = await Deno.readFile(filePath);
291
+ }
292
+ return this.#handleResponse(fileBuffer, {
293
+ status: 200,
294
+ headers: {
295
+ "Content-Disposition": `attachment; filename="${fileName}"`,
296
+ "Content-Type": "application/octet-stream",
297
+ "Content-Length": fileBuffer.byteLength.toString(),
298
+ },
299
+ });
300
+ }
301
+ catch (error) {
302
+ throw Error("Internal Server Error" + error?.message);
303
+ }
304
+ }
305
+ async sendFile(filePath, ...args) {
306
+ try {
307
+ const runtime = EnvironmentDetector.getEnvironment;
308
+ const resolvedPath = filePath;
309
+ let fileExists = false;
310
+ if (runtime === "node") {
311
+ const { existsSync } = await import("fs");
312
+ fileExists = existsSync(resolvedPath);
313
+ }
314
+ else if (runtime === "bun") {
315
+ fileExists = Bun.file(resolvedPath).exists();
316
+ }
317
+ else if (runtime === "deno") {
318
+ try {
319
+ await Deno.stat(resolvedPath);
320
+ fileExists = true;
321
+ }
322
+ catch {
323
+ fileExists = false;
324
+ }
325
+ }
326
+ if (!fileExists) {
327
+ throw Error("File not found");
328
+ }
329
+ let fileSize = 0;
330
+ if (runtime === "node") {
331
+ const { statSync } = await import("fs");
332
+ fileSize = statSync(resolvedPath).size;
333
+ }
334
+ else if (runtime === "bun") {
335
+ fileSize = (await Bun.file(resolvedPath).arrayBuffer()).byteLength;
336
+ }
337
+ else if (runtime === "deno") {
338
+ const fileInfo = await Deno.stat(resolvedPath);
339
+ fileSize = fileInfo.size;
340
+ }
341
+ const ext = filePath.split(".").pop()?.toLowerCase() || "";
342
+ const mimeType = mimeTypes[ext] || defaultMimeType;
343
+ let fileStream;
344
+ if (runtime === "node") {
345
+ const { createReadStream } = await import("fs");
346
+ fileStream = createReadStream(resolvedPath);
347
+ }
348
+ else if (runtime === "bun") {
349
+ fileStream = Bun.file(resolvedPath).stream();
350
+ }
351
+ else if (runtime === "deno") {
352
+ const file = await Deno.open(resolvedPath, { read: true });
353
+ fileStream = file.readable;
354
+ }
355
+ let headers = {
356
+ "Content-Type": mimeType,
357
+ "Content-Length": fileSize.toString(),
358
+ };
359
+ let fileName = "";
360
+ if (typeof args[0] === "string") {
361
+ fileName = args[0];
362
+ if (typeof args[1] === "object") {
363
+ headers = { ...headers, ...args[1] };
364
+ }
365
+ }
366
+ else if (typeof args[0] === "object") {
367
+ headers = { ...headers, ...args[0] };
368
+ }
369
+ if (fileName) {
370
+ headers["Content-Disposition"] = `attachment; filename="${fileName}"`;
371
+ }
372
+ return this.#handleResponse(fileStream, {
373
+ status: 200,
374
+ headers,
375
+ });
376
+ }
377
+ catch (error) {
378
+ throw Error("Internal Server Error" + error?.message);
379
+ }
380
+ }
381
+ #handleResponse(body, { headers, status }) {
382
+ let response = new Response(body, {
383
+ status: status,
384
+ headers,
385
+ });
386
+ let clone = response.clone();
387
+ this.res = response;
388
+ return clone;
389
+ }
390
+ get req() {
391
+ return new Request(this.#rawRequest, this.params, this.#remoteAddress);
392
+ }
393
+ set params(params) {
394
+ this.#params = params;
395
+ }
396
+ get params() {
397
+ return this.#params;
398
+ }
399
+ }
400
+ function serializeOptions(options) {
401
+ const parts = [];
402
+ if (options.maxAge) {
403
+ parts.push(`Max-Age=${options.maxAge}`);
404
+ }
405
+ if (options.expires) {
406
+ parts.push(`Expires=${options.expires.toUTCString()}`);
407
+ }
408
+ if (options.path) {
409
+ parts.push(`Path=${options.path}`);
410
+ }
411
+ if (options.domain) {
412
+ parts.push(`Domain=${options.domain}`);
413
+ }
414
+ if (options.secure) {
415
+ parts.push(`Secure`);
416
+ }
417
+ if (options.httpOnly) {
418
+ parts.push(`HttpOnly`);
419
+ }
420
+ if (options.sameSite) {
421
+ parts.push(`SameSite=${options.sameSite}`);
422
+ }
423
+ return parts.join("; ");
424
+ }
@@ -0,0 +1,6 @@
1
+ import { HeadersParser } from "./header";
2
+ export declare class EnvironmentDetector {
3
+ static get getEnvironment(): "node" | "bun" | "deno" | "unknown";
4
+ static detectProtocol(req: any): "unknown" | "http" | "https";
5
+ static getHost(headers: HeadersParser): string;
6
+ }
package/environment.js ADDED
@@ -0,0 +1,30 @@
1
+ export class EnvironmentDetector {
2
+ static get getEnvironment() {
3
+ if (typeof Bun !== "undefined")
4
+ return "bun";
5
+ if (typeof Deno !== "undefined")
6
+ return "deno";
7
+ if (typeof process !== "undefined" && process.versions?.node)
8
+ return "node";
9
+ return "unknown";
10
+ }
11
+ static detectProtocol(req) {
12
+ try {
13
+ if (this.getEnvironment === "node") {
14
+ return req?.socket?.encrypted ? "https" : "http";
15
+ }
16
+ return "unknown";
17
+ }
18
+ catch (error) {
19
+ throw new Error("Failed to detect protocol.");
20
+ }
21
+ }
22
+ static getHost(headers) {
23
+ try {
24
+ return headers?.get("host") || "unknown";
25
+ }
26
+ catch (error) {
27
+ throw new Error("Failed to get host.");
28
+ }
29
+ }
30
+ }
package/header.d.ts ADDED
@@ -0,0 +1,71 @@
1
+ export declare class HeadersParser {
2
+ private headers;
3
+ constructor(init?: [string, string | string[]][] | Record<string, string>);
4
+ /**
5
+ * Adds multiple headers to the parser.
6
+ * @param headers - Headers as an array of tuples or a record object.
7
+ */
8
+ add(headers: [string, string | string[]][] | Record<string, string>): this;
9
+ /**
10
+ * Sets a header value.
11
+ * @param key - Header name.
12
+ * @param value - Header value(s).
13
+ */
14
+ set(key: string, value: string | string[]): this;
15
+ clear(): this;
16
+ /**
17
+ * Retrieves the first value of a header.
18
+ * @param key - Header name.
19
+ * @returns The first header value or undefined if not found.
20
+ */
21
+ get(key: string): string | undefined;
22
+ /**
23
+ * Retrieves all values of a header.
24
+ * @param key - Header name.
25
+ * @returns An array of header values.
26
+ */
27
+ getAll(key: string): string[];
28
+ /**
29
+ * Checks if a header exists.
30
+ * @param key - Header name.
31
+ * @returns True if the header exists, false otherwise.
32
+ */
33
+ has(key: string): boolean;
34
+ /**
35
+ * Deletes a header.
36
+ * @param key - Header name.
37
+ * @returns True if deleted successfully, false otherwise.
38
+ */
39
+ delete(key: string): boolean;
40
+ /**
41
+ * Appends a value to an existing header or creates a new one.
42
+ * @param key - Header name.
43
+ * @param value - Value to append.
44
+ */
45
+ append(key: string, value: string): this;
46
+ /**
47
+ * Returns an iterator over header entries.
48
+ * @returns IterableIterator of header key-value pairs.
49
+ */
50
+ entries(): IterableIterator<[string, string[]]>;
51
+ /**
52
+ * Returns an iterator over header keys.
53
+ * @returns IterableIterator of header names.
54
+ */
55
+ keys(): IterableIterator<string>;
56
+ /**
57
+ * Returns an iterator over header values.
58
+ * @returns IterableIterator of header values arrays.
59
+ */
60
+ values(): IterableIterator<string[]>;
61
+ /**
62
+ * Iterates over headers and executes a callback function.
63
+ * @param callback - Function to execute for each header.
64
+ */
65
+ forEach(callback: (value: string[], key: string) => void): void;
66
+ /**
67
+ * Converts headers into a plain object.
68
+ * @returns A record of headers where single-value headers are returned as a string.
69
+ */
70
+ toObject(): Record<string, string | string[]>;
71
+ }
package/header.js ADDED
@@ -0,0 +1,81 @@
1
+ export class HeadersParser {
2
+ headers = new Map();
3
+ constructor(init) {
4
+ if (init) {
5
+ this.add(init);
6
+ }
7
+ }
8
+ add(headers) {
9
+ if (Array.isArray(headers)) {
10
+ for (const [key, value] of headers) {
11
+ this.set(key, value);
12
+ }
13
+ }
14
+ else if (typeof Headers !== "undefined" && headers instanceof Headers) {
15
+ for (const [key, value] of headers.entries()) {
16
+ this.set(key, value);
17
+ }
18
+ }
19
+ else if (typeof headers === "object") {
20
+ for (const key in headers) {
21
+ if (Object.prototype.hasOwnProperty.call(headers, key)) {
22
+ this.set(key, headers[key]);
23
+ }
24
+ }
25
+ }
26
+ return this;
27
+ }
28
+ set(key, value) {
29
+ this.headers.set(key.toLowerCase(), Array.isArray(value) ? value : [value]);
30
+ return this;
31
+ }
32
+ clear() {
33
+ this.headers.clear();
34
+ return this;
35
+ }
36
+ get(key) {
37
+ const values = this.headers.get(key.toLowerCase());
38
+ return values ? values[0] : undefined;
39
+ }
40
+ getAll(key) {
41
+ return this.headers.get(key.toLowerCase()) || [];
42
+ }
43
+ has(key) {
44
+ return this.headers.has(key.toLowerCase());
45
+ }
46
+ delete(key) {
47
+ return this.headers.delete(key.toLowerCase());
48
+ }
49
+ append(key, value) {
50
+ const lowerKey = key.toLowerCase();
51
+ if (this.headers.has(lowerKey)) {
52
+ this.headers.get(lowerKey).push(value);
53
+ }
54
+ else {
55
+ this.headers.set(lowerKey, [value]);
56
+ }
57
+ return this;
58
+ }
59
+ entries() {
60
+ return this.headers.entries();
61
+ }
62
+ keys() {
63
+ return this.headers.keys();
64
+ }
65
+ values() {
66
+ return this.headers.values();
67
+ }
68
+ forEach(callback) {
69
+ for (const [key, value] of this.headers) {
70
+ callback(value, key);
71
+ }
72
+ }
73
+ toObject() {
74
+ const obj = {};
75
+ for (const [key, value] of this.headers.entries()) {
76
+ obj[key] = value.length > 1 ? value : value[0];
77
+ }
78
+ return obj;
79
+ }
80
+ }
81
+ Object.defineProperty(HeadersParser, "name", { value: "Headers" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tezx",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
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",
@@ -41,11 +41,7 @@
41
41
  },
42
42
  "homepage": "https://github.com/tezxjs/TezX",
43
43
  "files": [
44
- "index.js",
45
- "index.d.ts",
46
- "cjs/",
47
- "middleware/",
48
- "helper/"
44
+ "./**"
49
45
  ],
50
46
  "keywords": [
51
47
  "server",