@stacksjs/bun-router 0.0.15 → 0.0.17
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/dist/auth.d.ts +22 -8
- package/dist/chunk-1ahs68ys.js +18 -0
- package/dist/{chunk-j0e7z7hd.js → chunk-cgptvjdf.js} +153 -75
- package/dist/chunk-g3ybefhg.js +1923 -0
- package/dist/cli.js +13 -11
- package/dist/container/container.d.ts +1 -0
- package/dist/container/contextual-binding.d.ts +0 -1
- package/dist/container/decorators.d.ts +2 -5
- package/dist/container/index.d.ts +16 -0
- package/dist/container/index.js +322 -0
- package/dist/file-serving/static-files.d.ts +16 -0
- package/dist/index.d.ts +17 -1
- package/dist/index.js +1086 -1270
- package/dist/middleware/cors.d.ts +9 -0
- package/dist/middleware/csrf.d.ts +7 -0
- package/dist/middleware/ddos_protection.d.ts +16 -1
- package/dist/middleware/rate_limit.d.ts +7 -1
- package/dist/middleware/request_tracer.d.ts +15 -9
- package/dist/middleware/response_cache.d.ts +2 -1
- package/dist/request/context.d.ts +12 -1
- package/dist/request/macros.d.ts +40 -2
- package/dist/response/macros.d.ts +3 -3
- package/dist/router/fluent-routing.d.ts +50 -13
- package/dist/router/handler-resolver.d.ts +8 -0
- package/dist/router/index.d.ts +2 -0
- package/dist/router/route-compiler.d.ts +8 -10
- package/dist/router/route-trie.d.ts +7 -1
- package/dist/router/router.d.ts +13 -2
- package/dist/router/validation-integration.d.ts +10 -4
- package/dist/types/middleware-types.d.ts +1 -1
- package/dist/types/route-inference.d.ts +9 -2
- package/dist/types.d.ts +62 -9
- package/dist/utils.d.ts +1 -1
- package/package.json +11 -1
- package/src/auth.ts +32 -12
- package/src/container/container.ts +58 -12
- package/src/container/contextual-binding.ts +4 -1
- package/src/container/decorators.ts +16 -7
- package/src/container/index.ts +49 -0
- package/src/errors/circuit-breaker.ts +3 -2
- package/src/errors/graceful-degradation.ts +2 -0
- package/src/file-serving/static-files.ts +144 -7
- package/src/index.ts +53 -1
- package/src/middleware/cors.ts +76 -8
- package/src/middleware/csrf.ts +80 -22
- package/src/middleware/ddos_protection.ts +27 -3
- package/src/middleware/input_validation.ts +10 -6
- package/src/middleware/performance_alerting.ts +2 -0
- package/src/middleware/performance_monitor.ts +2 -0
- package/src/middleware/rate_limit.ts +7 -1
- package/src/middleware/request_signing.ts +2 -0
- package/src/middleware/request_tracer.ts +34 -17
- package/src/middleware/response_cache.ts +48 -13
- package/src/middleware/session.ts +3 -3
- package/src/observability/metrics.ts +1 -1
- package/src/request/context.ts +50 -3
- package/src/request/enhanced-request.ts +20 -1
- package/src/request/macros.ts +183 -49
- package/src/response/macros.ts +3 -3
- package/src/router/file-based-routing.ts +17 -10
- package/src/router/fluent-routing.ts +117 -48
- package/src/router/group-organization.ts +17 -1
- package/src/router/handler-resolver.ts +36 -0
- package/src/router/http-methods.ts +33 -3
- package/src/router/index.ts +6 -0
- package/src/router/middleware.ts +3 -0
- package/src/router/optimized-route-matching.ts +58 -9
- package/src/router/route-compiler.ts +62 -73
- package/src/router/route-matching.ts +19 -0
- package/src/router/route-trie.ts +34 -48
- package/src/router/router.ts +72 -60
- package/src/router/server.ts +361 -139
- package/src/router/validation-integration.ts +11 -5
- package/src/testing/auth-testing.ts +6 -4
- package/src/types/middleware-types.ts +7 -4
- package/src/types/route-inference.ts +29 -14
- package/src/types.ts +63 -7
- package/src/url.ts +7 -2
- package/src/utils.ts +107 -39
- package/src/validation/validator.ts +7 -4
- package/src/websocket/clustering.ts +8 -1
- package/dist/router/fluent-router.d.ts +0 -315
- package/src/router/fluent-router.ts +0 -927
package/dist/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EnhancedRequest, JwtPayload } from './types';
|
|
1
|
+
import type { EnhancedRequest, JwtHeader, JwtPayload } from './types';
|
|
2
2
|
export interface JwtVerifyOptions {
|
|
3
3
|
issuer?: string;
|
|
4
4
|
audience?: string;
|
|
@@ -19,9 +19,23 @@ export interface JwtSignOptions {
|
|
|
19
19
|
keyid?: string;
|
|
20
20
|
jwtid?: string;
|
|
21
21
|
noTimestamp?: boolean;
|
|
22
|
-
header?:
|
|
22
|
+
header?: Partial<JwtHeader>;
|
|
23
23
|
encoding?: string;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Token endpoint response for the OAuth2 authorization-code flow
|
|
27
|
+
* (RFC 6749 §5.1). Providers may include extra fields — they're preserved
|
|
28
|
+
* under the index signature.
|
|
29
|
+
*/
|
|
30
|
+
export interface OAuth2TokenResponse {
|
|
31
|
+
access_token: string;
|
|
32
|
+
token_type: string;
|
|
33
|
+
expires_in?: number;
|
|
34
|
+
refresh_token?: string;
|
|
35
|
+
scope?: string;
|
|
36
|
+
id_token?: string;
|
|
37
|
+
[key: string]: string | number | undefined;
|
|
38
|
+
}
|
|
25
39
|
export interface ApiKeyOptions {
|
|
26
40
|
source?: 'header' | 'query' | 'cookie';
|
|
27
41
|
keyName?: string;
|
|
@@ -46,17 +60,17 @@ export declare class JWT {
|
|
|
46
60
|
/**
|
|
47
61
|
* Sign payload into a JWT token
|
|
48
62
|
*/
|
|
49
|
-
sign(payload:
|
|
63
|
+
sign(payload: JwtPayload, options?: JwtSignOptions): string;
|
|
50
64
|
/**
|
|
51
65
|
* Verify a JWT token and return the decoded payload
|
|
52
66
|
*/
|
|
53
|
-
verify(token: string, options?: JwtVerifyOptions):
|
|
67
|
+
verify(token: string, options?: JwtVerifyOptions): JwtPayload | null;
|
|
54
68
|
/**
|
|
55
69
|
* Decode a JWT token without verifying the signature
|
|
56
70
|
*/
|
|
57
71
|
decode(token: string): {
|
|
58
|
-
header:
|
|
59
|
-
payload:
|
|
72
|
+
header: JwtHeader;
|
|
73
|
+
payload: JwtPayload;
|
|
60
74
|
} | null;
|
|
61
75
|
/**
|
|
62
76
|
* Simple signature creation - Note: This is NOT secure for production
|
|
@@ -117,11 +131,11 @@ export declare class OAuth2Helper {
|
|
|
117
131
|
/**
|
|
118
132
|
* Exchange authorization code for access token
|
|
119
133
|
*/
|
|
120
|
-
exchangeCodeForToken(code: string): Promise<
|
|
134
|
+
exchangeCodeForToken(code: string): Promise<OAuth2TokenResponse>;
|
|
121
135
|
/**
|
|
122
136
|
* Refresh an access token using a refresh token
|
|
123
137
|
*/
|
|
124
|
-
refreshToken(refreshToken: string): Promise<
|
|
138
|
+
refreshToken(refreshToken: string): Promise<OAuth2TokenResponse>;
|
|
125
139
|
}
|
|
126
140
|
/**
|
|
127
141
|
* Authentication helpers namespace
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __require = import.meta.require;
|
|
17
|
+
|
|
18
|
+
export { __export, __require };
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
var __require = import.meta.require;
|
|
2
|
+
import {
|
|
3
|
+
__require
|
|
4
|
+
} from "./chunk-1ahs68ys.js";
|
|
17
5
|
|
|
18
6
|
// src/config.ts
|
|
19
7
|
import process19 from "process";
|
|
@@ -40,7 +28,7 @@ import process2 from "process";
|
|
|
40
28
|
import { existsSync as existsSync22, mkdirSync, readdirSync, writeFileSync } from "fs";
|
|
41
29
|
import { dirname, resolve } from "path";
|
|
42
30
|
import process from "process";
|
|
43
|
-
import { Buffer } from "buffer";
|
|
31
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
44
32
|
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
45
33
|
import { closeSync, createReadStream, createWriteStream, existsSync as existsSync222, fsyncSync, openSync, writeFileSync as writeFileSync2 } from "fs";
|
|
46
34
|
import { access, constants, mkdir, readdir, rename, stat, unlink, writeFile } from "fs/promises";
|
|
@@ -50,7 +38,7 @@ import { pipeline } from "stream/promises";
|
|
|
50
38
|
import { createGzip } from "zlib";
|
|
51
39
|
import process4 from "process";
|
|
52
40
|
import process3 from "process";
|
|
53
|
-
import { Buffer as
|
|
41
|
+
import { Buffer as Buffer22 } from "buffer";
|
|
54
42
|
import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes2 } from "crypto";
|
|
55
43
|
import { closeSync as closeSync2, createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync4, fsyncSync as fsyncSync2, openSync as openSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
56
44
|
import { access as access2, constants as constants2, mkdir as mkdir2, readdir as readdir2, rename as rename2, stat as stat2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
@@ -323,23 +311,23 @@ class PerformanceMonitor {
|
|
|
323
311
|
}
|
|
324
312
|
var globalCache = new ConfigCache;
|
|
325
313
|
var globalPerformanceMonitor = new PerformanceMonitor;
|
|
326
|
-
var
|
|
327
|
-
var
|
|
328
|
-
function
|
|
329
|
-
this[name] =
|
|
314
|
+
var __defProp = Object.defineProperty;
|
|
315
|
+
var __returnValue = (v) => v;
|
|
316
|
+
function __exportSetter(name, newValue) {
|
|
317
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
330
318
|
}
|
|
331
|
-
var
|
|
319
|
+
var __export = (target, all) => {
|
|
332
320
|
for (var name in all)
|
|
333
|
-
|
|
321
|
+
__defProp(target, name, {
|
|
334
322
|
get: all[name],
|
|
335
323
|
enumerable: true,
|
|
336
324
|
configurable: true,
|
|
337
|
-
set:
|
|
325
|
+
set: __exportSetter.bind(all, name)
|
|
338
326
|
});
|
|
339
327
|
};
|
|
340
328
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
341
329
|
var exports_dist = {};
|
|
342
|
-
|
|
330
|
+
__export(exports_dist, {
|
|
343
331
|
withErrorRecovery: () => withErrorRecovery,
|
|
344
332
|
tryLoadConfig: () => tryLoadConfig3,
|
|
345
333
|
loadConfigWithResult: () => loadConfigWithResult,
|
|
@@ -1028,7 +1016,7 @@ class Logger {
|
|
|
1028
1016
|
}
|
|
1029
1017
|
if (cancelled)
|
|
1030
1018
|
throw new Error("Operation cancelled: Logger was destroyed");
|
|
1031
|
-
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted :
|
|
1019
|
+
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted : Buffer2.from(data);
|
|
1032
1020
|
try {
|
|
1033
1021
|
if (!existsSync222(this.currentLogFile)) {
|
|
1034
1022
|
await writeFile(this.currentLogFile, "", { mode: 420 });
|
|
@@ -1196,13 +1184,13 @@ class Logger {
|
|
|
1196
1184
|
const { key } = this.getCurrentKey();
|
|
1197
1185
|
const iv = randomBytes(16);
|
|
1198
1186
|
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
1199
|
-
const encrypted =
|
|
1187
|
+
const encrypted = Buffer2.concat([
|
|
1200
1188
|
cipher.update(data, "utf8"),
|
|
1201
1189
|
cipher.final()
|
|
1202
1190
|
]);
|
|
1203
1191
|
const authTag = cipher.getAuthTag();
|
|
1204
1192
|
return {
|
|
1205
|
-
encrypted:
|
|
1193
|
+
encrypted: Buffer2.concat([iv, encrypted, authTag]),
|
|
1206
1194
|
iv
|
|
1207
1195
|
};
|
|
1208
1196
|
}
|
|
@@ -1211,7 +1199,7 @@ class Logger {
|
|
|
1211
1199
|
const gzip = createGzip();
|
|
1212
1200
|
const chunks = [];
|
|
1213
1201
|
gzip.on("data", (chunk2) => chunks.push(chunk2));
|
|
1214
|
-
gzip.on("end", () => resolve32(
|
|
1202
|
+
gzip.on("end", () => resolve32(Buffer2.from(Buffer2.concat(chunks))));
|
|
1215
1203
|
gzip.on("error", reject);
|
|
1216
1204
|
gzip.write(data);
|
|
1217
1205
|
gzip.end();
|
|
@@ -1700,13 +1688,13 @@ class Logger {
|
|
|
1700
1688
|
throw new Error("No valid encryption key available");
|
|
1701
1689
|
const key = this.keys.get(this.currentKeyId);
|
|
1702
1690
|
try {
|
|
1703
|
-
const encryptedData =
|
|
1691
|
+
const encryptedData = Buffer2.isBuffer(data) ? data : Buffer2.from(data, "base64");
|
|
1704
1692
|
const iv = encryptedData.slice(0, 16);
|
|
1705
1693
|
const authTag = encryptedData.slice(-16);
|
|
1706
1694
|
const ciphertext = encryptedData.slice(16, -16);
|
|
1707
1695
|
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
1708
1696
|
decipher.setAuthTag(authTag);
|
|
1709
|
-
const decrypted =
|
|
1697
|
+
const decrypted = Buffer2.concat([
|
|
1710
1698
|
decipher.update(ciphertext),
|
|
1711
1699
|
decipher.final()
|
|
1712
1700
|
]);
|
|
@@ -2603,7 +2591,7 @@ class Logger2 {
|
|
|
2603
2591
|
}
|
|
2604
2592
|
if (cancelled)
|
|
2605
2593
|
throw new Error("Operation cancelled: Logger was destroyed");
|
|
2606
|
-
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted :
|
|
2594
|
+
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted : Buffer22.from(data);
|
|
2607
2595
|
try {
|
|
2608
2596
|
if (!existsSync4(this.currentLogFile)) {
|
|
2609
2597
|
await writeFile2(this.currentLogFile, "", { mode: 420 });
|
|
@@ -2773,12 +2761,12 @@ class Logger2 {
|
|
|
2773
2761
|
const { key } = this.getCurrentKey();
|
|
2774
2762
|
const iv = randomBytes2(16);
|
|
2775
2763
|
const cipher = createCipheriv2("aes-256-gcm", key, iv);
|
|
2776
|
-
const input =
|
|
2764
|
+
const input = Buffer22.isBuffer(data) ? data : Buffer22.from(data, "utf8");
|
|
2777
2765
|
const part1 = cipher.update(input);
|
|
2778
2766
|
const part2 = cipher.final();
|
|
2779
2767
|
const totalCipherLen = part1.length + part2.length;
|
|
2780
2768
|
const authTag = cipher.getAuthTag();
|
|
2781
|
-
const out =
|
|
2769
|
+
const out = Buffer22.allocUnsafe(16 + totalCipherLen + 16);
|
|
2782
2770
|
iv.copy(out, 0);
|
|
2783
2771
|
part1.copy(out, 16);
|
|
2784
2772
|
part2.copy(out, 16 + part1.length);
|
|
@@ -2793,7 +2781,7 @@ class Logger2 {
|
|
|
2793
2781
|
const gzip = createGzip2();
|
|
2794
2782
|
const chunks = [];
|
|
2795
2783
|
gzip.on("data", (chunk2) => chunks.push(chunk2));
|
|
2796
|
-
gzip.on("end", () => resolve52(
|
|
2784
|
+
gzip.on("end", () => resolve52(Buffer22.from(Buffer22.concat(chunks))));
|
|
2797
2785
|
gzip.on("error", reject);
|
|
2798
2786
|
gzip.write(data);
|
|
2799
2787
|
gzip.end();
|
|
@@ -3388,7 +3376,7 @@ class Logger2 {
|
|
|
3388
3376
|
throw new Error("No valid encryption key available");
|
|
3389
3377
|
const key = this.keys.get(this.currentKeyId);
|
|
3390
3378
|
try {
|
|
3391
|
-
const encryptedData =
|
|
3379
|
+
const encryptedData = Buffer22.isBuffer(data) ? data : Buffer22.from(data, "base64");
|
|
3392
3380
|
const iv = encryptedData.subarray(0, 16);
|
|
3393
3381
|
const authTag = encryptedData.subarray(encryptedData.length - 16);
|
|
3394
3382
|
const ciphertext = encryptedData.subarray(16, encryptedData.length - 16);
|
|
@@ -3397,7 +3385,7 @@ class Logger2 {
|
|
|
3397
3385
|
const d1 = decipher.update(ciphertext);
|
|
3398
3386
|
const d2 = decipher.final();
|
|
3399
3387
|
const totalLen = d1.length + d2.length;
|
|
3400
|
-
const out =
|
|
3388
|
+
const out = Buffer22.allocUnsafe(totalLen);
|
|
3401
3389
|
d1.copy(out, 0);
|
|
3402
3390
|
d2.copy(out, d1.length);
|
|
3403
3391
|
return out.toString("utf8");
|
|
@@ -7043,6 +7031,7 @@ class Logger3 {
|
|
|
7043
7031
|
}
|
|
7044
7032
|
}
|
|
7045
7033
|
var logger3 = new Logger3("stacks");
|
|
7034
|
+
|
|
7046
7035
|
class BunfigError2 extends Error {
|
|
7047
7036
|
timestamp;
|
|
7048
7037
|
context;
|
|
@@ -9137,26 +9126,55 @@ var DEFAULT_CORS_HEADERS = {
|
|
|
9137
9126
|
"Access-Control-Allow-Origin": "*",
|
|
9138
9127
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
9139
9128
|
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, Accept, Origin",
|
|
9140
|
-
"Access-Control-Max-Age": "86400"
|
|
9141
|
-
"Access-Control-Allow-Credentials": "true"
|
|
9129
|
+
"Access-Control-Max-Age": "86400"
|
|
9142
9130
|
};
|
|
9143
9131
|
|
|
9144
9132
|
class Cors {
|
|
9145
|
-
|
|
9133
|
+
resolveAllowOrigin(configuredOrigin, credentials, requestOrigin) {
|
|
9134
|
+
if (Array.isArray(configuredOrigin)) {
|
|
9135
|
+
if (requestOrigin && configuredOrigin.includes(requestOrigin)) {
|
|
9136
|
+
return { value: requestOrigin, vary: true };
|
|
9137
|
+
}
|
|
9138
|
+
return { value: null, vary: true };
|
|
9139
|
+
}
|
|
9140
|
+
const origin = configuredOrigin || "*";
|
|
9141
|
+
if (origin === "*") {
|
|
9142
|
+
if (credentials) {
|
|
9143
|
+
if (requestOrigin) {
|
|
9144
|
+
return { value: requestOrigin, vary: true };
|
|
9145
|
+
}
|
|
9146
|
+
return { value: null, vary: true };
|
|
9147
|
+
}
|
|
9148
|
+
return { value: "*", vary: false };
|
|
9149
|
+
}
|
|
9150
|
+
return { value: origin, vary: false };
|
|
9151
|
+
}
|
|
9152
|
+
getCorsHeaders(req) {
|
|
9146
9153
|
const corsConfig = config5.server?.cors;
|
|
9154
|
+
const requestOrigin = req.headers.get("origin");
|
|
9147
9155
|
if (corsConfig && corsConfig.enabled) {
|
|
9148
|
-
|
|
9149
|
-
|
|
9156
|
+
const credentials = Boolean(corsConfig.credentials);
|
|
9157
|
+
const { value: allowOrigin, vary } = this.resolveAllowOrigin(corsConfig.origin, credentials, requestOrigin);
|
|
9158
|
+
const headers = {
|
|
9150
9159
|
"Access-Control-Allow-Methods": Array.isArray(corsConfig.methods) ? corsConfig.methods.join(", ") : DEFAULT_CORS_HEADERS["Access-Control-Allow-Methods"],
|
|
9151
9160
|
"Access-Control-Allow-Headers": Array.isArray(corsConfig.allowedHeaders) ? corsConfig.allowedHeaders.join(", ") : DEFAULT_CORS_HEADERS["Access-Control-Allow-Headers"],
|
|
9152
|
-
"Access-Control-Max-Age": String(corsConfig.maxAge || 86400)
|
|
9153
|
-
"Access-Control-Allow-Credentials": String(corsConfig.credentials || false)
|
|
9161
|
+
"Access-Control-Max-Age": String(corsConfig.maxAge || 86400)
|
|
9154
9162
|
};
|
|
9163
|
+
if (allowOrigin !== null) {
|
|
9164
|
+
headers["Access-Control-Allow-Origin"] = allowOrigin;
|
|
9165
|
+
}
|
|
9166
|
+
if (credentials && allowOrigin !== null && allowOrigin !== "*") {
|
|
9167
|
+
headers["Access-Control-Allow-Credentials"] = "true";
|
|
9168
|
+
}
|
|
9169
|
+
if (vary) {
|
|
9170
|
+
headers.Vary = "Origin";
|
|
9171
|
+
}
|
|
9172
|
+
return headers;
|
|
9155
9173
|
}
|
|
9156
9174
|
return { ...DEFAULT_CORS_HEADERS };
|
|
9157
9175
|
}
|
|
9158
9176
|
async handle(req, next) {
|
|
9159
|
-
const corsHeaders = this.getCorsHeaders();
|
|
9177
|
+
const corsHeaders = this.getCorsHeaders(req);
|
|
9160
9178
|
if (req.method === "OPTIONS") {
|
|
9161
9179
|
return new Response(null, { status: 204, headers: corsHeaders });
|
|
9162
9180
|
}
|
|
@@ -9194,15 +9212,14 @@ class Cors {
|
|
|
9194
9212
|
}
|
|
9195
9213
|
|
|
9196
9214
|
// src/middleware/csrf.ts
|
|
9197
|
-
import { createHash, randomBytes as randomBytes4 } from "crypto";
|
|
9215
|
+
import { createHash, randomBytes as randomBytes4, timingSafeEqual } from "crypto";
|
|
9216
|
+
var MAX_TOKENS = 1e4;
|
|
9217
|
+
var TOKEN_TTL_MS = 2 * 60 * 60 * 1000;
|
|
9218
|
+
|
|
9198
9219
|
class Csrf {
|
|
9199
9220
|
static tokens = new Map;
|
|
9200
9221
|
async handle(req, next) {
|
|
9201
|
-
const csrfConfig = config5.server?.security?.csrf
|
|
9202
|
-
if (["GET", "HEAD", "OPTIONS"].includes(req.method)) {
|
|
9203
|
-
const response2 = await next();
|
|
9204
|
-
return response2 || new Response("Not Found", { status: 404 });
|
|
9205
|
-
}
|
|
9222
|
+
const csrfConfig = config5.server?.security?.csrf ?? {};
|
|
9206
9223
|
const method = req.method.toUpperCase();
|
|
9207
9224
|
const ignoredMethods = ["GET", "HEAD", "OPTIONS", ...csrfConfig?.ignoreMethods || []];
|
|
9208
9225
|
if (ignoredMethods.includes(method)) {
|
|
@@ -9226,17 +9243,23 @@ class Csrf {
|
|
|
9226
9243
|
}
|
|
9227
9244
|
const newHeaders = new Headers(response2.headers);
|
|
9228
9245
|
newHeaders.append("Set-Cookie", cookieOptions.join("; "));
|
|
9229
|
-
Csrf.
|
|
9246
|
+
Csrf.storeToken(token2);
|
|
9230
9247
|
return new Response(response2.body, {
|
|
9231
9248
|
status: response2.status,
|
|
9232
9249
|
statusText: response2.statusText,
|
|
9233
9250
|
headers: newHeaders
|
|
9234
9251
|
});
|
|
9235
9252
|
}
|
|
9236
|
-
const
|
|
9253
|
+
const authHeader = req.headers.get("authorization") ?? req.headers.get("Authorization");
|
|
9254
|
+
if (authHeader && /^Bearer\s+\S/i.test(authHeader)) {
|
|
9255
|
+
const response2 = await next();
|
|
9256
|
+
return response2 || new Response("Not Found", { status: 404 });
|
|
9257
|
+
}
|
|
9258
|
+
const rawToken = req.headers.get("X-CSRF-TOKEN") || req.jsonBody?.csrf_token || req.jsonBody?._token || null;
|
|
9259
|
+
const token = typeof rawToken === "string" ? rawToken : null;
|
|
9237
9260
|
const cookies = this.parseCookies(req);
|
|
9238
9261
|
const cookieToken = cookies[csrfConfig?.cookie?.name || "csrf-token"];
|
|
9239
|
-
if (!token || !cookieToken || token
|
|
9262
|
+
if (!token || !cookieToken || !Csrf.safeCompare(token, cookieToken) || !Csrf.isTokenValid(token)) {
|
|
9240
9263
|
return new Response(JSON.stringify({ error: "CSRF token validation failed" }), {
|
|
9241
9264
|
status: 403,
|
|
9242
9265
|
headers: { "Content-Type": "application/json" }
|
|
@@ -9245,6 +9268,34 @@ class Csrf {
|
|
|
9245
9268
|
const response = await next();
|
|
9246
9269
|
return response || new Response("Not Found", { status: 404 });
|
|
9247
9270
|
}
|
|
9271
|
+
static safeCompare(a, b) {
|
|
9272
|
+
const bufA = Buffer.from(a);
|
|
9273
|
+
const bufB = Buffer.from(b);
|
|
9274
|
+
if (bufA.length !== bufB.length) {
|
|
9275
|
+
return false;
|
|
9276
|
+
}
|
|
9277
|
+
return timingSafeEqual(bufA, bufB);
|
|
9278
|
+
}
|
|
9279
|
+
static storeToken(token) {
|
|
9280
|
+
if (Csrf.tokens.size >= MAX_TOKENS) {
|
|
9281
|
+
const oldest = Csrf.tokens.keys().next().value;
|
|
9282
|
+
if (oldest !== undefined) {
|
|
9283
|
+
Csrf.tokens.delete(oldest);
|
|
9284
|
+
}
|
|
9285
|
+
}
|
|
9286
|
+
Csrf.tokens.set(token, Date.now());
|
|
9287
|
+
}
|
|
9288
|
+
static isTokenValid(token) {
|
|
9289
|
+
const issuedAt = Csrf.tokens.get(token);
|
|
9290
|
+
if (issuedAt === undefined) {
|
|
9291
|
+
return false;
|
|
9292
|
+
}
|
|
9293
|
+
if (Date.now() - issuedAt > TOKEN_TTL_MS) {
|
|
9294
|
+
Csrf.tokens.delete(token);
|
|
9295
|
+
return false;
|
|
9296
|
+
}
|
|
9297
|
+
return true;
|
|
9298
|
+
}
|
|
9248
9299
|
generateToken(secret) {
|
|
9249
9300
|
const randomString = randomBytes4(16).toString("hex");
|
|
9250
9301
|
return createHash("sha256").update(`${randomString}${secret}`).digest("hex");
|
|
@@ -9253,11 +9304,17 @@ class Csrf {
|
|
|
9253
9304
|
const cookieHeader = req.headers.get("cookie");
|
|
9254
9305
|
if (!cookieHeader)
|
|
9255
9306
|
return {};
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9307
|
+
const cookies = {};
|
|
9308
|
+
for (const cookie of cookieHeader.split(";")) {
|
|
9309
|
+
const eqIndex = cookie.indexOf("=");
|
|
9310
|
+
if (eqIndex === -1)
|
|
9311
|
+
continue;
|
|
9312
|
+
const name = cookie.slice(0, eqIndex).trim();
|
|
9313
|
+
if (!name)
|
|
9314
|
+
continue;
|
|
9315
|
+
cookies[name] = cookie.slice(eqIndex + 1).trim();
|
|
9316
|
+
}
|
|
9317
|
+
return cookies;
|
|
9261
9318
|
}
|
|
9262
9319
|
}
|
|
9263
9320
|
|
|
@@ -9312,18 +9369,18 @@ import process72 from "process";
|
|
|
9312
9369
|
import { existsSync as existsSync62, readdirSync as readdirSync22 } from "fs";
|
|
9313
9370
|
import { extname as extname2, resolve as resolve62 } from "path";
|
|
9314
9371
|
import process82 from "process";
|
|
9315
|
-
var
|
|
9316
|
-
var
|
|
9317
|
-
function
|
|
9318
|
-
this[name] =
|
|
9372
|
+
var __defProp2 = Object.defineProperty;
|
|
9373
|
+
var __returnValue2 = (v) => v;
|
|
9374
|
+
function __exportSetter2(name, newValue) {
|
|
9375
|
+
this[name] = __returnValue2.bind(null, newValue);
|
|
9319
9376
|
}
|
|
9320
|
-
var
|
|
9377
|
+
var __export2 = (target, all) => {
|
|
9321
9378
|
for (var name in all)
|
|
9322
|
-
|
|
9379
|
+
__defProp2(target, name, {
|
|
9323
9380
|
get: all[name],
|
|
9324
9381
|
enumerable: true,
|
|
9325
9382
|
configurable: true,
|
|
9326
|
-
set:
|
|
9383
|
+
set: __exportSetter2.bind(all, name)
|
|
9327
9384
|
});
|
|
9328
9385
|
};
|
|
9329
9386
|
var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -12874,7 +12931,7 @@ export type ConfigOf = Config
|
|
|
12874
12931
|
}
|
|
12875
12932
|
var init_plugin = () => {};
|
|
12876
12933
|
var exports_src = {};
|
|
12877
|
-
|
|
12934
|
+
__export2(exports_src, {
|
|
12878
12935
|
withErrorRecovery: () => withErrorRecovery3,
|
|
12879
12936
|
tryLoadConfig: () => tryLoadConfig4,
|
|
12880
12937
|
loadConfigWithResult: () => loadConfigWithResult2,
|
|
@@ -13742,6 +13799,7 @@ class InMemoryNonceStore {
|
|
|
13742
13799
|
cleanupInterval = null;
|
|
13743
13800
|
constructor(cleanupIntervalMs = 60000) {
|
|
13744
13801
|
this.cleanupInterval = setInterval(() => this.cleanup(), cleanupIntervalMs);
|
|
13802
|
+
this.cleanupInterval.unref?.();
|
|
13745
13803
|
}
|
|
13746
13804
|
has(nonce) {
|
|
13747
13805
|
const expiry = this.nonces.get(nonce);
|
|
@@ -14747,6 +14805,7 @@ class DDoSProtection {
|
|
|
14747
14805
|
}
|
|
14748
14806
|
}
|
|
14749
14807
|
}, this.options.windowSize / 2);
|
|
14808
|
+
this.cleanupInterval.unref?.();
|
|
14750
14809
|
}
|
|
14751
14810
|
isWhitelisted(ip) {
|
|
14752
14811
|
return this.options.whitelistedIPs?.includes(ip) ?? false;
|
|
@@ -14757,6 +14816,15 @@ class DDoSProtection {
|
|
|
14757
14816
|
getRateLimitInfo(key, now) {
|
|
14758
14817
|
let info = this.requestStore.get(key);
|
|
14759
14818
|
if (!info) {
|
|
14819
|
+
if (this.requestStore.size >= 1e5) {
|
|
14820
|
+
let toEvict = Math.ceil(this.requestStore.size / 10);
|
|
14821
|
+
for (const oldestKey of this.requestStore.keys()) {
|
|
14822
|
+
this.requestStore.delete(oldestKey);
|
|
14823
|
+
if (--toEvict <= 0) {
|
|
14824
|
+
break;
|
|
14825
|
+
}
|
|
14826
|
+
}
|
|
14827
|
+
}
|
|
14760
14828
|
info = {
|
|
14761
14829
|
count: 0,
|
|
14762
14830
|
firstRequest: now,
|
|
@@ -15344,6 +15412,9 @@ class Helmet {
|
|
|
15344
15412
|
}
|
|
15345
15413
|
|
|
15346
15414
|
// src/middleware/input_validation.ts
|
|
15415
|
+
var EMAIL_REGEX = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/;
|
|
15416
|
+
var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
15417
|
+
|
|
15347
15418
|
class InputValidation {
|
|
15348
15419
|
options;
|
|
15349
15420
|
constructor(options = {}) {
|
|
@@ -15440,8 +15511,7 @@ class InputValidation {
|
|
|
15440
15511
|
}
|
|
15441
15512
|
break;
|
|
15442
15513
|
case "email": {
|
|
15443
|
-
|
|
15444
|
-
if (typeof value === "string" && !emailRegex.test(value)) {
|
|
15514
|
+
if (typeof value === "string" && !EMAIL_REGEX.test(value)) {
|
|
15445
15515
|
errors.push({
|
|
15446
15516
|
field: fieldPath,
|
|
15447
15517
|
message: "Must be a valid email address",
|
|
@@ -15464,8 +15534,7 @@ class InputValidation {
|
|
|
15464
15534
|
}
|
|
15465
15535
|
break;
|
|
15466
15536
|
case "uuid": {
|
|
15467
|
-
|
|
15468
|
-
if (typeof value === "string" && !uuidRegex.test(value)) {
|
|
15537
|
+
if (typeof value === "string" && !UUID_REGEX.test(value)) {
|
|
15469
15538
|
errors.push({
|
|
15470
15539
|
field: fieldPath,
|
|
15471
15540
|
message: "Must be a valid UUID",
|
|
@@ -15519,7 +15588,7 @@ class InputValidation {
|
|
|
15519
15588
|
rule: "enum"
|
|
15520
15589
|
});
|
|
15521
15590
|
}
|
|
15522
|
-
if (rule.pattern && typeof value === "string" && !rule.pattern.test(value)) {
|
|
15591
|
+
if (rule.type !== "regex" && rule.pattern && typeof value === "string" && !rule.pattern.test(value)) {
|
|
15523
15592
|
errors.push({
|
|
15524
15593
|
field: fieldPath,
|
|
15525
15594
|
message: "Does not match required pattern",
|
|
@@ -15802,6 +15871,7 @@ class PerformanceAlerting {
|
|
|
15802
15871
|
this.cleanupOldNotifications();
|
|
15803
15872
|
this.autoResolveAlerts();
|
|
15804
15873
|
}, 1e4);
|
|
15874
|
+
this.checkInterval.unref?.();
|
|
15805
15875
|
}
|
|
15806
15876
|
evaluateRules() {
|
|
15807
15877
|
const now = Date.now();
|
|
@@ -16686,6 +16756,7 @@ class PerformanceMonitor5 {
|
|
|
16686
16756
|
this.cleanupInterval = setInterval(() => {
|
|
16687
16757
|
this.cleanupOldMetrics();
|
|
16688
16758
|
}, 60000);
|
|
16759
|
+
this.cleanupInterval.unref?.();
|
|
16689
16760
|
}
|
|
16690
16761
|
cleanupOldMetrics() {
|
|
16691
16762
|
const now = Date.now();
|
|
@@ -17188,6 +17259,7 @@ class RequestTracer {
|
|
|
17188
17259
|
console.warn("Error exporting spans:", error);
|
|
17189
17260
|
}
|
|
17190
17261
|
}, 100);
|
|
17262
|
+
this.exportInterval.unref?.();
|
|
17191
17263
|
}
|
|
17192
17264
|
shouldSample() {
|
|
17193
17265
|
return Math.random() < (this.options.sampleRate || 1);
|
|
@@ -17308,11 +17380,17 @@ class RequestTracer {
|
|
|
17308
17380
|
}
|
|
17309
17381
|
}
|
|
17310
17382
|
async exportSpans() {
|
|
17311
|
-
|
|
17383
|
+
if (this.spans.size > 0) {
|
|
17384
|
+
const cutoff = performance.now() - 60000;
|
|
17385
|
+
for (const [spanId, span] of this.spans) {
|
|
17386
|
+
if (span.endTime === undefined && span.startTime < cutoff) {
|
|
17387
|
+
this.spans.delete(spanId);
|
|
17388
|
+
}
|
|
17389
|
+
}
|
|
17390
|
+
}
|
|
17312
17391
|
if (this.exportQueue.length === 0) {
|
|
17313
17392
|
const finishedSpans = Array.from(this.spans.values()).filter((span) => span.endTime !== undefined);
|
|
17314
17393
|
if (finishedSpans.length > 0) {
|
|
17315
|
-
console.warn(`[RequestTracer] Found ${finishedSpans.length} finished spans to export`);
|
|
17316
17394
|
this.exportQueue.push(...finishedSpans);
|
|
17317
17395
|
for (const span of finishedSpans) {
|
|
17318
17396
|
this.spans.delete(span.spanId);
|
|
@@ -18050,4 +18128,4 @@ var middleware = new Proxy({}, {
|
|
|
18050
18128
|
}
|
|
18051
18129
|
});
|
|
18052
18130
|
|
|
18053
|
-
export {
|
|
18131
|
+
export { defaultConfig4 as defaultConfig, getConfig, config5 as config, extractBasicAuth, extractBearerToken, extractApiKey, basicAuth, bearerAuth, jwtAuth, apiKeyAuth, oauth2Auth, Cors, Csrf, JsonBody, RateLimit, rateLimit, RequestId, SignatureError, InMemoryNonceStore, RequestSigning, requestSigning, signRequest, verifySignature, MemorySessionStore, FileSessionStore, RedisSessionStore, DatabaseSessionStore, createSessionStore, SessionManager, generateSessionId, Session, ContentSecurityPolicy, contentSecurityPolicy, DDoSProtection, ddosProtection, FileSecurity, FileUpload, fileUpload, singleFileUpload, multipleFileUpload, imageUpload, documentUpload, Helmet, InputValidation, inputValidation, PerformanceAlerting, performanceAlerting, PerformanceDashboard, performanceDashboard, PerformanceMonitor5 as PerformanceMonitor, performanceMonitor, RequestTracer, requestTracer, Security, security, SecuritySuite, securitySuite, cors, jsonBody, requestId, session, csrf, auth, middleware };
|