tezx 3.0.14-beta → 3.0.14-beta2
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/bun/env.d.ts +5 -0
- package/bun/env.js +44 -0
- package/bun/index.d.ts +1 -1
- package/bun/index.js +1 -1
- package/bun/ws.js +4 -3
- package/cjs/bun/env.js +47 -0
- package/cjs/bun/index.js +1 -1
- package/cjs/bun/ws.js +4 -3
- package/cjs/core/context.js +4 -3
- package/cjs/core/error.js +8 -0
- package/cjs/core/request.js +3 -2
- package/cjs/core/router.js +11 -17
- package/cjs/core/server.js +14 -9
- package/cjs/deno/env.js +2 -1
- package/cjs/deno/serveStatic.js +2 -1
- package/cjs/deno/ws.js +4 -3
- package/cjs/helper/index.js +12 -10
- package/cjs/index.js +1 -1
- package/cjs/jwt/node.js +94 -0
- package/cjs/jwt/web.js +178 -0
- package/cjs/middleware/basic-auth.js +9 -14
- package/cjs/middleware/bearer-auth.js +5 -5
- package/cjs/middleware/cache-control.js +44 -0
- package/cjs/middleware/cors.js +1 -1
- package/cjs/middleware/detect-bot.js +57 -0
- package/cjs/middleware/i18n.js +92 -0
- package/cjs/middleware/index.js +5 -0
- package/cjs/middleware/logger.js +3 -2
- package/cjs/middleware/rate-limiter.js +1 -1
- package/cjs/middleware/sanitize-headers.js +1 -1
- package/cjs/middleware/secure-headers copy.js +143 -0
- package/cjs/middleware/secure-headers.js +157 -0
- package/cjs/node/env.js +4 -3
- package/cjs/node/serveStatic.js +2 -1
- package/cjs/node/ws.js +3 -2
- package/cjs/registry/RadixRouter.js +2 -33
- package/cjs/utils/buffer.js +17 -0
- package/cjs/utils/file.js +28 -6
- package/cjs/utils/generateID.js +10 -0
- package/cjs/utils/response.js +3 -1
- package/core/context.d.ts +3 -3
- package/core/context.js +4 -3
- package/core/error.d.ts +1 -0
- package/core/error.js +7 -0
- package/core/request.js +3 -2
- package/core/router.d.ts +3 -8
- package/core/router.js +11 -17
- package/core/server.d.ts +10 -23
- package/core/server.js +15 -10
- package/deno/env.js +2 -1
- package/deno/index.d.ts +1 -1
- package/deno/serveStatic.js +2 -1
- package/deno/ws.d.ts +1 -1
- package/deno/ws.js +4 -3
- package/helper/index.d.ts +7 -6
- package/helper/index.js +7 -6
- package/index.d.ts +5 -2
- package/index.js +1 -1
- 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 +2 -1
- package/middleware/basic-auth.js +9 -14
- package/middleware/bearer-auth.d.ts +2 -1
- package/middleware/bearer-auth.js +5 -5
- package/middleware/cache-control.d.ts +30 -0
- package/middleware/cache-control.js +40 -0
- package/middleware/cors.js +1 -1
- package/middleware/detect-bot.d.ts +113 -0
- package/middleware/detect-bot.js +53 -0
- package/middleware/i18n.d.ts +194 -0
- package/middleware/i18n.js +88 -0
- package/middleware/index.d.ts +5 -0
- package/middleware/index.js +5 -0
- package/middleware/logger.d.ts +1 -1
- package/middleware/logger.js +3 -2
- package/middleware/rate-limiter.d.ts +3 -2
- package/middleware/rate-limiter.js +1 -1
- package/middleware/sanitize-headers.js +1 -1
- 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/node/env.js +4 -3
- package/node/serveStatic.d.ts +8 -0
- package/node/serveStatic.js +2 -1
- package/node/ws.d.ts +1 -1
- package/node/ws.js +3 -2
- package/package.json +12 -1
- package/registry/RadixRouter.js +2 -33
- package/types/index.d.ts +1 -1
- package/utils/buffer.d.ts +1 -0
- package/utils/buffer.js +14 -0
- package/utils/file.d.ts +6 -2
- package/utils/file.js +27 -6
- package/utils/generateID.d.ts +9 -0
- package/utils/generateID.js +9 -0
- package/utils/response.js +3 -1
- package/cjs/utils/regexRouter.js +0 -57
- package/utils/regexRouter.d.ts +0 -66
- package/utils/regexRouter.js +0 -52
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.secureHeaders = void 0;
|
|
4
|
+
const index_js_1 = require("../helper/index.js");
|
|
5
|
+
const joinSrc = (v) => typeof v === "string" ? v : v.join(" ");
|
|
6
|
+
const buildCSPString = (cspObj) => {
|
|
7
|
+
const parts = [];
|
|
8
|
+
for (const key in cspObj)
|
|
9
|
+
parts.push(`${key} ${joinSrc(cspObj[key])}`);
|
|
10
|
+
return parts.join("; ");
|
|
11
|
+
};
|
|
12
|
+
const secureHeaders = (userOpts = {}) => {
|
|
13
|
+
const defaultPresets = {
|
|
14
|
+
strict: {
|
|
15
|
+
preset: "strict",
|
|
16
|
+
hsts: { maxAge: 63072000, includeSubDomains: true, preload: true },
|
|
17
|
+
frameGuard: "DENY",
|
|
18
|
+
noSniff: true,
|
|
19
|
+
xssProtection: true,
|
|
20
|
+
referrerPolicy: "strict-origin-when-cross-origin",
|
|
21
|
+
permissionsPolicy: "geolocation=(), microphone=(), camera=(), usb=()",
|
|
22
|
+
csp: {
|
|
23
|
+
"default-src": ["'self'"],
|
|
24
|
+
"script-src": ["'self'"],
|
|
25
|
+
"style-src": ["'self'", "'unsafe-inline'"],
|
|
26
|
+
"img-src": ["'self'", "data:", "blob:"],
|
|
27
|
+
"font-src": ["'self'"],
|
|
28
|
+
"connect-src": ["'self'"],
|
|
29
|
+
"object-src": ["'none'"],
|
|
30
|
+
"frame-ancestors": ["'none'"],
|
|
31
|
+
},
|
|
32
|
+
cspReportOnly: false,
|
|
33
|
+
},
|
|
34
|
+
balanced: {
|
|
35
|
+
preset: "balanced",
|
|
36
|
+
hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
|
|
37
|
+
frameGuard: "SAMEORIGIN",
|
|
38
|
+
noSniff: true,
|
|
39
|
+
xssProtection: true,
|
|
40
|
+
referrerPolicy: "no-referrer-when-downgrade",
|
|
41
|
+
permissionsPolicy: "geolocation=(), microphone=()",
|
|
42
|
+
csp: {
|
|
43
|
+
"default-src": ["'self'"],
|
|
44
|
+
"script-src": ["'self'", "https://cdn.jsdelivr.net"],
|
|
45
|
+
"style-src": [
|
|
46
|
+
"'self'",
|
|
47
|
+
"'unsafe-inline'",
|
|
48
|
+
"https://fonts.googleapis.com",
|
|
49
|
+
],
|
|
50
|
+
"img-src": ["'self'", "data:", "https://images.example.com"],
|
|
51
|
+
"connect-src": ["'self'", "https://api.example.com"],
|
|
52
|
+
},
|
|
53
|
+
cspReportOnly: true,
|
|
54
|
+
},
|
|
55
|
+
dev: {
|
|
56
|
+
preset: "dev",
|
|
57
|
+
hsts: undefined,
|
|
58
|
+
frameGuard: "SAMEORIGIN",
|
|
59
|
+
noSniff: false,
|
|
60
|
+
xssProtection: false,
|
|
61
|
+
referrerPolicy: "no-referrer",
|
|
62
|
+
permissionsPolicy: "",
|
|
63
|
+
csp: {
|
|
64
|
+
"default-src": [
|
|
65
|
+
"'self'",
|
|
66
|
+
"'unsafe-inline'",
|
|
67
|
+
"'unsafe-eval'",
|
|
68
|
+
"http://localhost:3000",
|
|
69
|
+
],
|
|
70
|
+
"img-src": ["'self'", "data:", "blob:"],
|
|
71
|
+
},
|
|
72
|
+
cspReportOnly: true,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
const preset = userOpts.preset ?? "balanced";
|
|
76
|
+
const base = {
|
|
77
|
+
...(defaultPresets[preset] || defaultPresets.balanced),
|
|
78
|
+
...userOpts,
|
|
79
|
+
};
|
|
80
|
+
const frameHeader = base.frameGuard || "SAMEORIGIN";
|
|
81
|
+
const xssHeader = base.xssProtection ? "1; mode=block" : "0";
|
|
82
|
+
const noSniffHeader = base.noSniff ? "nosniff" : "";
|
|
83
|
+
const permissionsHeader = base.permissionsPolicy || "";
|
|
84
|
+
const referrerHeader = base.referrerPolicy || "no-referrer";
|
|
85
|
+
const hstsParts = [`max-age=${base.hsts?.maxAge || 31536000}`];
|
|
86
|
+
if (base.hsts?.includeSubDomains)
|
|
87
|
+
hstsParts.push("includeSubDomains");
|
|
88
|
+
if (base.hsts?.preload)
|
|
89
|
+
hstsParts.push("preload");
|
|
90
|
+
const hstsHeader = hstsParts.join("; ");
|
|
91
|
+
let cspStatic = null;
|
|
92
|
+
let cspNeedsNonce = !!base.cspUseNonce;
|
|
93
|
+
if (typeof base.csp === "string")
|
|
94
|
+
cspStatic = base.csp;
|
|
95
|
+
else if (base.csp)
|
|
96
|
+
cspStatic = buildCSPString(base.csp);
|
|
97
|
+
const cspReportOnly = !!base.cspReportOnly;
|
|
98
|
+
const ultraFast = !!base.ultraFastMode;
|
|
99
|
+
if (cspNeedsNonce && ultraFast) {
|
|
100
|
+
index_js_1.GlobalConfig.debugging.warn("secureHeaders: ultraFastMode disables CSP nonce support. Nonce will not be used.");
|
|
101
|
+
}
|
|
102
|
+
if (ultraFast)
|
|
103
|
+
cspNeedsNonce = false;
|
|
104
|
+
return async (ctx, next) => {
|
|
105
|
+
try {
|
|
106
|
+
if (base.hsts) {
|
|
107
|
+
const proto = (ctx.req?.header("x-forwarded-proto") || "").toString();
|
|
108
|
+
if (!base.hsts.hstsOnlyOnHttps || proto.includes("https")) {
|
|
109
|
+
ctx.headers.set("Strict-Transport-Security", hstsHeader);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
ctx.headers.set("X-Frame-Options", frameHeader);
|
|
113
|
+
ctx.headers.set("X-Content-Type-Options", noSniffHeader);
|
|
114
|
+
ctx.headers.set("X-XSS-Protection", xssHeader);
|
|
115
|
+
ctx.headers.set("Referrer-Policy", referrerHeader);
|
|
116
|
+
if (permissionsHeader)
|
|
117
|
+
ctx.headers.set("Permissions-Policy", permissionsHeader);
|
|
118
|
+
if (cspNeedsNonce) {
|
|
119
|
+
const nonce = (0, index_js_1.generateRandomBase64)();
|
|
120
|
+
let cspHeader = cspStatic;
|
|
121
|
+
if (!cspHeader) {
|
|
122
|
+
cspHeader = `default-src 'self'; script-src 'self' 'nonce-${nonce}'`;
|
|
123
|
+
}
|
|
124
|
+
if (typeof base.csp === "object") {
|
|
125
|
+
const idx = cspHeader.indexOf("script-src");
|
|
126
|
+
if (idx >= 0) {
|
|
127
|
+
const parts = [];
|
|
128
|
+
parts.push(cspHeader.slice(0, idx + 10));
|
|
129
|
+
parts.push(" 'nonce-" + nonce + "'");
|
|
130
|
+
parts.push(cspHeader.slice(idx + 10));
|
|
131
|
+
cspHeader = parts.join("");
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
cspHeader += "; script-src 'self' 'nonce-" + nonce + "'";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
ctx.cspNonce = nonce;
|
|
138
|
+
if (cspReportOnly)
|
|
139
|
+
ctx.headers.set("Content-Security-Policy-Report-Only", cspHeader);
|
|
140
|
+
else
|
|
141
|
+
ctx.headers.set("Content-Security-Policy", cspHeader);
|
|
142
|
+
}
|
|
143
|
+
else if (cspStatic) {
|
|
144
|
+
if (cspReportOnly)
|
|
145
|
+
ctx.headers.set("Content-Security-Policy-Report-Only", cspStatic);
|
|
146
|
+
else
|
|
147
|
+
ctx.headers.set("Content-Security-Policy", cspStatic);
|
|
148
|
+
}
|
|
149
|
+
return await next();
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
console.error("secureHeaders middleware error", err);
|
|
153
|
+
return await next();
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
exports.secureHeaders = secureHeaders;
|
package/cjs/node/env.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.loadEnv = loadEnv;
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
|
-
const
|
|
5
|
+
const error_js_1 = require("../core/error.js");
|
|
6
6
|
const colors_js_1 = require("../utils/colors.js");
|
|
7
|
+
const runtime_js_1 = require("../utils/runtime.js");
|
|
7
8
|
function parseEnvFile(filePath, result) {
|
|
8
9
|
try {
|
|
9
|
-
if (runtime_js_1.runtime !== "
|
|
10
|
-
throw new
|
|
10
|
+
if (runtime_js_1.runtime !== "node") {
|
|
11
|
+
throw new error_js_1.TezXError(`Please use ${(0, colors_js_1.colorText)(`import {loadEnv} from "tezx/${runtime_js_1.runtime}"`, "bgRed")} environment`);
|
|
11
12
|
}
|
|
12
13
|
let fileExists = (0, node_fs_1.existsSync)(filePath);
|
|
13
14
|
if (!fileExists) {
|
package/cjs/node/serveStatic.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.getFiles = getFiles;
|
|
|
5
5
|
const node_fs_1 = require("node:fs");
|
|
6
6
|
const node_path_1 = require("node:path");
|
|
7
7
|
const low_level_js_1 = require("../utils/low-level.js");
|
|
8
|
+
const error_js_1 = require("../core/error.js");
|
|
8
9
|
function serveStatic(...args) {
|
|
9
10
|
let route = "";
|
|
10
11
|
let dir;
|
|
@@ -25,7 +26,7 @@ function serveStatic(...args) {
|
|
|
25
26
|
[dir] = args;
|
|
26
27
|
break;
|
|
27
28
|
default:
|
|
28
|
-
throw new
|
|
29
|
+
throw new error_js_1.TezXError(`\x1b[1;31m404 Not Found\x1b[0m \x1b[1;32mInvalid arguments\x1b[0m`);
|
|
29
30
|
}
|
|
30
31
|
return {
|
|
31
32
|
files: getFiles(dir, route, options),
|
package/cjs/node/ws.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.upgradeWebSocket = upgradeWebSocket;
|
|
37
|
+
const error_js_1 = require("../core/error.js");
|
|
37
38
|
function upgradeWebSocket(callback, options = {}) {
|
|
38
39
|
const { onUpgradeError = (error, ctx) => {
|
|
39
40
|
ctx.setStatus = 401;
|
|
@@ -49,13 +50,13 @@ function upgradeWebSocket(callback, options = {}) {
|
|
|
49
50
|
return next();
|
|
50
51
|
}
|
|
51
52
|
ctx.setStatus = 401;
|
|
52
|
-
return onUpgradeError(new
|
|
53
|
+
return onUpgradeError(new error_js_1.TezXError("401 Bad Request: Invalid WebSocket headers", 401), ctx);
|
|
53
54
|
}
|
|
54
55
|
ctx.wsProtocol = ctx.url?.startsWith("https") ? "wss" : "ws";
|
|
55
56
|
const server = ctx.args?.[2];
|
|
56
57
|
if (!server?.on) {
|
|
57
58
|
ctx.setStatus = 500;
|
|
58
|
-
return onUpgradeError(new
|
|
59
|
+
return onUpgradeError(new error_js_1.TezXError("Node server instance missing for WebSocket", 426), ctx);
|
|
59
60
|
}
|
|
60
61
|
const { WebSocketServer } = await Promise.resolve().then(() => __importStar(require("ws")));
|
|
61
62
|
const wss = new WebSocketServer({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RadixRouter = void 0;
|
|
4
|
+
const error_js_1 = require("../core/error.js");
|
|
4
5
|
const index_js_1 = require("../helper/index.js");
|
|
5
6
|
class RadixRouter {
|
|
6
7
|
root = { children: {} };
|
|
@@ -22,7 +23,7 @@ class RadixRouter {
|
|
|
22
23
|
}
|
|
23
24
|
else if (node.children[":"]?.paramName !== paramName ||
|
|
24
25
|
node.children[":"]?.isOptional !== isOptional) {
|
|
25
|
-
throw new
|
|
26
|
+
throw new error_js_1.TezXError(`Conflicting param definition for ${paramName}`);
|
|
26
27
|
}
|
|
27
28
|
node = node.children[":"];
|
|
28
29
|
}
|
|
@@ -145,35 +146,3 @@ class RadixRouter {
|
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
exports.RadixRouter = RadixRouter;
|
|
148
|
-
const routes = [
|
|
149
|
-
"/",
|
|
150
|
-
"/users",
|
|
151
|
-
"/users/:id",
|
|
152
|
-
"/users/:id/profile",
|
|
153
|
-
"/posts/:postId?",
|
|
154
|
-
"/files/*",
|
|
155
|
-
"/admin/settings",
|
|
156
|
-
"/search/:term?",
|
|
157
|
-
"/categories/:categoryId/products/:productId",
|
|
158
|
-
"/about",
|
|
159
|
-
];
|
|
160
|
-
const testPaths = [
|
|
161
|
-
"/",
|
|
162
|
-
"/users",
|
|
163
|
-
"/users/123",
|
|
164
|
-
"/users/123/profile",
|
|
165
|
-
"/posts",
|
|
166
|
-
"/posts/456",
|
|
167
|
-
"/files/path/to/file.txt",
|
|
168
|
-
"/admin/settings",
|
|
169
|
-
"/search",
|
|
170
|
-
"/search/nodejs",
|
|
171
|
-
"/categories/12/products/999",
|
|
172
|
-
"/notfound",
|
|
173
|
-
];
|
|
174
|
-
const router = new RadixRouter();
|
|
175
|
-
let x = function xx() {
|
|
176
|
-
return {
|
|
177
|
-
body: "3453455",
|
|
178
|
-
};
|
|
179
|
-
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.base64Decode = base64Decode;
|
|
4
|
+
function base64Decode(base64) {
|
|
5
|
+
const pad = base64.length % 4;
|
|
6
|
+
if (pad)
|
|
7
|
+
base64 += "=".repeat(4 - pad);
|
|
8
|
+
if (typeof Buffer !== "undefined") {
|
|
9
|
+
return Buffer.from(base64, "base64").toString("utf-8");
|
|
10
|
+
}
|
|
11
|
+
else if (typeof atob === "function") {
|
|
12
|
+
return new TextDecoder().decode(Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)));
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
throw new Error("Base64 decode not supported in this environment");
|
|
16
|
+
}
|
|
17
|
+
}
|
package/cjs/utils/file.js
CHANGED
|
@@ -37,6 +37,8 @@ exports.fileExists = fileExists;
|
|
|
37
37
|
exports.getFileBuffer = getFileBuffer;
|
|
38
38
|
exports.readStream = readStream;
|
|
39
39
|
exports.fileSize = fileSize;
|
|
40
|
+
exports.etagDigest = etagDigest;
|
|
41
|
+
const error_js_1 = require("../core/error.js");
|
|
40
42
|
const runtime_js_1 = require("./runtime.js");
|
|
41
43
|
async function fileExists(path) {
|
|
42
44
|
switch (runtime_js_1.runtime) {
|
|
@@ -76,7 +78,7 @@ async function getFileBuffer(path) {
|
|
|
76
78
|
case "deno":
|
|
77
79
|
return Deno.readFile(path);
|
|
78
80
|
default:
|
|
79
|
-
throw new
|
|
81
|
+
throw new error_js_1.TezXError("Unsupported runtime environment");
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
async function readStream(path) {
|
|
@@ -93,22 +95,42 @@ async function readStream(path) {
|
|
|
93
95
|
return (await Deno.open(path, { read: true })).readable;
|
|
94
96
|
}
|
|
95
97
|
default:
|
|
96
|
-
throw new
|
|
98
|
+
throw new error_js_1.TezXError("Unsupported runtime environment");
|
|
97
99
|
}
|
|
98
100
|
}
|
|
99
101
|
async function fileSize(path) {
|
|
100
102
|
switch (runtime_js_1.runtime) {
|
|
101
103
|
case "node": {
|
|
102
104
|
const { stat } = await Promise.resolve().then(() => __importStar(require("node:fs/promises")));
|
|
103
|
-
|
|
105
|
+
const st = await stat(path);
|
|
106
|
+
return { size: st.size, mtime: st.mtime };
|
|
104
107
|
}
|
|
105
108
|
case "bun": {
|
|
106
|
-
|
|
109
|
+
const st = await Bun.file(path).stat();
|
|
110
|
+
return { size: st.size, mtime: st.mtime };
|
|
107
111
|
}
|
|
108
112
|
case "deno": {
|
|
109
|
-
|
|
113
|
+
const st = await Deno.stat(path);
|
|
114
|
+
return {
|
|
115
|
+
size: st.size,
|
|
116
|
+
mtime: st.mtime ?? new Date(),
|
|
117
|
+
};
|
|
110
118
|
}
|
|
111
119
|
default:
|
|
112
|
-
|
|
120
|
+
throw new error_js_1.TezXError("Unsupported runtime: " + runtime_js_1.runtime);
|
|
113
121
|
}
|
|
114
122
|
}
|
|
123
|
+
async function etagDigest(algo = "MD5", data) {
|
|
124
|
+
const encoded = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
125
|
+
if (runtime_js_1.runtime === "bun") {
|
|
126
|
+
return Bun.hash(data, 256).toString(16);
|
|
127
|
+
}
|
|
128
|
+
if (globalThis?.crypto?.subtle) {
|
|
129
|
+
const buffer = await crypto.subtle.digest(algo, encoded);
|
|
130
|
+
return Array.from(new Uint8Array(buffer))
|
|
131
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
132
|
+
.join("");
|
|
133
|
+
}
|
|
134
|
+
const { createHash } = await Promise.resolve().then(() => __importStar(require("node:crypto")));
|
|
135
|
+
return createHash(algo).update(encoded).digest("hex");
|
|
136
|
+
}
|
package/cjs/utils/generateID.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateID = generateID;
|
|
4
4
|
exports.generateUUID = generateUUID;
|
|
5
|
+
exports.generateRandomBase64 = generateRandomBase64;
|
|
5
6
|
function generateID() {
|
|
6
7
|
const timestamp = Date.now().toString(16);
|
|
7
8
|
let randomHex = "";
|
|
@@ -25,3 +26,12 @@ function generateUUID() {
|
|
|
25
26
|
return crypto.randomUUID();
|
|
26
27
|
return generateID();
|
|
27
28
|
}
|
|
29
|
+
function generateRandomBase64(length = 16) {
|
|
30
|
+
let result = "";
|
|
31
|
+
const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
32
|
+
for (let i = 0; i < length; i++) {
|
|
33
|
+
const idx = Math.floor(Math.random() * 64);
|
|
34
|
+
result += BASE64[idx];
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
package/cjs/utils/response.js
CHANGED
|
@@ -16,7 +16,9 @@ exports.notFoundResponse = notFoundResponse;
|
|
|
16
16
|
async function handleErrorResponse(err = error_js_1.TezXError.internal(), ctx) {
|
|
17
17
|
if (err instanceof error_js_1.TezXError) {
|
|
18
18
|
config_js_1.GlobalConfig.debugging.error(err.details ?? err?.message);
|
|
19
|
-
return ctx
|
|
19
|
+
return ctx
|
|
20
|
+
.status(err.statusCode ?? 500)
|
|
21
|
+
.send(err.details ?? err?.message ?? "Internal Server Error");
|
|
20
22
|
}
|
|
21
23
|
return await handleErrorResponse(error_js_1.TezXError.internal(), ctx);
|
|
22
24
|
}
|
package/core/context.d.ts
CHANGED
|
@@ -252,9 +252,9 @@ export declare class Context<TEnv extends Record<string, any> = {}, TPath extend
|
|
|
252
252
|
filename?: string;
|
|
253
253
|
}): Promise<HttpBaseResponse>;
|
|
254
254
|
/**
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
255
|
+
*@property bun [server]
|
|
256
|
+
*@property deno [connInfo]
|
|
257
|
+
*@property node [res, server]
|
|
258
258
|
*/
|
|
259
259
|
protected get args(): any;
|
|
260
260
|
}
|
package/core/context.js
CHANGED
|
@@ -2,6 +2,7 @@ import { fileExists, fileSize, getFileBuffer, readStream, } from "../utils/file.
|
|
|
2
2
|
import { extensionExtract } from "../utils/low-level.js";
|
|
3
3
|
import { defaultMimeType, mimeTypes } from "../utils/mimeTypes.js";
|
|
4
4
|
import { determineContentTypeBody, toString } from "../utils/response.js";
|
|
5
|
+
import { TezXError } from "./error.js";
|
|
5
6
|
import { TezXRequest } from "./request.js";
|
|
6
7
|
export class Context {
|
|
7
8
|
#status = 200;
|
|
@@ -133,7 +134,7 @@ export class Context {
|
|
|
133
134
|
}
|
|
134
135
|
async download(filePath, filename) {
|
|
135
136
|
if (!(await fileExists(filePath)))
|
|
136
|
-
throw
|
|
137
|
+
throw TezXError.notFound("File not found");
|
|
137
138
|
const buf = await getFileBuffer(filePath);
|
|
138
139
|
const headers = {
|
|
139
140
|
"Content-Disposition": `attachment; filename="${filename}"`,
|
|
@@ -146,8 +147,8 @@ export class Context {
|
|
|
146
147
|
}
|
|
147
148
|
async sendFile(filePath, init) {
|
|
148
149
|
if (!(await fileExists(filePath)))
|
|
149
|
-
throw
|
|
150
|
-
let size = await fileSize(filePath);
|
|
150
|
+
throw TezXError.notFound("File not found");
|
|
151
|
+
let { size, mtime } = await fileSize(filePath);
|
|
151
152
|
const ext = extensionExtract(filePath);
|
|
152
153
|
const mimeType = mimeTypes[ext] ?? defaultMimeType;
|
|
153
154
|
let fileStream = await readStream(filePath);
|
package/core/error.d.ts
CHANGED
package/core/error.js
CHANGED
|
@@ -35,3 +35,10 @@ export class TezXError extends Error {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
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
|
+
}
|
package/core/request.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { url2query } from "../utils/url.js";
|
|
2
|
+
import { TezXError } from "./error.js";
|
|
2
3
|
export class TezXRequest {
|
|
3
4
|
url;
|
|
4
5
|
method;
|
|
@@ -80,11 +81,11 @@ export class TezXRequest {
|
|
|
80
81
|
return this.#cachedFormObject;
|
|
81
82
|
const ct = this.#contentType;
|
|
82
83
|
if (!ct)
|
|
83
|
-
throw new
|
|
84
|
+
throw new TezXError("Missing Content-Type");
|
|
84
85
|
if (ct === "application/x-www-form-urlencoded" ||
|
|
85
86
|
ct === "multipart/form-data") {
|
|
86
87
|
if (this.#bodyConsumed) {
|
|
87
|
-
throw new
|
|
88
|
+
throw new TezXError("Multipart body already consumed elsewhere");
|
|
88
89
|
}
|
|
89
90
|
this.#cachedFormObject = (await this.#rawRequest.formData());
|
|
90
91
|
this.#bodyConsumed = true;
|
package/core/router.d.ts
CHANGED
|
@@ -3,11 +3,6 @@ import { Callback, HandlerType, HTTPMethod, Middleware, RouteRegistry, ServeStat
|
|
|
3
3
|
* Router configuration options.
|
|
4
4
|
*/
|
|
5
5
|
export type RouterConfig = {
|
|
6
|
-
/**
|
|
7
|
-
* Custom route registry instance used internally to store routes.
|
|
8
|
-
* If not provided, the router will use the default CombineRouteRegistry.
|
|
9
|
-
*/
|
|
10
|
-
routeRegistry?: RouteRegistry;
|
|
11
6
|
/**
|
|
12
7
|
* `env` allows you to define environment variables for the router instance.
|
|
13
8
|
* Example: `{ NODE_ENV: "production", API_VERSION: 2 }`
|
|
@@ -33,7 +28,7 @@ export declare class Router<T extends Record<string, any> = {}> {
|
|
|
33
28
|
/** Environment variables accessible within this router */
|
|
34
29
|
protected env: Record<string, string | number>;
|
|
35
30
|
/** Internal route registry to hold all routes */
|
|
36
|
-
protected router
|
|
31
|
+
protected router?: RouteRegistry;
|
|
37
32
|
/** Array tracking registered routes and their handlers */
|
|
38
33
|
protected route: {
|
|
39
34
|
method: string;
|
|
@@ -52,7 +47,7 @@ export declare class Router<T extends Record<string, any> = {}> {
|
|
|
52
47
|
* @param config.env - Environment variables for router
|
|
53
48
|
* @param config.routeRegistry - Custom route registry instance
|
|
54
49
|
*/
|
|
55
|
-
constructor({ basePath, env
|
|
50
|
+
constructor({ basePath, env }?: RouterConfig);
|
|
56
51
|
/**
|
|
57
52
|
* Registers static file routes to the application for serving files like HTML, CSS, JS, images, etc.
|
|
58
53
|
*
|
|
@@ -61,7 +56,7 @@ export declare class Router<T extends Record<string, any> = {}> {
|
|
|
61
56
|
*
|
|
62
57
|
* @example
|
|
63
58
|
* ```ts
|
|
64
|
-
* import { serveStatic } from "tezx/bun"; // or "tezx/node"
|
|
59
|
+
* import { serveStatic } from "tezx/bun"; // or "tezx/node"
|
|
65
60
|
*
|
|
66
61
|
* app.static(
|
|
67
62
|
* serveStatic("public", {
|
package/core/router.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { RadixRouter } from "../registry/RadixRouter.js";
|
|
2
1
|
import { sanitizePathSplitBasePath } from "../utils/low-level.js";
|
|
2
|
+
import { TezXError } from "./error.js";
|
|
3
3
|
export class Router {
|
|
4
4
|
env = {};
|
|
5
5
|
router;
|
|
6
6
|
route = [];
|
|
7
7
|
staticFile = Object.create(null);
|
|
8
8
|
basePath;
|
|
9
|
-
constructor({ basePath = "/", env = {}
|
|
10
|
-
this.router = routeRegistry;
|
|
9
|
+
constructor({ basePath = "/", env = {} } = {}) {
|
|
11
10
|
this.basePath = basePath;
|
|
12
11
|
this.env = { ...env };
|
|
13
12
|
this.get = this.get.bind(this);
|
|
@@ -29,7 +28,7 @@ export class Router {
|
|
|
29
28
|
if (headers) {
|
|
30
29
|
for (const key in headers) {
|
|
31
30
|
let value = headers?.[key];
|
|
32
|
-
ctx.
|
|
31
|
+
ctx.headers.set(key, value);
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
return ctx.sendFile(r.fileSource);
|
|
@@ -74,14 +73,14 @@ export class Router {
|
|
|
74
73
|
return this;
|
|
75
74
|
}
|
|
76
75
|
addRouter(path, router) {
|
|
77
|
-
return this.#
|
|
76
|
+
return this.#addRouterInstance(path, router);
|
|
78
77
|
}
|
|
79
78
|
group(prefix, callback) {
|
|
80
79
|
const router = new Router({
|
|
81
80
|
basePath: prefix,
|
|
82
81
|
});
|
|
83
82
|
callback(router);
|
|
84
|
-
this.#
|
|
83
|
+
this.#addRouterInstance("/", router);
|
|
85
84
|
return this;
|
|
86
85
|
}
|
|
87
86
|
use(...args) {
|
|
@@ -128,7 +127,7 @@ export class Router {
|
|
|
128
127
|
}
|
|
129
128
|
#addRoute(method, path, handlers) {
|
|
130
129
|
let pattern = `/${sanitizePathSplitBasePath(this.basePath, path).join("/")}`;
|
|
131
|
-
this.router
|
|
130
|
+
this.router?.addRoute(method, pattern, handlers);
|
|
132
131
|
this.route.push({
|
|
133
132
|
method: method,
|
|
134
133
|
pattern: pattern,
|
|
@@ -137,7 +136,7 @@ export class Router {
|
|
|
137
136
|
}
|
|
138
137
|
#registerRoute(method, path, ...args) {
|
|
139
138
|
if (args.length === 0) {
|
|
140
|
-
throw new
|
|
139
|
+
throw new TezXError("At least one handler is required.");
|
|
141
140
|
}
|
|
142
141
|
let middlewares = [];
|
|
143
142
|
let callback;
|
|
@@ -154,22 +153,17 @@ export class Router {
|
|
|
154
153
|
callback = args[0];
|
|
155
154
|
}
|
|
156
155
|
if (typeof callback !== "function") {
|
|
157
|
-
throw new
|
|
156
|
+
throw new TezXError("Route callback function is missing or invalid.");
|
|
158
157
|
}
|
|
159
158
|
if (!middlewares.every((middleware) => typeof middleware === "function")) {
|
|
160
|
-
throw new
|
|
159
|
+
throw new TezXError("Middleware must be a function or an array of functions.");
|
|
161
160
|
}
|
|
162
161
|
this.#addRoute(method, path, [...middlewares, callback]);
|
|
163
162
|
}
|
|
164
|
-
#
|
|
163
|
+
#addRouterInstance(path, router) {
|
|
165
164
|
this.env = { ...this.env, ...router.env };
|
|
166
|
-
if (this.router?.name &&
|
|
167
|
-
router.router?.name &&
|
|
168
|
-
this.router?.name !== router.router?.name) {
|
|
169
|
-
throw new Error(`Router name mismatch: expected "${this.router.name}", got "${router.router.name}"`);
|
|
170
|
-
}
|
|
171
165
|
if (!(router instanceof Router)) {
|
|
172
|
-
throw new
|
|
166
|
+
throw new TezXError("Router instance is required.");
|
|
173
167
|
}
|
|
174
168
|
router.route.forEach((r) => {
|
|
175
169
|
this.#addRoute(r?.method, `/${sanitizePathSplitBasePath(path, r?.pattern).join("/")}`, r?.handlers);
|
package/core/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Callback, ErrorHandler } from "../types/index.js";
|
|
1
|
+
import { Callback, ErrorHandler, RouteRegistry } from "../types/index.js";
|
|
2
2
|
import { Router, RouterConfig } from "./router.js";
|
|
3
3
|
export type TezXConfig = {
|
|
4
4
|
/**
|
|
@@ -20,6 +20,11 @@ export type TezXConfig = {
|
|
|
20
20
|
* @returns The transformed or resolved path used for routing (e.g., `/api/users`)
|
|
21
21
|
*/
|
|
22
22
|
onPathResolve?: (pathname: string) => string;
|
|
23
|
+
/**
|
|
24
|
+
* Custom route registry instance used internally to store routes.
|
|
25
|
+
* If not provided, the router will use the default CombineRouteRegistry.
|
|
26
|
+
*/
|
|
27
|
+
routeRegistry?: RouteRegistry;
|
|
23
28
|
/**
|
|
24
29
|
* Enables or disables debugging for the middleware.
|
|
25
30
|
* When set to `true`, detailed debug logs will be output,
|
|
@@ -31,7 +36,7 @@ export type TezXConfig = {
|
|
|
31
36
|
} & RouterConfig;
|
|
32
37
|
/**
|
|
33
38
|
* TezX is an ultra-fast, flexible request router and server handler.
|
|
34
|
-
* It supports plug-and-play for
|
|
39
|
+
* It supports plug-and-play for Bun, and Node runtimes.
|
|
35
40
|
*
|
|
36
41
|
* @template T - The environment object shared across requests.
|
|
37
42
|
*
|
|
@@ -45,6 +50,8 @@ export type TezXConfig = {
|
|
|
45
50
|
*/
|
|
46
51
|
export declare class TezX<T extends Record<string, any> = {}> extends Router<T> {
|
|
47
52
|
#private;
|
|
53
|
+
/** Internal route registry to hold all routes */
|
|
54
|
+
protected router?: RouteRegistry;
|
|
48
55
|
constructor({ basePath, env, debugMode, onPathResolve, routeRegistry, }?: TezXConfig);
|
|
49
56
|
/**
|
|
50
57
|
* Register a custom 404 (not found) handler.
|
|
@@ -85,27 +92,7 @@ export declare class TezX<T extends Record<string, any> = {}> extends Router<T>
|
|
|
85
92
|
* port: 3001,
|
|
86
93
|
* reusePort: true, // enables SO_REUSEPORT for clustering
|
|
87
94
|
* fetch: app.serve,
|
|
88
|
-
* websocket:
|
|
89
|
-
* open(ws) {
|
|
90
|
-
* console.log(ws.data);
|
|
91
|
-
* return ws.data?.open?.(ws);
|
|
92
|
-
* },
|
|
93
|
-
* message(ws, msg) {
|
|
94
|
-
* return ws.data?.message?.(ws, msg);
|
|
95
|
-
* },
|
|
96
|
-
* close(ws, code, reason) {
|
|
97
|
-
* return ws.data?.close?.(ws, { code, reason });
|
|
98
|
-
* },
|
|
99
|
-
* ping(ws, data) {
|
|
100
|
-
* return ws.data?.ping?.(ws, data);
|
|
101
|
-
* },
|
|
102
|
-
* pong(ws, data) {
|
|
103
|
-
* return ws.data?.pong?.(ws, data);
|
|
104
|
-
* },
|
|
105
|
-
* drain(ws) {
|
|
106
|
-
* return ws.data?.drain?.(ws);
|
|
107
|
-
* },
|
|
108
|
-
* },
|
|
95
|
+
* websocket: wsHandlers()
|
|
109
96
|
* });
|
|
110
97
|
* ```
|
|
111
98
|
*
|