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.
- package/README.md +122 -89
- package/bun/getConnInfo.d.ts +21 -0
- package/bun/getConnInfo.js +9 -0
- package/bun/index.d.ts +10 -4
- package/bun/index.js +8 -4
- package/bun/ws.d.ts +48 -0
- package/bun/ws.js +58 -0
- package/cjs/bun/getConnInfo.js +12 -0
- package/cjs/bun/index.js +35 -7
- package/cjs/bun/ws.js +63 -0
- package/cjs/core/config.js +2 -12
- package/cjs/core/context.js +131 -379
- package/cjs/core/error.js +49 -0
- package/cjs/core/request.js +79 -131
- package/cjs/core/router.js +54 -387
- package/cjs/core/server.js +83 -202
- package/cjs/deno/env.js +4 -4
- package/cjs/deno/getConnInfo.js +18 -0
- package/cjs/deno/index.js +11 -18
- package/cjs/deno/serveStatic.js +53 -0
- package/cjs/deno/ws.js +39 -0
- package/cjs/helper/index.js +46 -10
- package/cjs/index.js +5 -7
- package/cjs/jwt/node.js +94 -0
- package/cjs/jwt/web.js +178 -0
- package/cjs/middleware/basic-auth.js +42 -0
- package/cjs/middleware/bearer-auth.js +34 -0
- package/cjs/middleware/cache-control.js +44 -0
- package/cjs/middleware/cors.js +11 -21
- package/cjs/middleware/detect-bot.js +57 -0
- package/cjs/middleware/i18n.js +73 -60
- package/cjs/middleware/index.js +8 -46
- package/cjs/middleware/logger.js +9 -4
- package/cjs/middleware/pagination.js +3 -2
- package/cjs/middleware/powered-by.js +3 -2
- package/cjs/middleware/rate-limiter.js +38 -0
- package/cjs/middleware/request-id.js +4 -5
- package/cjs/middleware/sanitize-headers.js +22 -0
- package/cjs/middleware/secure-headers copy.js +143 -0
- package/cjs/middleware/secure-headers.js +157 -0
- package/cjs/middleware/{xssProtection.js → xss-protection.js} +5 -8
- package/cjs/node/env.js +7 -7
- package/cjs/node/getConnInfo.js +16 -0
- package/cjs/node/index.js +17 -18
- package/cjs/node/mount-node.js +59 -0
- package/cjs/node/serveStatic.js +56 -0
- package/cjs/node/toWebRequest.js +25 -0
- package/cjs/node/ws.js +82 -0
- package/cjs/registry/RadixRouter.js +148 -0
- package/cjs/registry/index.js +17 -0
- package/cjs/types/headers.js +2 -0
- package/cjs/types/index.js +13 -0
- package/cjs/utils/buffer.js +17 -0
- package/cjs/utils/colors.js +2 -0
- package/cjs/utils/cookie.js +59 -0
- package/cjs/utils/file.js +136 -0
- package/cjs/utils/formData.js +60 -10
- package/cjs/utils/generateID.js +37 -0
- package/cjs/utils/low-level.js +115 -0
- package/cjs/utils/{staticFile.js → mimeTypes.js} +0 -87
- package/cjs/utils/rateLimit.js +41 -0
- package/cjs/utils/response.js +65 -0
- package/cjs/{core/environment.js → utils/runtime.js} +2 -1
- package/cjs/utils/url.js +65 -30
- package/core/config.d.ts +2 -7
- package/core/config.js +2 -12
- package/core/context.d.ts +209 -164
- package/core/context.js +131 -346
- package/core/error.d.ts +96 -0
- package/core/error.js +44 -0
- package/core/request.d.ts +67 -107
- package/core/request.js +78 -130
- package/core/router.d.ts +138 -133
- package/core/router.js +53 -352
- package/core/server.d.ts +99 -38
- package/core/server.js +83 -202
- package/deno/env.js +3 -3
- package/deno/getConnInfo.d.ts +21 -0
- package/deno/getConnInfo.js +15 -0
- package/deno/index.d.ts +9 -4
- package/deno/index.js +7 -4
- package/deno/serveStatic.d.ts +28 -0
- package/deno/serveStatic.js +49 -0
- package/deno/ws.d.ts +42 -0
- package/deno/ws.js +36 -0
- package/helper/index.d.ts +29 -15
- package/helper/index.js +27 -7
- package/index.d.ts +10 -8
- package/index.js +4 -5
- package/jwt/node.d.ts +39 -0
- package/jwt/node.js +87 -0
- package/jwt/web.d.ts +14 -0
- package/jwt/web.js +174 -0
- package/middleware/basic-auth.d.ts +56 -0
- package/middleware/basic-auth.js +38 -0
- package/middleware/bearer-auth.d.ts +53 -0
- package/middleware/bearer-auth.js +30 -0
- package/middleware/cache-control.d.ts +30 -0
- package/middleware/cache-control.js +40 -0
- package/middleware/cors.d.ts +30 -3
- package/middleware/cors.js +12 -22
- package/middleware/detect-bot.d.ts +113 -0
- package/middleware/detect-bot.js +53 -0
- package/middleware/i18n.d.ts +166 -73
- package/middleware/i18n.js +73 -60
- package/middleware/index.d.ts +8 -32
- package/middleware/index.js +8 -44
- package/middleware/logger.d.ts +5 -2
- package/middleware/logger.js +9 -4
- package/middleware/pagination.d.ts +9 -6
- package/middleware/pagination.js +3 -2
- package/middleware/powered-by.d.ts +2 -1
- package/middleware/powered-by.js +3 -2
- package/middleware/{rateLimiter.d.ts → rate-limiter.d.ts} +15 -9
- package/middleware/rate-limiter.js +34 -0
- package/middleware/request-id.d.ts +2 -1
- package/middleware/request-id.js +5 -6
- package/middleware/{sanitizeHeader.d.ts → sanitize-headers.d.ts} +5 -19
- package/middleware/sanitize-headers.js +18 -0
- package/middleware/secure-headers copy.d.ts +15 -0
- package/middleware/secure-headers copy.js +136 -0
- package/middleware/secure-headers.d.ts +132 -0
- package/middleware/secure-headers.js +153 -0
- package/middleware/{xssProtection.d.ts → xss-protection.d.ts} +2 -1
- package/middleware/xss-protection.js +19 -0
- package/node/env.js +4 -4
- package/node/getConnInfo.d.ts +21 -0
- package/node/getConnInfo.js +13 -0
- package/node/index.d.ts +13 -4
- package/node/index.js +11 -4
- package/node/mount-node.d.ts +11 -0
- package/node/mount-node.js +56 -0
- package/node/serveStatic.d.ts +36 -0
- package/node/serveStatic.js +52 -0
- package/node/toWebRequest.js +22 -0
- package/node/ws.d.ts +56 -0
- package/node/ws.js +46 -0
- package/package.json +39 -30
- package/registry/RadixRouter.d.ts +40 -0
- package/registry/RadixRouter.js +144 -0
- package/registry/index.d.ts +2 -0
- package/registry/index.js +1 -0
- package/types/headers.d.ts +2 -0
- package/types/headers.js +1 -0
- package/types/index.d.ts +318 -18
- package/types/index.js +12 -1
- package/utils/buffer.d.ts +1 -0
- package/utils/buffer.js +14 -0
- package/utils/colors.d.ts +24 -0
- package/utils/colors.js +2 -0
- package/utils/cookie.d.ts +55 -0
- package/utils/cookie.js +53 -0
- package/utils/file.d.ts +38 -0
- package/utils/file.js +96 -0
- package/utils/formData.d.ts +41 -1
- package/utils/formData.js +58 -9
- package/utils/generateID.d.ts +42 -0
- package/utils/generateID.js +32 -0
- package/utils/httpStatusMap.d.ts +14 -0
- package/utils/low-level.d.ts +58 -0
- package/utils/low-level.js +108 -0
- package/utils/mimeTypes.d.ts +4 -0
- package/utils/{staticFile.js → mimeTypes.js} +0 -53
- package/utils/rateLimit.d.ts +18 -0
- package/utils/rateLimit.js +37 -0
- package/utils/response.d.ts +18 -0
- package/utils/response.js +58 -0
- package/{core/environment.d.ts → utils/runtime.d.ts} +1 -0
- package/{core/environment.js → utils/runtime.js} +1 -0
- package/utils/url.d.ts +42 -14
- package/utils/url.js +61 -27
- package/bun/adapter.d.ts +0 -127
- package/bun/adapter.js +0 -97
- package/cjs/bun/adapter.js +0 -100
- package/cjs/core/MiddlewareConfigure.js +0 -68
- package/cjs/core/common.js +0 -15
- package/cjs/deno/adpater.js +0 -67
- package/cjs/helper/common.js +0 -17
- package/cjs/middleware/basicAuth.js +0 -71
- package/cjs/middleware/cacheControl.js +0 -90
- package/cjs/middleware/detectBot.js +0 -104
- package/cjs/middleware/detectLocale.js +0 -43
- package/cjs/middleware/lazyLoadModules.js +0 -73
- package/cjs/middleware/rateLimiter.js +0 -24
- package/cjs/middleware/requestTimeout.js +0 -42
- package/cjs/middleware/sanitizeHeader.js +0 -51
- package/cjs/middleware/secureHeaders.js +0 -42
- package/cjs/node/adapter.js +0 -138
- package/cjs/utils/regexRouter.js +0 -58
- package/cjs/utils/state.js +0 -34
- package/cjs/utils/toWebRequest.js +0 -35
- package/cjs/ws/deno.js +0 -20
- package/cjs/ws/index.js +0 -53
- package/cjs/ws/node.js +0 -65
- package/core/MiddlewareConfigure.d.ts +0 -15
- package/core/MiddlewareConfigure.js +0 -63
- package/core/common.d.ts +0 -21
- package/core/common.js +0 -11
- package/deno/adpater.d.ts +0 -38
- package/deno/adpater.js +0 -64
- package/helper/common.d.ts +0 -5
- package/helper/common.js +0 -14
- package/middleware/basicAuth.d.ts +0 -81
- package/middleware/basicAuth.js +0 -67
- package/middleware/cacheControl.d.ts +0 -48
- package/middleware/cacheControl.js +0 -53
- package/middleware/detectBot.d.ts +0 -121
- package/middleware/detectBot.js +0 -98
- package/middleware/detectLocale.d.ts +0 -55
- package/middleware/detectLocale.js +0 -39
- package/middleware/lazyLoadModules.d.ts +0 -72
- package/middleware/lazyLoadModules.js +0 -69
- package/middleware/rateLimiter.js +0 -20
- package/middleware/requestTimeout.d.ts +0 -25
- package/middleware/requestTimeout.js +0 -38
- package/middleware/sanitizeHeader.js +0 -47
- package/middleware/secureHeaders.d.ts +0 -78
- package/middleware/secureHeaders.js +0 -38
- package/middleware/xssProtection.js +0 -22
- package/node/adapter.d.ts +0 -46
- package/node/adapter.js +0 -102
- package/utils/regexRouter.d.ts +0 -66
- package/utils/regexRouter.js +0 -53
- package/utils/state.d.ts +0 -50
- package/utils/state.js +0 -30
- package/utils/staticFile.d.ts +0 -10
- package/utils/toWebRequest.js +0 -32
- package/ws/deno.d.ts +0 -6
- package/ws/deno.js +0 -16
- package/ws/index.d.ts +0 -180
- package/ws/index.js +0 -50
- package/ws/node.d.ts +0 -7
- package/ws/node.js +0 -28
- /package/{utils → node}/toWebRequest.d.ts +0 -0
package/core/context.js
CHANGED
|
@@ -1,388 +1,173 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { fileExists, fileSize, getFileBuffer, readStream, } from "../utils/file.js";
|
|
2
|
+
import { extensionExtract } from "../utils/low-level.js";
|
|
3
|
+
import { defaultMimeType, mimeTypes } from "../utils/mimeTypes.js";
|
|
4
|
+
import { determineContentTypeBody, toString } from "../utils/response.js";
|
|
5
|
+
import { TezXError } from "./error.js";
|
|
6
|
+
import { TezXRequest } from "./request.js";
|
|
7
7
|
export class Context {
|
|
8
|
+
#status = 200;
|
|
9
|
+
#headers;
|
|
10
|
+
#req = null;
|
|
11
|
+
#params = {};
|
|
8
12
|
rawRequest;
|
|
13
|
+
#args;
|
|
14
|
+
#body;
|
|
15
|
+
url;
|
|
16
|
+
res;
|
|
9
17
|
env = {};
|
|
10
|
-
headers = new Headers();
|
|
11
18
|
pathname;
|
|
12
|
-
url;
|
|
13
19
|
method;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
this.url = req.url;
|
|
30
|
-
this.rawRequest = req;
|
|
31
|
-
}
|
|
32
|
-
this.pathname = this.req.urlRef.pathname;
|
|
20
|
+
constructor(req, pathname, method, env, args) {
|
|
21
|
+
this.#args = args;
|
|
22
|
+
this.rawRequest = req;
|
|
23
|
+
this.url = req.url;
|
|
24
|
+
this.pathname = pathname;
|
|
25
|
+
this.env = env ?? {};
|
|
26
|
+
this.method = method;
|
|
27
|
+
}
|
|
28
|
+
get getStatus() {
|
|
29
|
+
return this.#status;
|
|
30
|
+
}
|
|
31
|
+
set setStatus(code) {
|
|
32
|
+
this.#status = code;
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
get headers() {
|
|
35
|
+
return this.res?.headers ?? (this.#headers ??= new Headers());
|
|
36
|
+
}
|
|
37
|
+
setHeader(key, value, options) {
|
|
38
|
+
if (!value)
|
|
39
|
+
return this;
|
|
40
|
+
const _key = key.toLowerCase();
|
|
41
|
+
const append = options?.append || _key === "set-cookie";
|
|
42
|
+
const target = this.res?.headers ?? (this.#headers ??= new Headers());
|
|
36
43
|
if (append) {
|
|
37
|
-
|
|
44
|
+
target.append(_key, value);
|
|
38
45
|
}
|
|
39
46
|
else {
|
|
40
|
-
|
|
47
|
+
target.set(_key, value);
|
|
41
48
|
}
|
|
42
49
|
return this;
|
|
43
50
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
let cookies = {};
|
|
47
|
-
if (typeof c == "string") {
|
|
48
|
-
const cookieHeader = c.split(";");
|
|
49
|
-
for (const pair of cookieHeader) {
|
|
50
|
-
const [key, value] = pair?.trim()?.split("=");
|
|
51
|
-
cookies[key] = decodeURIComponent(value);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return {
|
|
55
|
-
get: (cookie) => {
|
|
56
|
-
return cookies?.[cookie];
|
|
57
|
-
},
|
|
58
|
-
all: () => {
|
|
59
|
-
return cookies;
|
|
60
|
-
},
|
|
61
|
-
delete: (name, options) => {
|
|
62
|
-
const value = "";
|
|
63
|
-
const cookieOptions = {
|
|
64
|
-
...options,
|
|
65
|
-
expires: new Date(0),
|
|
66
|
-
};
|
|
67
|
-
const cookieHeader = `${name}=${value};${serializeOptions(cookieOptions)}`;
|
|
68
|
-
this.headers.set("Set-Cookie", cookieHeader);
|
|
69
|
-
},
|
|
70
|
-
set: (name, value, options) => {
|
|
71
|
-
const cookieHeader = `${name}=${value};${serializeOptions(options || {})}`;
|
|
72
|
-
this.headers.set("Set-Cookie", cookieHeader);
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
json(body, ...args) {
|
|
77
|
-
let status = this.#status;
|
|
78
|
-
let headers = {
|
|
79
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
80
|
-
};
|
|
81
|
-
if (typeof args[0] === "number") {
|
|
82
|
-
status = args[0];
|
|
83
|
-
if (typeof args[1] === "object") {
|
|
84
|
-
headers = { ...headers, ...args[1] };
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else if (typeof args[0] === "object") {
|
|
88
|
-
headers = { ...headers, ...args[0] };
|
|
89
|
-
}
|
|
90
|
-
return this.#handleResponse(JSON.stringify(body), {
|
|
91
|
-
status: status,
|
|
92
|
-
headers: headers,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
send(body, ...args) {
|
|
96
|
-
let status = this.#status;
|
|
97
|
-
let headers = {};
|
|
98
|
-
if (typeof args[0] === "number") {
|
|
99
|
-
status = args[0];
|
|
100
|
-
if (typeof args[1] === "object") {
|
|
101
|
-
headers = args[1];
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
else if (typeof args[0] === "object") {
|
|
105
|
-
headers = args[0];
|
|
106
|
-
}
|
|
107
|
-
const contentTypeHeader = headers["Content-Type"] || headers["content-type"] || "";
|
|
108
|
-
if (!contentTypeHeader) {
|
|
109
|
-
if (typeof body === "string") {
|
|
110
|
-
headers["Content-Type"] = "text/plain; charset=utf-8";
|
|
111
|
-
}
|
|
112
|
-
else if (typeof body === "number" || typeof body === "boolean") {
|
|
113
|
-
headers["Content-Type"] = "text/plain; charset=utf-8";
|
|
114
|
-
}
|
|
115
|
-
else if (typeof body === "object" &&
|
|
116
|
-
body !== null &&
|
|
117
|
-
!(body instanceof ReadableStream)) {
|
|
118
|
-
headers["Content-Type"] = "application/json; charset=utf-8";
|
|
119
|
-
body = JSON.stringify(body);
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
headers["Content-Type"] = "application/octet-stream";
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return this.#handleResponse(body, {
|
|
126
|
-
status: status,
|
|
127
|
-
headers,
|
|
128
|
-
});
|
|
51
|
+
set params(params) {
|
|
52
|
+
this.#params = params;
|
|
129
53
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
let data = strings;
|
|
133
|
-
if (Array.isArray(strings)) {
|
|
134
|
-
data = strings.reduce((result, str, i) => {
|
|
135
|
-
const value = args?.[i] ?? "";
|
|
136
|
-
return result + str + value;
|
|
137
|
-
}, "");
|
|
138
|
-
}
|
|
139
|
-
let headers = {
|
|
140
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
141
|
-
};
|
|
142
|
-
if (typeof args[0] === "number") {
|
|
143
|
-
status = args[0];
|
|
144
|
-
if (typeof args[1] === "object") {
|
|
145
|
-
headers = { ...headers, ...args[1] };
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
else if (typeof args[0] === "object") {
|
|
149
|
-
headers = { ...headers, ...args[0] };
|
|
150
|
-
}
|
|
151
|
-
return this.#handleResponse(data, {
|
|
152
|
-
status: status,
|
|
153
|
-
headers: headers,
|
|
154
|
-
});
|
|
54
|
+
get req() {
|
|
55
|
+
return (this.#req ??= new TezXRequest(this.rawRequest, this.method, this.pathname, this.#params));
|
|
155
56
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
let headers = {
|
|
159
|
-
"Content-Type": "text/plain; charset=utf-8",
|
|
160
|
-
};
|
|
161
|
-
if (typeof args[0] === "number") {
|
|
162
|
-
status = args[0];
|
|
163
|
-
if (typeof args[1] === "object") {
|
|
164
|
-
headers = { ...headers, ...args[1] };
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
else if (typeof args[0] === "object") {
|
|
168
|
-
headers = { ...headers, ...args[0] };
|
|
169
|
-
}
|
|
170
|
-
return this.#handleResponse(data, {
|
|
171
|
-
status: status,
|
|
172
|
-
headers: headers,
|
|
173
|
-
});
|
|
57
|
+
get body() {
|
|
58
|
+
return this.#body;
|
|
174
59
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
let headers = {
|
|
178
|
-
"Content-Type": "application/xml; charset=utf-8",
|
|
179
|
-
};
|
|
180
|
-
if (typeof args[0] === "number") {
|
|
181
|
-
status = args[0];
|
|
182
|
-
if (typeof args[1] === "object") {
|
|
183
|
-
headers = { ...headers, ...args[1] };
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
else if (typeof args[0] === "object") {
|
|
187
|
-
headers = { ...headers, ...args[0] };
|
|
188
|
-
}
|
|
189
|
-
return this.#handleResponse(data, {
|
|
190
|
-
status: status,
|
|
191
|
-
headers: headers,
|
|
192
|
-
});
|
|
60
|
+
set body(value) {
|
|
61
|
+
this.#body = value;
|
|
193
62
|
}
|
|
194
63
|
status = (status) => {
|
|
195
64
|
this.#status = status;
|
|
196
65
|
return this;
|
|
197
66
|
};
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
async download(filePath, fileName) {
|
|
211
|
-
try {
|
|
212
|
-
let fileExists = false;
|
|
213
|
-
const runtime = Environment.getEnvironment;
|
|
214
|
-
if (runtime === "node") {
|
|
215
|
-
const { existsSync } = await import("node:fs");
|
|
216
|
-
fileExists = existsSync(filePath);
|
|
217
|
-
}
|
|
218
|
-
else if (runtime === "bun") {
|
|
219
|
-
fileExists = Bun.file(filePath).exists();
|
|
220
|
-
}
|
|
221
|
-
else if (runtime === "deno") {
|
|
222
|
-
try {
|
|
223
|
-
await Deno.stat(filePath);
|
|
224
|
-
fileExists = true;
|
|
67
|
+
#newResponse(body, type, init = {}) {
|
|
68
|
+
const headers = new Headers(this.#headers);
|
|
69
|
+
headers.set("Content-Type", type);
|
|
70
|
+
if (init.headers) {
|
|
71
|
+
for (const key in init.headers) {
|
|
72
|
+
const value = init.headers[key];
|
|
73
|
+
if (!value)
|
|
74
|
+
continue;
|
|
75
|
+
if (key.toLowerCase() === "set-cookie") {
|
|
76
|
+
headers.append(key, value);
|
|
225
77
|
}
|
|
226
|
-
|
|
227
|
-
|
|
78
|
+
else {
|
|
79
|
+
headers.set(key, value);
|
|
228
80
|
}
|
|
229
81
|
}
|
|
230
|
-
if (!fileExists) {
|
|
231
|
-
throw Error("File not found");
|
|
232
|
-
}
|
|
233
|
-
let fileBuffer;
|
|
234
|
-
if (runtime === "node") {
|
|
235
|
-
const { readFileSync } = await import("node:fs");
|
|
236
|
-
fileBuffer = await readFileSync(filePath);
|
|
237
|
-
}
|
|
238
|
-
else if (runtime === "bun") {
|
|
239
|
-
fileBuffer = await Bun.file(filePath)
|
|
240
|
-
.arrayBuffer()
|
|
241
|
-
.then((buf) => new Uint8Array(buf));
|
|
242
|
-
}
|
|
243
|
-
else if (runtime === "deno") {
|
|
244
|
-
fileBuffer = await Deno.readFile(filePath);
|
|
245
|
-
}
|
|
246
|
-
return this.#handleResponse(fileBuffer, {
|
|
247
|
-
status: 200,
|
|
248
|
-
headers: {
|
|
249
|
-
"Content-Disposition": `attachment; filename="${fileName}"`,
|
|
250
|
-
"Content-Type": "application/octet-stream",
|
|
251
|
-
"Content-Length": fileBuffer.byteLength.toString(),
|
|
252
|
-
},
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
catch (error) {
|
|
256
|
-
throw Error("Internal Server Error" + error?.message);
|
|
257
82
|
}
|
|
83
|
+
return new Response(body, {
|
|
84
|
+
status: init.status ?? this.#status,
|
|
85
|
+
statusText: init.statusText,
|
|
86
|
+
headers,
|
|
87
|
+
});
|
|
258
88
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
fileExists = existsSync(resolvedPath);
|
|
267
|
-
}
|
|
268
|
-
else if (runtime === "bun") {
|
|
269
|
-
fileExists = Bun.file(resolvedPath).exists();
|
|
270
|
-
}
|
|
271
|
-
else if (runtime === "deno") {
|
|
272
|
-
try {
|
|
273
|
-
await Deno.stat(resolvedPath);
|
|
274
|
-
fileExists = true;
|
|
89
|
+
newResponse(body, init = {}) {
|
|
90
|
+
const headers = new Headers(this.#headers);
|
|
91
|
+
if (init.headers) {
|
|
92
|
+
for (const key in init.headers) {
|
|
93
|
+
const value = init.headers[key];
|
|
94
|
+
if (!value) {
|
|
95
|
+
continue;
|
|
275
96
|
}
|
|
276
|
-
|
|
277
|
-
|
|
97
|
+
if (key.toLowerCase() === "set-cookie") {
|
|
98
|
+
headers.append(key, value);
|
|
278
99
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
throw Error("File not found");
|
|
282
|
-
}
|
|
283
|
-
let fileSize = 0;
|
|
284
|
-
if (runtime === "node") {
|
|
285
|
-
const { statSync } = await import("node:fs");
|
|
286
|
-
fileSize = statSync(resolvedPath).size;
|
|
287
|
-
}
|
|
288
|
-
else if (runtime === "bun") {
|
|
289
|
-
fileSize = (await Bun.file(resolvedPath).arrayBuffer()).byteLength;
|
|
290
|
-
}
|
|
291
|
-
else if (runtime === "deno") {
|
|
292
|
-
const fileInfo = await Deno.stat(resolvedPath);
|
|
293
|
-
fileSize = fileInfo.size;
|
|
294
|
-
}
|
|
295
|
-
const ext = filePath.split(".").pop()?.toLowerCase() || "";
|
|
296
|
-
const mimeType = mimeTypes[ext] || defaultMimeType;
|
|
297
|
-
let fileStream;
|
|
298
|
-
if (runtime === "node") {
|
|
299
|
-
const { createReadStream } = await import("node:fs");
|
|
300
|
-
fileStream = createReadStream(resolvedPath);
|
|
301
|
-
}
|
|
302
|
-
else if (runtime === "bun") {
|
|
303
|
-
fileStream = Bun.file(resolvedPath).stream();
|
|
304
|
-
}
|
|
305
|
-
else if (runtime === "deno") {
|
|
306
|
-
const file = await Deno.open(resolvedPath, { read: true });
|
|
307
|
-
fileStream = file.readable;
|
|
308
|
-
}
|
|
309
|
-
let headers = {
|
|
310
|
-
"Content-Type": mimeType,
|
|
311
|
-
"Content-Length": fileSize.toString(),
|
|
312
|
-
};
|
|
313
|
-
let fileName = "";
|
|
314
|
-
if (typeof args[0] === "string") {
|
|
315
|
-
fileName = args[0];
|
|
316
|
-
if (typeof args[1] === "object") {
|
|
317
|
-
headers = { ...headers, ...args[1] };
|
|
100
|
+
else {
|
|
101
|
+
headers.set(key, value);
|
|
318
102
|
}
|
|
319
103
|
}
|
|
320
|
-
else if (typeof args[0] === "object") {
|
|
321
|
-
headers = { ...headers, ...args[0] };
|
|
322
|
-
}
|
|
323
|
-
if (fileName) {
|
|
324
|
-
headers["Content-Disposition"] = `attachment; filename="${fileName}"`;
|
|
325
|
-
}
|
|
326
|
-
return this.#handleResponse(fileStream, {
|
|
327
|
-
status: 200,
|
|
328
|
-
headers,
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
catch (error) {
|
|
332
|
-
throw Error("Internal Server Error" + error?.message);
|
|
333
104
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
status: status,
|
|
105
|
+
return new Response(body, {
|
|
106
|
+
status: init.status ?? this.#status,
|
|
107
|
+
statusText: init.statusText,
|
|
338
108
|
headers,
|
|
339
109
|
});
|
|
340
|
-
this.resBody = body;
|
|
341
|
-
return response;
|
|
342
|
-
}
|
|
343
|
-
get req() {
|
|
344
|
-
return new RequestParser({
|
|
345
|
-
method: this.method,
|
|
346
|
-
req: this.rawRequest,
|
|
347
|
-
options: this.#options,
|
|
348
|
-
params: this.#params,
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
set params(params) {
|
|
352
|
-
this.#params = params;
|
|
353
|
-
}
|
|
354
|
-
set body(body) {
|
|
355
|
-
this.#body = body;
|
|
356
110
|
}
|
|
357
|
-
|
|
358
|
-
return this.#
|
|
111
|
+
text(content, init) {
|
|
112
|
+
return this.#newResponse(content, "text/plain; charset=utf-8", init);
|
|
359
113
|
}
|
|
360
|
-
|
|
361
|
-
return this.#
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
function serializeOptions(options) {
|
|
365
|
-
const parts = [];
|
|
366
|
-
if (options.maxAge) {
|
|
367
|
-
parts.push(`Max-Age=${options.maxAge}`);
|
|
114
|
+
html(strings, ...args) {
|
|
115
|
+
return this.#newResponse(toString(strings, args), "text/html; charset=utf-8", args?.[0]);
|
|
368
116
|
}
|
|
369
|
-
|
|
370
|
-
|
|
117
|
+
xml(strings, ...args) {
|
|
118
|
+
return this.#newResponse(toString(strings, args), "text/html; charset=utf-8", args?.[0]);
|
|
371
119
|
}
|
|
372
|
-
|
|
373
|
-
|
|
120
|
+
json(json, init) {
|
|
121
|
+
return this.#newResponse(JSON.stringify(json), "application/json; charset=utf-8", init);
|
|
374
122
|
}
|
|
375
|
-
|
|
376
|
-
|
|
123
|
+
send(body, init) {
|
|
124
|
+
let { body: _body, type } = determineContentTypeBody(body);
|
|
125
|
+
const contentType = init?.headers?.["Content-Type"] ??
|
|
126
|
+
init?.headers?.["content-type"] ??
|
|
127
|
+
type;
|
|
128
|
+
return this.#newResponse(_body, contentType, init);
|
|
377
129
|
}
|
|
378
|
-
|
|
379
|
-
|
|
130
|
+
redirect(url, status = 302) {
|
|
131
|
+
const headers = new Headers(this.#headers);
|
|
132
|
+
headers.set("Location", url);
|
|
133
|
+
return new Response(null, { status, headers });
|
|
134
|
+
}
|
|
135
|
+
async download(filePath, filename) {
|
|
136
|
+
if (!(await fileExists(filePath)))
|
|
137
|
+
throw TezXError.notFound("File not found");
|
|
138
|
+
const buf = await getFileBuffer(filePath);
|
|
139
|
+
const headers = {
|
|
140
|
+
"Content-Disposition": `attachment; filename="${filename}"`,
|
|
141
|
+
"Content-Length": buf.byteLength.toString(),
|
|
142
|
+
};
|
|
143
|
+
return this.#newResponse(buf, "application/octet-stream", {
|
|
144
|
+
status: this.#status,
|
|
145
|
+
headers,
|
|
146
|
+
});
|
|
380
147
|
}
|
|
381
|
-
|
|
382
|
-
|
|
148
|
+
async sendFile(filePath, init) {
|
|
149
|
+
if (!(await fileExists(filePath)))
|
|
150
|
+
throw TezXError.notFound("File not found");
|
|
151
|
+
let { size, mtime } = await fileSize(filePath);
|
|
152
|
+
const ext = extensionExtract(filePath);
|
|
153
|
+
const mimeType = mimeTypes[ext] ?? defaultMimeType;
|
|
154
|
+
let fileStream = await readStream(filePath);
|
|
155
|
+
let headers = {
|
|
156
|
+
"Content-Type": mimeType,
|
|
157
|
+
"Content-Length": size.toString(),
|
|
158
|
+
...init?.headers,
|
|
159
|
+
};
|
|
160
|
+
let filename = init?.filename;
|
|
161
|
+
if (filename) {
|
|
162
|
+
headers["Content-Disposition"] = `attachment; filename="${filename}"`;
|
|
163
|
+
}
|
|
164
|
+
return this.newResponse(fileStream, {
|
|
165
|
+
status: init?.status ?? this.#status,
|
|
166
|
+
statusText: init?.statusText,
|
|
167
|
+
headers,
|
|
168
|
+
});
|
|
383
169
|
}
|
|
384
|
-
|
|
385
|
-
|
|
170
|
+
get args() {
|
|
171
|
+
return this.#args;
|
|
386
172
|
}
|
|
387
|
-
return parts.join("; ");
|
|
388
173
|
}
|
package/core/error.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error type used throughout the Tezx application.
|
|
3
|
+
*
|
|
4
|
+
* Extends the built-in `Error` and carries an HTTP status code plus optional
|
|
5
|
+
* structured `details` payload (useful for validation errors, metadata, etc.).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Throw a 404
|
|
9
|
+
* throw TezXError.notFound("User not found", { userId });
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Create and throw a custom status
|
|
13
|
+
* throw new TezXError("Something went wrong", 502);
|
|
14
|
+
*
|
|
15
|
+
* @class TezXError
|
|
16
|
+
* @extends Error
|
|
17
|
+
*/
|
|
18
|
+
export declare class TezXError extends Error {
|
|
19
|
+
/**
|
|
20
|
+
* HTTP status code representing the error (defaults to 500).
|
|
21
|
+
*/
|
|
22
|
+
statusCode: number;
|
|
23
|
+
/**
|
|
24
|
+
* Optional additional details about the error (validation lists, meta, etc.).
|
|
25
|
+
*/
|
|
26
|
+
details?: any;
|
|
27
|
+
/**
|
|
28
|
+
* Create an instance of TezXError.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} message - Human readable error message.
|
|
31
|
+
* @param {number} [statusCode=500] - HTTP status code to associate with this error.
|
|
32
|
+
* @param {*} [details] - Optional additional error payload (e.g. validation errors).
|
|
33
|
+
*/
|
|
34
|
+
constructor(message: string, statusCode?: number, details?: any);
|
|
35
|
+
/**
|
|
36
|
+
* Create a 400 Bad Request error.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} [message="Bad Request"]
|
|
39
|
+
* @param {*} [details]
|
|
40
|
+
* @returns {TezXError}
|
|
41
|
+
*/
|
|
42
|
+
static badRequest(message?: string, details?: any): TezXError;
|
|
43
|
+
/**
|
|
44
|
+
* Create a 401 Unauthorized error.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} [message="Unauthorized"]
|
|
47
|
+
* @param {*} [details]
|
|
48
|
+
* @returns {TezXError}
|
|
49
|
+
*/
|
|
50
|
+
static unauthorized(message?: string, details?: any): TezXError;
|
|
51
|
+
/**
|
|
52
|
+
* Create a 403 Forbidden error.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} [message="Forbidden"]
|
|
55
|
+
* @param {*} [details]
|
|
56
|
+
* @returns {TezXError}
|
|
57
|
+
*/
|
|
58
|
+
static forbidden(message?: string, details?: any): TezXError;
|
|
59
|
+
/**
|
|
60
|
+
* Create a 404 Not Found error.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} [message="Resource Not Found"]
|
|
63
|
+
* @param {*} [details]
|
|
64
|
+
* @returns {TezXError}
|
|
65
|
+
*/
|
|
66
|
+
static notFound(message?: string, details?: any): TezXError;
|
|
67
|
+
/**
|
|
68
|
+
* Create a 409 Conflict error.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} [message="Conflict"]
|
|
71
|
+
* @param {*} [details]
|
|
72
|
+
* @returns {TezXError}
|
|
73
|
+
*/
|
|
74
|
+
static conflict(message?: string, details?: any): TezXError;
|
|
75
|
+
/**
|
|
76
|
+
* Create a 500 Internal Server Error.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} [message="Internal Server Error"]
|
|
79
|
+
* @param {*} [details]
|
|
80
|
+
* @returns {TezXError}
|
|
81
|
+
*/
|
|
82
|
+
static internal(message?: string, details?: any): TezXError;
|
|
83
|
+
/**
|
|
84
|
+
* Convert the error into a JSON-serializable object that can be sent
|
|
85
|
+
* in HTTP responses or logged safely.
|
|
86
|
+
*
|
|
87
|
+
* @returns {{ error: boolean, message: string, statusCode: number, details: any }}
|
|
88
|
+
*/
|
|
89
|
+
toJSON(): {
|
|
90
|
+
error: boolean;
|
|
91
|
+
message: string;
|
|
92
|
+
statusCode: number;
|
|
93
|
+
details: any;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export declare function TezXErrorParse(err: unknown, statusCode?: number): TezXError;
|
package/core/error.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export class TezXError extends Error {
|
|
2
|
+
statusCode;
|
|
3
|
+
details;
|
|
4
|
+
constructor(message, statusCode = 500, details) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.statusCode = statusCode;
|
|
7
|
+
this.details = details;
|
|
8
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
9
|
+
Error.captureStackTrace(this, this.constructor);
|
|
10
|
+
}
|
|
11
|
+
static badRequest(message = "Bad Request", details) {
|
|
12
|
+
return new TezXError(message, 400, details);
|
|
13
|
+
}
|
|
14
|
+
static unauthorized(message = "Unauthorized", details) {
|
|
15
|
+
return new TezXError(message, 401, details);
|
|
16
|
+
}
|
|
17
|
+
static forbidden(message = "Forbidden", details) {
|
|
18
|
+
return new TezXError(message, 403, details);
|
|
19
|
+
}
|
|
20
|
+
static notFound(message = "Resource Not Found", details) {
|
|
21
|
+
return new TezXError(message, 404, details);
|
|
22
|
+
}
|
|
23
|
+
static conflict(message = "Conflict", details) {
|
|
24
|
+
return new TezXError(message, 409, details);
|
|
25
|
+
}
|
|
26
|
+
static internal(message = "Internal Server Error", details) {
|
|
27
|
+
return new TezXError(message, 500, details);
|
|
28
|
+
}
|
|
29
|
+
toJSON() {
|
|
30
|
+
return {
|
|
31
|
+
error: true,
|
|
32
|
+
message: this.message,
|
|
33
|
+
statusCode: this.statusCode,
|
|
34
|
+
details: this.details ?? null,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function TezXErrorParse(err, statusCode) {
|
|
39
|
+
if (err instanceof TezXError)
|
|
40
|
+
return err;
|
|
41
|
+
else if (err instanceof Error)
|
|
42
|
+
return new TezXError(err?.message, 500, err?.stack);
|
|
43
|
+
return new TezXError(String(err), statusCode);
|
|
44
|
+
}
|