@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.
Files changed (83) hide show
  1. package/dist/auth.d.ts +22 -8
  2. package/dist/chunk-1ahs68ys.js +18 -0
  3. package/dist/{chunk-j0e7z7hd.js → chunk-cgptvjdf.js} +153 -75
  4. package/dist/chunk-g3ybefhg.js +1923 -0
  5. package/dist/cli.js +13 -11
  6. package/dist/container/container.d.ts +1 -0
  7. package/dist/container/contextual-binding.d.ts +0 -1
  8. package/dist/container/decorators.d.ts +2 -5
  9. package/dist/container/index.d.ts +16 -0
  10. package/dist/container/index.js +322 -0
  11. package/dist/file-serving/static-files.d.ts +16 -0
  12. package/dist/index.d.ts +17 -1
  13. package/dist/index.js +1086 -1270
  14. package/dist/middleware/cors.d.ts +9 -0
  15. package/dist/middleware/csrf.d.ts +7 -0
  16. package/dist/middleware/ddos_protection.d.ts +16 -1
  17. package/dist/middleware/rate_limit.d.ts +7 -1
  18. package/dist/middleware/request_tracer.d.ts +15 -9
  19. package/dist/middleware/response_cache.d.ts +2 -1
  20. package/dist/request/context.d.ts +12 -1
  21. package/dist/request/macros.d.ts +40 -2
  22. package/dist/response/macros.d.ts +3 -3
  23. package/dist/router/fluent-routing.d.ts +50 -13
  24. package/dist/router/handler-resolver.d.ts +8 -0
  25. package/dist/router/index.d.ts +2 -0
  26. package/dist/router/route-compiler.d.ts +8 -10
  27. package/dist/router/route-trie.d.ts +7 -1
  28. package/dist/router/router.d.ts +13 -2
  29. package/dist/router/validation-integration.d.ts +10 -4
  30. package/dist/types/middleware-types.d.ts +1 -1
  31. package/dist/types/route-inference.d.ts +9 -2
  32. package/dist/types.d.ts +62 -9
  33. package/dist/utils.d.ts +1 -1
  34. package/package.json +11 -1
  35. package/src/auth.ts +32 -12
  36. package/src/container/container.ts +58 -12
  37. package/src/container/contextual-binding.ts +4 -1
  38. package/src/container/decorators.ts +16 -7
  39. package/src/container/index.ts +49 -0
  40. package/src/errors/circuit-breaker.ts +3 -2
  41. package/src/errors/graceful-degradation.ts +2 -0
  42. package/src/file-serving/static-files.ts +144 -7
  43. package/src/index.ts +53 -1
  44. package/src/middleware/cors.ts +76 -8
  45. package/src/middleware/csrf.ts +80 -22
  46. package/src/middleware/ddos_protection.ts +27 -3
  47. package/src/middleware/input_validation.ts +10 -6
  48. package/src/middleware/performance_alerting.ts +2 -0
  49. package/src/middleware/performance_monitor.ts +2 -0
  50. package/src/middleware/rate_limit.ts +7 -1
  51. package/src/middleware/request_signing.ts +2 -0
  52. package/src/middleware/request_tracer.ts +34 -17
  53. package/src/middleware/response_cache.ts +48 -13
  54. package/src/middleware/session.ts +3 -3
  55. package/src/observability/metrics.ts +1 -1
  56. package/src/request/context.ts +50 -3
  57. package/src/request/enhanced-request.ts +20 -1
  58. package/src/request/macros.ts +183 -49
  59. package/src/response/macros.ts +3 -3
  60. package/src/router/file-based-routing.ts +17 -10
  61. package/src/router/fluent-routing.ts +117 -48
  62. package/src/router/group-organization.ts +17 -1
  63. package/src/router/handler-resolver.ts +36 -0
  64. package/src/router/http-methods.ts +33 -3
  65. package/src/router/index.ts +6 -0
  66. package/src/router/middleware.ts +3 -0
  67. package/src/router/optimized-route-matching.ts +58 -9
  68. package/src/router/route-compiler.ts +62 -73
  69. package/src/router/route-matching.ts +19 -0
  70. package/src/router/route-trie.ts +34 -48
  71. package/src/router/router.ts +72 -60
  72. package/src/router/server.ts +361 -139
  73. package/src/router/validation-integration.ts +11 -5
  74. package/src/testing/auth-testing.ts +6 -4
  75. package/src/types/middleware-types.ts +7 -4
  76. package/src/types/route-inference.ts +29 -14
  77. package/src/types.ts +63 -7
  78. package/src/url.ts +7 -2
  79. package/src/utils.ts +107 -39
  80. package/src/validation/validator.ts +7 -4
  81. package/src/websocket/clustering.ts +8 -1
  82. package/dist/router/fluent-router.d.ts +0 -315
  83. 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?: Record<string, any>;
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: Record<string, any>, options?: JwtSignOptions): string;
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): Record<string, any> | null;
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: Record<string, any>;
59
- payload: Record<string, any>;
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<Record<string, any>>;
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<Record<string, any>>;
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
- 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;
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 Buffer2 } from "buffer";
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 __defProp2 = Object.defineProperty;
327
- var __returnValue2 = (v) => v;
328
- function __exportSetter2(name, newValue) {
329
- this[name] = __returnValue2.bind(null, newValue);
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 __export2 = (target, all) => {
319
+ var __export = (target, all) => {
332
320
  for (var name in all)
333
- __defProp2(target, name, {
321
+ __defProp(target, name, {
334
322
  get: all[name],
335
323
  enumerable: true,
336
324
  configurable: true,
337
- set: __exportSetter2.bind(all, name)
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
- __export2(exports_dist, {
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 : Buffer.from(data);
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 = Buffer.concat([
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: Buffer.concat([iv, encrypted, authTag]),
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(Buffer.from(Buffer.concat(chunks))));
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 = Buffer.isBuffer(data) ? data : Buffer.from(data, "base64");
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 = Buffer.concat([
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 : Buffer2.from(data);
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 = Buffer2.isBuffer(data) ? data : Buffer2.from(data, "utf8");
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 = Buffer2.allocUnsafe(16 + totalCipherLen + 16);
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(Buffer2.from(Buffer2.concat(chunks))));
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 = Buffer2.isBuffer(data) ? data : Buffer2.from(data, "base64");
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 = Buffer2.allocUnsafe(totalLen);
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
- getCorsHeaders() {
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
- return {
9149
- "Access-Control-Allow-Origin": corsConfig.origin || "*",
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.tokens.set(token2, token2);
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 token = req.headers.get("X-CSRF-TOKEN") || req.jsonBody?.csrf_token || req.jsonBody?._token || null;
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 !== cookieToken || !Csrf.tokens.has(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
- return cookieHeader.split(";").reduce((cookies, cookie) => {
9257
- const [name, value] = cookie.trim().split("=");
9258
- cookies[name] = value;
9259
- return cookies;
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 __defProp3 = Object.defineProperty;
9316
- var __returnValue3 = (v) => v;
9317
- function __exportSetter3(name, newValue) {
9318
- this[name] = __returnValue3.bind(null, newValue);
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 __export3 = (target, all) => {
9377
+ var __export2 = (target, all) => {
9321
9378
  for (var name in all)
9322
- __defProp3(target, name, {
9379
+ __defProp2(target, name, {
9323
9380
  get: all[name],
9324
9381
  enumerable: true,
9325
9382
  configurable: true,
9326
- set: __exportSetter3.bind(all, name)
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
- __export3(exports_src, {
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
- const emailRegex = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/;
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
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
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
- console.warn(`[RequestTracer] Exporting spans, queue size: ${this.exportQueue.length}, active spans: ${this.spans.size}`);
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 { __export, __require, 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 };
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 };