@stacksjs/bun-router 0.0.18 → 0.0.19

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/index.js CHANGED
@@ -64,48 +64,11 @@ import {
64
64
  signRequest,
65
65
  singleFileUpload,
66
66
  verifySignature
67
- } from "./chunk-cgptvjdf.js";
67
+ } from "./chunk-y3xq6wq6.js";
68
68
  import {
69
- AuthenticationException,
70
- AuthorizationException,
71
- BindingBuilder,
72
- BusinessLogicException,
73
- CircuitBreakerOpenException,
74
- CircularDependencyException,
75
- ConfigurationException,
76
- Container,
77
- ContextualContainer,
78
- DatabaseException,
79
- DefaultServiceProviderManager,
80
- EnvironmentManager,
81
- ErrorFactory,
82
- ExternalServiceException,
83
- FileSystemException,
84
- HandlerResolutionException,
85
- InternalServerException,
86
- MethodNotAllowedException,
87
- MiddlewareConfigurationException,
88
- ModelBindingException,
89
- NotFoundException,
90
- RateLimitException,
91
- RouteConflictException,
92
- RouteNotFoundException,
93
- RouterErrorFactory,
94
- RouterException,
95
- ServiceUnavailableException,
96
- TimeoutException,
97
- ValidationException,
98
- createContainer,
99
- detectRouteConflicts,
100
- findSimilarRoutes,
101
- getContainer,
102
- setContainer
103
- } from "./chunk-g3ybefhg.js";
104
- import {
105
- __export,
106
- __require
107
- } from "./chunk-1ahs68ys.js";
108
-
69
+ ValidationException
70
+ } from "./chunk-a8p83068.js";
71
+ import"./chunk-2mx7fq49.js";
109
72
  // src/auth.ts
110
73
  import { timingSafeEqual } from "crypto";
111
74
  function base64UrlEncode(str) {
@@ -247,1805 +210,128 @@ class ApiKeyManager {
247
210
  this.keys = new Map;
248
211
  this.options = {
249
212
  source: "header",
250
- keyName: "X-API-Key",
251
- keyPrefix: "bun_",
252
- ...options
253
- };
254
- }
255
- generateKey(owner, scopes = [], expiresIn) {
256
- const key = `${this.options.keyPrefix}${this.randomString(32)}`;
257
- this.keys.set(key, {
258
- owner,
259
- scopes,
260
- expiresAt: expiresIn ? new Date(Date.now() + expiresIn * 1000) : undefined
261
- });
262
- return key;
263
- }
264
- validateKey(key, requiredScopes = []) {
265
- const keyInfo = this.keys.get(key);
266
- if (!keyInfo) {
267
- return false;
268
- }
269
- if (keyInfo.expiresAt && keyInfo.expiresAt < new Date) {
270
- return false;
271
- }
272
- if (requiredScopes.length > 0) {
273
- return requiredScopes.every((scope) => keyInfo.scopes.includes(scope));
274
- }
275
- return true;
276
- }
277
- getKeyInfo(key) {
278
- return this.keys.get(key) || null;
279
- }
280
- revokeKey(key) {
281
- return this.keys.delete(key);
282
- }
283
- extractFromRequest(req) {
284
- const { source, keyName } = this.options;
285
- switch (source) {
286
- case "header":
287
- return req.headers.get(keyName || "X-API-Key") || null;
288
- case "query":
289
- return new URL(req.url).searchParams.get(keyName || "api_key") || null;
290
- case "cookie":
291
- if (req.cookies) {
292
- const key = keyName || "api_key";
293
- if (typeof req.cookies.get === "function") {
294
- return req.cookies.get(key) || null;
295
- }
296
- return req.cookies[key] || null;
297
- }
298
- return null;
299
- default:
300
- return null;
301
- }
302
- }
303
- randomString(length) {
304
- const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
305
- let result = "";
306
- for (let i = 0;i < length; i++) {
307
- result += chars.charAt(Math.floor(Math.random() * chars.length));
308
- }
309
- return result;
310
- }
311
- }
312
-
313
- class OAuth2Helper {
314
- config;
315
- constructor(config2) {
316
- this.config = config2;
317
- }
318
- getAuthorizationUrl(additionalParams = {}) {
319
- const params = new URLSearchParams({
320
- client_id: this.config.clientId,
321
- redirect_uri: this.config.redirectUri,
322
- response_type: "code",
323
- ...additionalParams
324
- });
325
- if (this.config.scope) {
326
- params.set("scope", this.config.scope);
327
- }
328
- if (this.config.state) {
329
- params.set("state", this.config.state);
330
- }
331
- return `${this.config.authorizeUrl}?${params.toString()}`;
332
- }
333
- async exchangeCodeForToken(code) {
334
- const params = new URLSearchParams({
335
- grant_type: "authorization_code",
336
- code,
337
- redirect_uri: this.config.redirectUri,
338
- client_id: this.config.clientId,
339
- client_secret: this.config.clientSecret
340
- });
341
- const response = await fetch(this.config.tokenUrl, {
342
- method: "POST",
343
- headers: {
344
- "Content-Type": "application/x-www-form-urlencoded"
345
- },
346
- body: params.toString()
347
- });
348
- if (!response.ok) {
349
- throw new Error(`OAuth token exchange failed: ${response.status} ${response.statusText}`);
350
- }
351
- return await response.json();
352
- }
353
- async refreshToken(refreshToken) {
354
- const params = new URLSearchParams({
355
- grant_type: "refresh_token",
356
- refresh_token: refreshToken,
357
- client_id: this.config.clientId,
358
- client_secret: this.config.clientSecret
359
- });
360
- const response = await fetch(this.config.tokenUrl, {
361
- method: "POST",
362
- headers: {
363
- "Content-Type": "application/x-www-form-urlencoded"
364
- },
365
- body: params.toString()
366
- });
367
- if (!response.ok) {
368
- throw new Error(`OAuth token refresh failed: ${response.status} ${response.statusText}`);
369
- }
370
- return await response.json();
371
- }
372
- }
373
- var Auth = {
374
- JWT,
375
- ApiKeyManager,
376
- OAuth2Helper
377
- };
378
- var auth_default = Auth;
379
- // src/errors/index.ts
380
- var exports_errors = {};
381
- __export(exports_errors, {
382
- withCircuitBreaker: () => withCircuitBreaker,
383
- globalCircuitBreakerRegistry: () => globalCircuitBreakerRegistry,
384
- findSimilarRoutes: () => findSimilarRoutes,
385
- detectRouteConflicts: () => detectRouteConflicts,
386
- createGracefulDegradationMiddleware: () => createGracefulDegradationMiddleware,
387
- createErrorReporting: () => createErrorReporting,
388
- createCircuitBreakerMiddleware: () => createCircuitBreakerMiddleware,
389
- createCircuitBreaker: () => createCircuitBreaker,
390
- createAdvancedErrorHandler: () => createAdvancedErrorHandler,
391
- circuitBreaker: () => circuitBreaker,
392
- ValidationException: () => ValidationException,
393
- TimeoutException: () => TimeoutException,
394
- ServiceUnavailableException: () => ServiceUnavailableException,
395
- ServiceHealthMonitor: () => ServiceHealthMonitor,
396
- SentryReporter: () => SentryReporter,
397
- RouterException: () => RouterException,
398
- RouterErrorFactory: () => RouterErrorFactory,
399
- RouteNotFoundException: () => RouteNotFoundException,
400
- RouteConflictException: () => RouteConflictException,
401
- RateLimitException: () => RateLimitException,
402
- NotFoundException: () => NotFoundException,
403
- ModelBindingException: () => ModelBindingException,
404
- MiddlewareConfigurationException: () => MiddlewareConfigurationException,
405
- MethodNotAllowedException: () => MethodNotAllowedException,
406
- InternalServerException: () => InternalServerException,
407
- HandlerResolutionException: () => HandlerResolutionException,
408
- GracefulDegradationManager: () => GracefulDegradationManager,
409
- FileSystemException: () => FileSystemException,
410
- ExternalServiceException: () => ExternalServiceException,
411
- ErrorReportingPresets: () => ErrorReportingPresets,
412
- ErrorReportingManager: () => ErrorReportingManager,
413
- ErrorFactory: () => ErrorFactory,
414
- DegradationStrategies: () => DegradationStrategies,
415
- DatabaseException: () => DatabaseException,
416
- CustomReporter: () => CustomReporter,
417
- ConfigurationException: () => ConfigurationException,
418
- CircularDependencyException: () => CircularDependencyException,
419
- CircuitBreakerRegistry: () => CircuitBreakerRegistry,
420
- CircuitBreakerPresets: () => CircuitBreakerPresets,
421
- CircuitBreakerOpenException: () => CircuitBreakerOpenException,
422
- CircuitBreakerHttpClient: () => CircuitBreakerHttpClient,
423
- CircuitBreaker: () => CircuitBreaker,
424
- BusinessLogicException: () => BusinessLogicException,
425
- BugsnagReporter: () => BugsnagReporter,
426
- AuthorizationException: () => AuthorizationException,
427
- AuthenticationException: () => AuthenticationException
428
- });
429
-
430
- // src/errors/circuit-breaker.ts
431
- class CircuitBreaker {
432
- config;
433
- state = "CLOSED";
434
- metrics;
435
- nextAttemptTime;
436
- halfOpenCallCount = 0;
437
- recentRequests = [];
438
- constructor(config2) {
439
- const defaults = {
440
- failureThreshold: 5,
441
- recoveryTimeout: 60000,
442
- timeout: 30000,
443
- monitoringPeriod: 60000,
444
- minimumRequests: 10,
445
- errorThresholdPercentage: 50,
446
- halfOpenMaxCalls: 3,
447
- resetTimeout: 60000
448
- };
449
- this.config = {
450
- ...defaults,
451
- ...config2
452
- };
453
- this.metrics = {
454
- totalRequests: 0,
455
- successfulRequests: 0,
456
- failedRequests: 0,
457
- timeouts: 0,
458
- averageResponseTime: 0,
459
- state: this.state,
460
- consecutiveFailures: 0,
461
- consecutiveSuccesses: 0
462
- };
463
- setInterval(() => this.cleanupOldRequests(), this.config.monitoringPeriod / 4).unref?.();
464
- }
465
- async execute(call) {
466
- if (this.state === "OPEN") {
467
- if (this.nextAttemptTime && new Date < this.nextAttemptTime) {
468
- const error2 = new CircuitBreakerOpenException(this.config.name, this.nextAttemptTime, call.context);
469
- if (call.fallback) {
470
- try {
471
- return await call.fallback();
472
- } catch (fallbackError) {
473
- console.error(fallbackError);
474
- }
475
- }
476
- throw error2;
477
- } else {
478
- this.transitionToHalfOpen();
479
- }
480
- }
481
- if (this.state === "HALF_OPEN" && this.halfOpenCallCount >= this.config.halfOpenMaxCalls) {
482
- const error2 = new CircuitBreakerOpenException(this.config.name, this.nextAttemptTime || new Date(Date.now() + this.config.recoveryTimeout), call.context);
483
- if (call.fallback) {
484
- try {
485
- return await call.fallback();
486
- } catch (fallbackError) {
487
- console.error(fallbackError);
488
- throw error2;
489
- }
490
- }
491
- throw error2;
492
- }
493
- const startTime = Date.now();
494
- let result;
495
- let error = null;
496
- try {
497
- if (this.state === "HALF_OPEN") {
498
- this.halfOpenCallCount++;
499
- }
500
- const timeout = call.timeout || this.config.timeout;
501
- const timeoutPromise = new Promise((_, reject) => {
502
- setTimeout(() => {
503
- reject(new TimeoutException(`Circuit breaker timeout for ${this.config.name}`, timeout, call.context));
504
- }, timeout);
505
- });
506
- result = await Promise.race([call.execute(), timeoutPromise]);
507
- this.onSuccess(Date.now() - startTime);
508
- return result;
509
- } catch (err) {
510
- error = err;
511
- this.onFailure(error, Date.now() - startTime);
512
- if (call.fallback) {
513
- try {
514
- return await call.fallback();
515
- } catch (fallbackError) {
516
- console.error(fallbackError);
517
- throw error;
518
- }
519
- } else {
520
- throw error;
521
- }
522
- }
523
- }
524
- getState() {
525
- return this.state;
526
- }
527
- getMetrics() {
528
- return {
529
- ...this.metrics,
530
- state: this.state,
531
- nextAttemptTime: this.nextAttemptTime
532
- };
533
- }
534
- forceOpen() {
535
- this.transitionToOpen();
536
- }
537
- forceClosed() {
538
- this.transitionToClosed();
539
- }
540
- reset() {
541
- this.metrics = {
542
- totalRequests: 0,
543
- successfulRequests: 0,
544
- failedRequests: 0,
545
- timeouts: 0,
546
- averageResponseTime: 0,
547
- state: this.state,
548
- consecutiveFailures: 0,
549
- consecutiveSuccesses: 0
550
- };
551
- this.recentRequests = [];
552
- }
553
- onSuccess(responseTime) {
554
- this.metrics.totalRequests++;
555
- this.metrics.successfulRequests++;
556
- this.metrics.consecutiveSuccesses++;
557
- this.metrics.consecutiveFailures = 0;
558
- this.metrics.lastSuccessTime = new Date;
559
- this.updateAverageResponseTime(responseTime);
560
- this.recentRequests.push({
561
- timestamp: new Date,
562
- success: true,
563
- responseTime
564
- });
565
- if (this.state === "HALF_OPEN") {
566
- if (this.metrics.consecutiveSuccesses >= this.config.halfOpenMaxCalls) {
567
- this.transitionToClosed();
568
- }
569
- }
570
- if (this.config.onSuccess) {
571
- this.config.onSuccess(this.config.name);
572
- }
573
- }
574
- onFailure(error, responseTime) {
575
- this.metrics.totalRequests++;
576
- this.metrics.failedRequests++;
577
- this.metrics.consecutiveFailures++;
578
- this.metrics.consecutiveSuccesses = 0;
579
- this.metrics.lastFailureTime = new Date;
580
- if (error instanceof TimeoutException) {
581
- this.metrics.timeouts++;
582
- }
583
- this.updateAverageResponseTime(responseTime);
584
- this.recentRequests.push({
585
- timestamp: new Date,
586
- success: false,
587
- responseTime
588
- });
589
- if (this.shouldTrip(error)) {
590
- if (this.state === "CLOSED") {
591
- this.transitionToOpen();
592
- } else if (this.state === "HALF_OPEN") {
593
- this.transitionToOpen();
594
- }
595
- }
596
- if (this.config.onFailure) {
597
- this.config.onFailure(error, this.config.name);
598
- }
599
- }
600
- shouldTrip(error) {
601
- if (this.config.shouldTripOnError && !this.config.shouldTripOnError(error)) {
602
- return false;
603
- }
604
- if (this.metrics.consecutiveFailures >= this.config.failureThreshold) {
605
- return true;
606
- }
607
- const recentRequests = this.getRecentRequests();
608
- if (recentRequests.length >= this.config.minimumRequests) {
609
- const failedRequests = recentRequests.filter((r) => !r.success).length;
610
- const errorRate = failedRequests / recentRequests.length * 100;
611
- if (errorRate >= this.config.errorThresholdPercentage) {
612
- return true;
613
- }
614
- }
615
- return false;
616
- }
617
- transitionToClosed() {
618
- this.state = "CLOSED";
619
- this.nextAttemptTime = undefined;
620
- this.halfOpenCallCount = 0;
621
- this.metrics.state = this.state;
622
- if (this.config.onStateChange) {
623
- this.config.onStateChange(this.state, this.config.name);
624
- }
625
- }
626
- transitionToOpen() {
627
- this.state = "OPEN";
628
- this.nextAttemptTime = new Date(Date.now() + this.config.recoveryTimeout);
629
- this.halfOpenCallCount = 0;
630
- this.metrics.state = this.state;
631
- this.metrics.nextAttemptTime = this.nextAttemptTime;
632
- if (this.config.onStateChange) {
633
- this.config.onStateChange(this.state, this.config.name);
634
- }
635
- }
636
- transitionToHalfOpen() {
637
- this.state = "HALF_OPEN";
638
- this.halfOpenCallCount = 0;
639
- this.metrics.state = this.state;
640
- if (this.config.onStateChange) {
641
- this.config.onStateChange(this.state, this.config.name);
642
- }
643
- }
644
- updateAverageResponseTime(responseTime) {
645
- if (this.metrics.totalRequests === 1) {
646
- this.metrics.averageResponseTime = responseTime;
647
- } else {
648
- const alpha = 0.1;
649
- this.metrics.averageResponseTime = alpha * responseTime + (1 - alpha) * this.metrics.averageResponseTime;
650
- }
651
- }
652
- getRecentRequests() {
653
- const cutoff = new Date(Date.now() - this.config.monitoringPeriod);
654
- return this.recentRequests.filter((r) => r.timestamp > cutoff);
655
- }
656
- cleanupOldRequests() {
657
- const cutoff = new Date(Date.now() - this.config.monitoringPeriod);
658
- this.recentRequests = this.recentRequests.filter((r) => r.timestamp > cutoff);
659
- }
660
- }
661
-
662
- class CircuitBreakerRegistry {
663
- breakers = new Map;
664
- configs = new Map;
665
- register(config2) {
666
- const breaker = new CircuitBreaker(config2);
667
- this.breakers.set(config2.name, breaker);
668
- this.configs.set(config2.name, config2);
669
- return breaker;
670
- }
671
- get(name) {
672
- return this.breakers.get(name);
673
- }
674
- async execute(name, call) {
675
- const breaker = this.breakers.get(name);
676
- if (!breaker) {
677
- throw new Error(`Circuit breaker '${name}' not found`);
678
- }
679
- return breaker.execute(call);
680
- }
681
- getAllMetrics() {
682
- const metrics = {};
683
- for (const [name, breaker] of this.breakers) {
684
- metrics[name] = breaker.getMetrics();
685
- }
686
- return metrics;
687
- }
688
- getBreakersByState(state) {
689
- const names = [];
690
- for (const [name, breaker] of this.breakers) {
691
- if (breaker.getState() === state) {
692
- names.push(name);
693
- }
694
- }
695
- return names;
696
- }
697
- forceAllClosed() {
698
- for (const breaker of this.breakers.values()) {
699
- breaker.forceClosed();
700
- }
701
- }
702
- resetAll() {
703
- for (const breaker of this.breakers.values()) {
704
- breaker.reset();
705
- }
706
- }
707
- remove(name) {
708
- this.configs.delete(name);
709
- return this.breakers.delete(name);
710
- }
711
- getNames() {
712
- return Array.from(this.breakers.keys());
713
- }
714
- }
715
- function circuitBreaker(config2) {
716
- const breaker = new CircuitBreaker(config2);
717
- return function(target, propertyName, descriptor) {
718
- const method = descriptor.value;
719
- descriptor.value = async function(...args) {
720
- return breaker.execute({
721
- execute: () => method.apply(this, args),
722
- context: {
723
- method: propertyName,
724
- timestamp: new Date
725
- }
726
- });
727
- };
728
- return descriptor;
729
- };
730
- }
731
-
732
- class CircuitBreakerHttpClient {
733
- registry;
734
- constructor(registry) {
735
- this.registry = registry || new CircuitBreakerRegistry;
736
- }
737
- async fetch(url, options = {}) {
738
- const { circuitBreaker: circuitBreaker2, fallback, context, ...fetchOptions } = options;
739
- let breakerName;
740
- if (typeof circuitBreaker2 === "string") {
741
- breakerName = circuitBreaker2;
742
- } else if (circuitBreaker2) {
743
- breakerName = circuitBreaker2.name;
744
- if (!this.registry.get(breakerName)) {
745
- this.registry.register(circuitBreaker2);
746
- }
747
- } else {
748
- const hostname = new URL(url).hostname;
749
- breakerName = `http-${hostname}`;
750
- if (!this.registry.get(breakerName)) {
751
- this.registry.register({
752
- name: breakerName,
753
- failureThreshold: 5,
754
- recoveryTimeout: 60000,
755
- timeout: 30000,
756
- monitoringPeriod: 60000,
757
- minimumRequests: 10,
758
- errorThresholdPercentage: 50,
759
- halfOpenMaxCalls: 3,
760
- resetTimeout: 60000
761
- });
762
- }
763
- }
764
- return this.registry.execute(breakerName, {
765
- execute: async () => {
766
- const response = await fetch(url, fetchOptions);
767
- if (response.status >= 500) {
768
- throw new ExternalServiceException(`HTTP ${response.status}: ${response.statusText}`, breakerName, url, response.status, undefined, context);
769
- }
770
- return response;
771
- },
772
- fallback,
773
- context
774
- });
775
- }
776
- getRegistry() {
777
- return this.registry;
778
- }
779
- }
780
- function createCircuitBreakerMiddleware(registry) {
781
- return async (req, next) => {
782
- const serviceName = req.context?.serviceName;
783
- if (!serviceName) {
784
- return next();
785
- }
786
- const breaker = registry.get(serviceName);
787
- if (!breaker) {
788
- return next();
789
- }
790
- return breaker.execute({
791
- execute: next,
792
- context: {
793
- requestId: req.requestId,
794
- route: req.route,
795
- method: req.method,
796
- url: req.url
797
- }
798
- });
799
- };
800
- }
801
- var CircuitBreakerPresets = {
802
- critical: (name) => ({
803
- name,
804
- failureThreshold: 3,
805
- recoveryTimeout: 30000,
806
- timeout: 5000,
807
- monitoringPeriod: 30000,
808
- minimumRequests: 5,
809
- errorThresholdPercentage: 30,
810
- halfOpenMaxCalls: 2,
811
- resetTimeout: 30000
812
- }),
813
- standard: (name) => ({
814
- name,
815
- failureThreshold: 5,
816
- recoveryTimeout: 60000,
817
- timeout: 30000,
818
- monitoringPeriod: 60000,
819
- minimumRequests: 10,
820
- errorThresholdPercentage: 50,
821
- halfOpenMaxCalls: 3,
822
- resetTimeout: 60000
823
- }),
824
- tolerant: (name) => ({
825
- name,
826
- failureThreshold: 10,
827
- recoveryTimeout: 120000,
828
- timeout: 60000,
829
- monitoringPeriod: 120000,
830
- minimumRequests: 20,
831
- errorThresholdPercentage: 70,
832
- halfOpenMaxCalls: 5,
833
- resetTimeout: 120000
834
- }),
835
- database: (name) => ({
836
- name,
837
- failureThreshold: 3,
838
- recoveryTimeout: 60000,
839
- timeout: 1e4,
840
- monitoringPeriod: 60000,
841
- minimumRequests: 5,
842
- errorThresholdPercentage: 40,
843
- halfOpenMaxCalls: 2,
844
- resetTimeout: 60000,
845
- shouldTripOnError: (error) => {
846
- return !(error.message.includes("validation") || error.message.includes("constraint"));
847
- }
848
- }),
849
- externalApi: (name) => ({
850
- name,
851
- failureThreshold: 5,
852
- recoveryTimeout: 90000,
853
- timeout: 15000,
854
- monitoringPeriod: 90000,
855
- minimumRequests: 8,
856
- errorThresholdPercentage: 60,
857
- halfOpenMaxCalls: 3,
858
- resetTimeout: 90000,
859
- shouldTripOnError: (error) => {
860
- if (error instanceof ExternalServiceException) {
861
- return error.statusCode >= 500;
862
- }
863
- return true;
864
- }
865
- })
866
- };
867
- var globalCircuitBreakerRegistry = new CircuitBreakerRegistry;
868
- function createCircuitBreaker(config2) {
869
- return globalCircuitBreakerRegistry.register(config2);
870
- }
871
- async function withCircuitBreaker(name, call) {
872
- return globalCircuitBreakerRegistry.execute(name, call);
873
- }
874
- // src/errors/error-reporting.ts
875
- class SentryReporter {
876
- config;
877
- sentry;
878
- constructor(config2) {
879
- this.config = config2;
880
- this.initializeSentry();
881
- }
882
- async initializeSentry() {
883
- try {
884
- const Sentry = await import("@sentry/node");
885
- Sentry.init({
886
- dsn: this.config.dsn,
887
- environment: this.config.environment || "production",
888
- release: this.config.release,
889
- sampleRate: this.config.sampleRate || 1,
890
- beforeSend: (event, hint) => {
891
- const error = hint.originalException;
892
- if (error && this.config.beforeSend) {
893
- const filteredError = this.config.beforeSend(error, error.context);
894
- return filteredError ? event : null;
895
- }
896
- return event;
897
- },
898
- ignoreErrors: this.config.filters?.ignoreErrors,
899
- denyUrls: this.config.filters?.ignoreUrls,
900
- allowUrls: this.config.filters?.allowUrls,
901
- initialScope: {
902
- tags: this.config.tags,
903
- extra: this.config.extra,
904
- user: this.config.user
905
- },
906
- maxBreadcrumbs: this.config.breadcrumbs?.maxBreadcrumbs || 100,
907
- tracesSampleRate: this.config.performance?.tracesSampleRate || 0.1
908
- });
909
- this.sentry = Sentry;
910
- } catch (error) {
911
- console.warn("Failed to initialize Sentry:", error);
912
- }
913
- }
914
- async report(error, context) {
915
- if (!this.sentry || !this.config.enabled) {
916
- return null;
917
- }
918
- try {
919
- if (this.shouldIgnoreError(error)) {
920
- return null;
921
- }
922
- const mergedContext = { ...error.context, ...context };
923
- return this.sentry.withScope((scope) => {
924
- if (mergedContext.requestId) {
925
- scope.setTag("requestId", mergedContext.requestId);
926
- }
927
- if (mergedContext.userId) {
928
- scope.setUser({ id: mergedContext.userId });
929
- }
930
- if (mergedContext.traceId) {
931
- scope.setTag("traceId", mergedContext.traceId);
932
- }
933
- if (mergedContext.route) {
934
- scope.setTag("route", mergedContext.route);
935
- }
936
- if (mergedContext.method) {
937
- scope.setTag("method", mergedContext.method);
938
- }
939
- scope.setTag("errorCode", error.code);
940
- scope.setTag("severity", error.severity);
941
- scope.setTag("category", error.category);
942
- scope.setLevel(this.mapSeverityToLevel(error.severity));
943
- scope.setExtra("statusCode", error.statusCode);
944
- scope.setExtra("retryable", error.retryable);
945
- scope.setExtra("timestamp", error.timestamp);
946
- scope.setExtra("context", mergedContext);
947
- const fingerprint = this.generateFingerprint(error);
948
- if (fingerprint.length > 0) {
949
- scope.setFingerprint(fingerprint);
950
- }
951
- return this.sentry.captureException(error);
952
- });
953
- } catch (reportingError) {
954
- console.error("Failed to report error to Sentry:", reportingError);
955
- return null;
956
- }
957
- }
958
- addBreadcrumb(message, category = "default", level = "info", data) {
959
- if (!this.sentry || !this.config.breadcrumbs?.enabled) {
960
- return;
961
- }
962
- this.sentry.addBreadcrumb({
963
- message,
964
- category,
965
- level,
966
- data,
967
- timestamp: Date.now() / 1000
968
- });
969
- }
970
- setUser(user) {
971
- if (!this.sentry)
972
- return;
973
- this.sentry.setUser(user);
974
- }
975
- setTag(key, value) {
976
- if (!this.sentry)
977
- return;
978
- this.sentry.setTag(key, value);
979
- }
980
- setExtra(key, value) {
981
- if (!this.sentry)
982
- return;
983
- this.sentry.setExtra(key, value);
984
- }
985
- setContext(key, context) {
986
- if (!this.sentry)
987
- return;
988
- this.sentry.setContext(key, context);
989
- }
990
- async close(timeout = 2000) {
991
- if (!this.sentry)
992
- return true;
993
- return this.sentry.close(timeout);
994
- }
995
- shouldIgnoreError(error) {
996
- const filters = this.config.filters;
997
- if (!filters)
998
- return false;
999
- if (filters.ignoreCodes?.includes(error.code)) {
1000
- return true;
1001
- }
1002
- if (filters.ignoreErrors?.some((pattern) => {
1003
- if (typeof pattern === "string") {
1004
- return error.message.includes(pattern);
1005
- }
1006
- return pattern.test(error.message);
1007
- })) {
1008
- return true;
1009
- }
1010
- return false;
1011
- }
1012
- mapSeverityToLevel(severity) {
1013
- switch (severity) {
1014
- case "low":
1015
- return "info";
1016
- case "medium":
1017
- return "warning";
1018
- case "high":
1019
- return "error";
1020
- case "critical":
1021
- return "fatal";
1022
- default:
1023
- return "error";
1024
- }
1025
- }
1026
- generateFingerprint(error) {
1027
- const fingerprint = [];
1028
- fingerprint.push(error.code);
1029
- fingerprint.push(error.category);
1030
- if (error.context.route) {
1031
- fingerprint.push(error.context.route);
1032
- }
1033
- return fingerprint;
1034
- }
1035
- }
1036
-
1037
- class BugsnagReporter {
1038
- config;
1039
- bugsnag;
1040
- constructor(config2) {
1041
- this.config = config2;
1042
- this.initializeBugsnag();
1043
- }
1044
- async initializeBugsnag() {
1045
- try {
1046
- const Bugsnag = await import("@bugsnag/js");
1047
- Bugsnag.start({
1048
- apiKey: this.config.apiKey,
1049
- appVersion: this.config.release,
1050
- releaseStage: this.config.environment || "production",
1051
- enabledReleaseStages: ["production", "staging"],
1052
- maxBreadcrumbs: this.config.breadcrumbs?.maxBreadcrumbs || 25,
1053
- onError: (event) => {
1054
- const error = event.originalError;
1055
- if (error && this.config.beforeSend) {
1056
- const filteredError = this.config.beforeSend(error, error.context);
1057
- return filteredError !== null;
1058
- }
1059
- return !this.shouldIgnoreError(error);
1060
- }
1061
- });
1062
- this.bugsnag = Bugsnag;
1063
- } catch (error) {
1064
- console.warn("Failed to initialize Bugsnag:", error);
1065
- }
1066
- }
1067
- async report(error, context) {
1068
- if (!this.bugsnag || !this.config.enabled) {
1069
- return null;
1070
- }
1071
- try {
1072
- if (this.shouldIgnoreError(error)) {
1073
- return null;
1074
- }
1075
- const mergedContext = { ...error.context, ...context };
1076
- this.bugsnag.notify(error, (event) => {
1077
- event.severity = this.mapSeverityToBugsnag(error.severity);
1078
- event.context = mergedContext.route || mergedContext.url;
1079
- if (mergedContext.userId) {
1080
- event.setUser(mergedContext.userId, undefined, undefined);
1081
- }
1082
- event.addMetadata("error", {
1083
- code: error.code,
1084
- category: error.category,
1085
- statusCode: error.statusCode,
1086
- retryable: error.retryable,
1087
- timestamp: error.timestamp
1088
- });
1089
- event.addMetadata("request", {
1090
- requestId: mergedContext.requestId,
1091
- method: mergedContext.method,
1092
- url: mergedContext.url,
1093
- userAgent: mergedContext.userAgent,
1094
- ip: mergedContext.ip
1095
- });
1096
- event.groupingHash = this.generateGroupingHash(error);
1097
- });
1098
- return "reported";
1099
- } catch (reportingError) {
1100
- console.error("Failed to report error to Bugsnag:", reportingError);
1101
- return null;
1102
- }
1103
- }
1104
- addBreadcrumb(message, category = "manual", _level = "info", data) {
1105
- if (!this.bugsnag || !this.config.breadcrumbs?.enabled) {
1106
- return;
1107
- }
1108
- this.bugsnag.leaveBreadcrumb(message, data, category);
1109
- }
1110
- setUser(user) {
1111
- if (!this.bugsnag)
1112
- return;
1113
- this.bugsnag.setUser(user.id, user.email, user.username);
1114
- }
1115
- setTag(key, value) {
1116
- if (!this.bugsnag)
1117
- return;
1118
- this.bugsnag.addMetadata("tags", { [key]: value });
1119
- }
1120
- setExtra(key, value) {
1121
- if (!this.bugsnag)
1122
- return;
1123
- this.bugsnag.addMetadata("extra", { [key]: value });
1124
- }
1125
- setContext(key, context) {
1126
- if (!this.bugsnag)
1127
- return;
1128
- this.bugsnag.addMetadata(key, context);
1129
- }
1130
- async close() {
1131
- return true;
1132
- }
1133
- shouldIgnoreError(error) {
1134
- const filters = this.config.filters;
1135
- if (!filters)
1136
- return false;
1137
- if (filters.ignoreCodes?.includes(error.code)) {
1138
- return true;
1139
- }
1140
- if (filters.ignoreErrors?.some((pattern) => {
1141
- if (typeof pattern === "string") {
1142
- return error.message.includes(pattern);
1143
- }
1144
- return pattern.test(error.message);
1145
- })) {
1146
- return true;
1147
- }
1148
- return false;
1149
- }
1150
- mapSeverityToBugsnag(severity) {
1151
- switch (severity) {
1152
- case "low":
1153
- return "info";
1154
- case "medium":
1155
- return "warning";
1156
- case "high":
1157
- return "error";
1158
- case "critical":
1159
- return "error";
1160
- default:
1161
- return "warning";
1162
- }
1163
- }
1164
- generateGroupingHash(error) {
1165
- return `${error.code}-${error.category}-${error.context.route || "unknown"}`;
1166
- }
1167
- }
1168
-
1169
- class CustomReporter {
1170
- config;
1171
- reportHandler;
1172
- constructor(config2, reportHandler) {
1173
- this.config = config2;
1174
- this.reportHandler = reportHandler;
1175
- }
1176
- async report(error, context) {
1177
- if (!this.config.enabled) {
1178
- return null;
1179
- }
1180
- try {
1181
- const mergedContext = { ...error.context, ...context };
1182
- if (this.config.beforeSend) {
1183
- const filteredError = this.config.beforeSend(error, mergedContext);
1184
- if (!filteredError) {
1185
- return null;
1186
- }
1187
- }
1188
- const report = {
1189
- error,
1190
- context: mergedContext,
1191
- level: this.mapSeverityToLevel(error.severity),
1192
- tags: this.config.tags,
1193
- extra: this.config.extra,
1194
- user: this.config.user
1195
- };
1196
- return await this.reportHandler(report);
1197
- } catch (reportingError) {
1198
- console.error("Failed to report error with custom handler:", reportingError);
1199
- return null;
1200
- }
1201
- }
1202
- addBreadcrumb() {}
1203
- setUser() {}
1204
- setTag() {}
1205
- setExtra() {}
1206
- setContext() {}
1207
- async close() {
1208
- return true;
1209
- }
1210
- mapSeverityToLevel(severity) {
1211
- switch (severity) {
1212
- case "low":
1213
- return "info";
1214
- case "medium":
1215
- return "warning";
1216
- case "high":
1217
- return "error";
1218
- case "critical":
1219
- return "fatal";
1220
- default:
1221
- return "error";
1222
- }
1223
- }
1224
- }
1225
-
1226
- class ErrorReportingManager {
1227
- reporters = [];
1228
- breadcrumbs = [];
1229
- maxBreadcrumbs = 50;
1230
- constructor(configs) {
1231
- this.initializeReporters(configs);
1232
- }
1233
- initializeReporters(configs) {
1234
- for (const config2 of configs) {
1235
- if (!config2.enabled)
1236
- continue;
1237
- switch (config2.service) {
1238
- case "sentry":
1239
- this.reporters.push(new SentryReporter(config2));
1240
- break;
1241
- case "bugsnag":
1242
- this.reporters.push(new BugsnagReporter(config2));
1243
- break;
1244
- case "custom":
1245
- break;
1246
- }
1247
- }
1248
- }
1249
- addReporter(reporter) {
1250
- this.reporters.push(reporter);
1251
- }
1252
- async report(error, context) {
1253
- const reportIds = [];
1254
- this.addBreadcrumb(`Error occurred: ${error.message}`, "error", {
1255
- code: error.code,
1256
- severity: error.severity
1257
- });
1258
- const promises = this.reporters.map(async (reporter) => {
1259
- try {
1260
- const reportId = await reporter.report(error, context);
1261
- if (reportId) {
1262
- reportIds.push(reportId);
1263
- }
1264
- } catch (reportingError) {
1265
- console.error("Reporter failed:", reportingError);
1266
- }
1267
- });
1268
- await Promise.allSettled(promises);
1269
- return reportIds;
1270
- }
1271
- addBreadcrumb(message, category = "default", data) {
1272
- this.breadcrumbs.push({
1273
- message,
1274
- timestamp: new Date,
1275
- data: { category, ...data }
1276
- });
1277
- if (this.breadcrumbs.length > this.maxBreadcrumbs) {
1278
- this.breadcrumbs = this.breadcrumbs.slice(-this.maxBreadcrumbs);
1279
- }
1280
- this.reporters.forEach((reporter) => {
1281
- reporter.addBreadcrumb(message, category, "info", data);
1282
- });
1283
- }
1284
- setUser(user) {
1285
- this.reporters.forEach((reporter) => reporter.setUser(user));
1286
- }
1287
- setTag(key, value) {
1288
- this.reporters.forEach((reporter) => reporter.setTag(key, value));
1289
- }
1290
- setExtra(key, value) {
1291
- this.reporters.forEach((reporter) => reporter.setExtra(key, value));
1292
- }
1293
- setContext(key, context) {
1294
- this.reporters.forEach((reporter) => reporter.setContext(key, context));
1295
- }
1296
- async close(timeout = 5000) {
1297
- const promises = this.reporters.map((reporter) => reporter.close(timeout));
1298
- const results = await Promise.allSettled(promises);
1299
- return results.every((result) => result.status === "fulfilled" && result.value);
1300
- }
1301
- getBreadcrumbs() {
1302
- return [...this.breadcrumbs];
1303
- }
1304
- }
1305
- function createErrorReporting(configs) {
1306
- return new ErrorReportingManager(configs);
1307
- }
1308
- var ErrorReportingPresets = {
1309
- sentry: (dsn, environment = "production") => ({
1310
- enabled: true,
1311
- service: "sentry",
1312
- dsn,
1313
- environment,
1314
- sampleRate: 1,
1315
- breadcrumbs: {
1316
- enabled: true,
1317
- maxBreadcrumbs: 100
1318
- },
1319
- performance: {
1320
- enabled: true,
1321
- tracesSampleRate: 0.1
1322
- },
1323
- filters: {
1324
- ignoreErrors: [
1325
- "Non-Error promise rejection captured",
1326
- "ResizeObserver loop limit exceeded"
1327
- ],
1328
- ignoreCodes: ["VALIDATION_ERROR"]
1329
- }
1330
- }),
1331
- bugsnag: (apiKey, environment = "production") => ({
1332
- enabled: true,
1333
- service: "bugsnag",
1334
- apiKey,
1335
- environment,
1336
- breadcrumbs: {
1337
- enabled: true,
1338
- maxBreadcrumbs: 25
1339
- },
1340
- filters: {
1341
- ignoreCodes: ["VALIDATION_ERROR"]
1342
- }
1343
- }),
1344
- development: () => ({
1345
- enabled: false,
1346
- service: "custom",
1347
- environment: "development"
1348
- })
1349
- };
1350
-
1351
- // src/errors/graceful-degradation.ts
1352
- class ServiceHealthMonitor {
1353
- healthStatus = new Map;
1354
- healthCheckIntervals = new Map;
1355
- config;
1356
- constructor(config2) {
1357
- this.config = config2;
1358
- this.initializeHealthChecks();
1359
- }
1360
- initializeHealthChecks() {
1361
- for (const [serviceName, healthConfig] of Object.entries(this.config.healthChecks)) {
1362
- if (!healthConfig.enabled)
1363
- continue;
1364
- this.healthStatus.set(serviceName, {
1365
- name: serviceName,
1366
- status: "healthy",
1367
- lastCheck: new Date,
1368
- responseTime: 0,
1369
- errorRate: 0,
1370
- availability: 100,
1371
- consecutiveFailures: 0
1372
- });
1373
- const interval = setInterval(() => this.performHealthCheck(serviceName, healthConfig), healthConfig.interval);
1374
- interval.unref?.();
1375
- this.healthCheckIntervals.set(serviceName, interval);
1376
- }
1377
- }
1378
- async performHealthCheck(serviceName, config2) {
1379
- const startTime = Date.now();
1380
- let isHealthy = false;
1381
- try {
1382
- const controller = new AbortController;
1383
- const timeoutId = setTimeout(() => controller.abort(), config2.timeout);
1384
- const response = await fetch(config2.endpoint, {
1385
- signal: controller.signal,
1386
- headers: config2.headers,
1387
- method: "GET"
1388
- });
1389
- clearTimeout(timeoutId);
1390
- isHealthy = config2.expectedStatus.includes(response.status);
1391
- if (isHealthy && config2.expectedBody) {
1392
- const body = await response.text();
1393
- if (typeof config2.expectedBody === "string") {
1394
- isHealthy = body.includes(config2.expectedBody);
1395
- } else {
1396
- isHealthy = config2.expectedBody.test(body);
1397
- }
1398
- }
1399
- } catch (error) {
1400
- console.error(error);
1401
- isHealthy = false;
1402
- }
1403
- const responseTime = Date.now() - startTime;
1404
- this.updateHealthStatus(serviceName, isHealthy, responseTime);
1405
- if (isHealthy && config2.onHealthy) {
1406
- config2.onHealthy();
1407
- } else if (!isHealthy && config2.onUnhealthy) {
1408
- config2.onUnhealthy(new Error(`Health check failed for ${serviceName}`));
1409
- }
1410
- }
1411
- updateHealthStatus(serviceName, isHealthy, responseTime) {
1412
- const current = this.healthStatus.get(serviceName);
1413
- if (!current)
1414
- return;
1415
- const updated = {
1416
- ...current,
1417
- lastCheck: new Date,
1418
- responseTime,
1419
- consecutiveFailures: isHealthy ? 0 : current.consecutiveFailures + 1
1420
- };
1421
- if (updated.consecutiveFailures === 0) {
1422
- updated.status = "healthy";
1423
- } else if (updated.consecutiveFailures < 3) {
1424
- updated.status = "degraded";
1425
- } else {
1426
- updated.status = "unhealthy";
1427
- }
1428
- const healthyCount = isHealthy ? 1 : 0;
1429
- updated.availability = current.availability * 0.9 + healthyCount * 10;
1430
- this.healthStatus.set(serviceName, updated);
1431
- }
1432
- getServiceHealth(serviceName) {
1433
- return this.healthStatus.get(serviceName);
1434
- }
1435
- getAllServiceHealth() {
1436
- return Array.from(this.healthStatus.values());
1437
- }
1438
- isServiceHealthy(serviceName) {
1439
- const health = this.healthStatus.get(serviceName);
1440
- return health?.status === "healthy";
1441
- }
1442
- isServiceDegraded(serviceName) {
1443
- const health = this.healthStatus.get(serviceName);
1444
- return health?.status === "degraded";
1445
- }
1446
- stop() {
1447
- for (const interval of this.healthCheckIntervals.values()) {
1448
- clearInterval(interval);
1449
- }
1450
- this.healthCheckIntervals.clear();
1451
- }
1452
- }
1453
-
1454
- class GracefulDegradationManager {
1455
- config;
1456
- healthMonitor;
1457
- cache = new Map;
1458
- metrics = new Map;
1459
- constructor(config2) {
1460
- this.config = config2;
1461
- this.healthMonitor = new ServiceHealthMonitor(config2);
1462
- }
1463
- async handleFailure(serviceName, error, request, originalHandler) {
1464
- if (!this.config.enabled) {
1465
- throw error;
1466
- }
1467
- const strategy = this.config.fallbackStrategies[serviceName];
1468
- if (!strategy) {
1469
- throw error;
1470
- }
1471
- const context = {
1472
- request,
1473
- serviceName,
1474
- error,
1475
- attemptCount: 1,
1476
- startTime: new Date
1477
- };
1478
- return this.executeFallbackStrategy(strategy, context, originalHandler);
1479
- }
1480
- async executeFallbackStrategy(strategy, context, originalHandler) {
1481
- if (context.attemptCount <= strategy.retries) {
1482
- try {
1483
- if (context.attemptCount > 1) {
1484
- const delay = this.calculateBackoffDelay(strategy.backoff, context.attemptCount);
1485
- await this.sleep(delay);
1486
- }
1487
- const timeoutPromise = new Promise((_, reject) => {
1488
- setTimeout(() => reject(new Error("Timeout")), strategy.timeout);
1489
- });
1490
- const result = await Promise.race([originalHandler(), timeoutPromise]);
1491
- this.updateMetrics(context.serviceName, true, Date.now() - context.startTime.getTime());
1492
- return result;
1493
- } catch (retryError) {
1494
- console.error(retryError);
1495
- context.attemptCount++;
1496
- if (context.attemptCount <= strategy.retries) {
1497
- return this.executeFallbackStrategy(strategy, context, originalHandler);
1498
- }
1499
- }
1500
- }
1501
- this.updateMetrics(context.serviceName, false, Date.now() - context.startTime.getTime());
1502
- switch (strategy.type) {
1503
- case "cache":
1504
- return this.handleCacheFallback(strategy, context);
1505
- case "static":
1506
- return this.handleStaticFallback(strategy, context);
1507
- case "simplified":
1508
- return this.handleSimplifiedFallback(strategy, context);
1509
- case "redirect":
1510
- return this.handleRedirectFallback(strategy, context);
1511
- case "custom":
1512
- return this.handleCustomFallback(strategy, context);
1513
- default:
1514
- throw context.error;
1515
- }
1516
- }
1517
- async handleCacheFallback(strategy, context) {
1518
- if (!strategy.cacheConfig) {
1519
- throw context.error;
1520
- }
1521
- const cacheKey = this.generateCacheKey(strategy.cacheConfig.key, context);
1522
- const cached = this.cache.get(cacheKey);
1523
- if (cached && (cached.expires > new Date || strategy.cacheConfig.staleWhileRevalidate)) {
1524
- const headers = new Headers({
1525
- "Content-Type": "application/json",
1526
- "X-Cache": "HIT",
1527
- "X-Degraded": "true",
1528
- "X-Service-Status": this.healthMonitor.getServiceHealth(context.serviceName)?.status || "unknown"
1529
- });
1530
- if (strategy.cacheConfig.staleWhileRevalidate && cached.expires <= new Date) {
1531
- headers.set("X-Cache", "STALE");
1532
- }
1533
- return new Response(JSON.stringify(cached.data), {
1534
- status: 200,
1535
- headers
1536
- });
1537
- }
1538
- throw context.error;
1539
- }
1540
- async handleStaticFallback(strategy, context) {
1541
- if (!strategy.staticResponse) {
1542
- throw context.error;
1543
- }
1544
- const headers = new Headers(strategy.staticResponse.headers || {});
1545
- headers.set("X-Degraded", "true");
1546
- headers.set("X-Fallback-Type", "static");
1547
- headers.set("X-Service-Status", this.healthMonitor.getServiceHealth(context.serviceName)?.status || "unknown");
1548
- return new Response(typeof strategy.staticResponse.body === "string" ? strategy.staticResponse.body : JSON.stringify(strategy.staticResponse.body), {
1549
- status: strategy.staticResponse.status,
1550
- headers
1551
- });
1552
- }
1553
- async handleSimplifiedFallback(strategy, context) {
1554
- const simplifiedData = {
1555
- message: "Service temporarily unavailable. Showing simplified view.",
1556
- status: "degraded",
1557
- timestamp: new Date().toISOString(),
1558
- requestId: context.request.requestId
1559
- };
1560
- return new Response(JSON.stringify(simplifiedData), {
1561
- status: 200,
1562
- headers: {
1563
- "Content-Type": "application/json",
1564
- "X-Degraded": "true",
1565
- "X-Fallback-Type": "simplified",
1566
- "X-Service-Status": this.healthMonitor.getServiceHealth(context.serviceName)?.status || "unknown"
1567
- }
1568
- });
1569
- }
1570
- async handleRedirectFallback(strategy, context) {
1571
- if (!strategy.redirectConfig) {
1572
- throw context.error;
1573
- }
1574
- return new Response(null, {
1575
- status: strategy.redirectConfig.permanent ? 301 : 302,
1576
- headers: {
1577
- Location: strategy.redirectConfig.url,
1578
- "X-Degraded": "true",
1579
- "X-Fallback-Type": "redirect"
1580
- }
1581
- });
1582
- }
1583
- async handleCustomFallback(strategy, context) {
1584
- if (!strategy.fallbackHandler) {
1585
- throw context.error;
1586
- }
1587
- try {
1588
- const response = await strategy.fallbackHandler(context.error, context.error.context);
1589
- response.headers.set("X-Degraded", "true");
1590
- response.headers.set("X-Fallback-Type", "custom");
1591
- response.headers.set("X-Service-Status", this.healthMonitor.getServiceHealth(context.serviceName)?.status || "unknown");
1592
- return response;
1593
- } catch (fallbackError) {
1594
- console.error(fallbackError);
1595
- throw context.error;
1596
- }
1597
- }
1598
- cacheResponse(serviceName, key, data, ttl) {
1599
- const strategy = this.config.fallbackStrategies[serviceName];
1600
- if (strategy?.type === "cache" && strategy.cacheConfig) {
1601
- const cacheKey = this.generateCacheKey(key, { serviceName });
1602
- this.cache.set(cacheKey, {
1603
- data,
1604
- expires: new Date(Date.now() + ttl * 1000)
1605
- });
1606
- }
1607
- }
1608
- getServiceMetrics(serviceName) {
1609
- const metrics = this.metrics.get(serviceName);
1610
- if (!metrics || metrics.requests === 0) {
1611
- return { errorRate: 0, avgResponseTime: 0, totalRequests: 0 };
1612
- }
1613
- return {
1614
- errorRate: metrics.errors / metrics.requests * 100,
1615
- avgResponseTime: metrics.totalTime / metrics.requests,
1616
- totalRequests: metrics.requests
1617
- };
1618
- }
1619
- getSystemHealth() {
1620
- const services = this.healthMonitor.getAllServiceHealth();
1621
- const unhealthyCount = services.filter((s) => s.status === "unhealthy").length;
1622
- const degradedCount = services.filter((s) => s.status === "degraded").length;
1623
- let status = "healthy";
1624
- if (unhealthyCount > 0) {
1625
- status = unhealthyCount > services.length / 2 ? "unhealthy" : "degraded";
1626
- } else if (degradedCount > 0) {
1627
- status = "degraded";
1628
- }
1629
- return { status, services };
1630
- }
1631
- calculateBackoffDelay(backoff, attempt) {
1632
- if (backoff.type === "fixed") {
1633
- return backoff.delay;
1634
- }
1635
- const delay = backoff.delay * 2 ** (attempt - 1);
1636
- return Math.min(delay, backoff.maxDelay || 30000);
1637
- }
1638
- generateCacheKey(template, context) {
1639
- return template.replace("{service}", context.serviceName).replace("{url}", context.request.url).replace("{method}", context.request.method).replace("{userId}", String(context.request.user?.id || "anonymous"));
1640
- }
1641
- updateMetrics(serviceName, success, responseTime) {
1642
- const current = this.metrics.get(serviceName) || { requests: 0, errors: 0, totalTime: 0 };
1643
- current.requests++;
1644
- current.totalTime += responseTime;
1645
- if (!success) {
1646
- current.errors++;
1647
- }
1648
- this.metrics.set(serviceName, current);
1649
- }
1650
- sleep(ms) {
1651
- return new Promise((resolve) => setTimeout(resolve, ms));
1652
- }
1653
- stop() {
1654
- this.healthMonitor.stop();
1655
- }
1656
- }
1657
- function createGracefulDegradationMiddleware(config2) {
1658
- const manager = new GracefulDegradationManager(config2);
1659
- return async (req, next) => {
1660
- try {
1661
- const response = await next();
1662
- const serviceName = req.context?.serviceName;
1663
- if (serviceName && response.ok) {
1664
- const strategy = config2.fallbackStrategies[serviceName];
1665
- if (strategy?.type === "cache" && strategy.cacheConfig) {
1666
- try {
1667
- const data = await response.clone().json();
1668
- manager.cacheResponse(serviceName, strategy.cacheConfig.key, data, strategy.cacheConfig.ttl);
1669
- } catch {}
1670
- }
1671
- }
1672
- return response;
1673
- } catch (error) {
1674
- const serviceName = req.context?.serviceName;
1675
- if (serviceName && error instanceof Error) {
1676
- const routerError = error;
1677
- try {
1678
- return await manager.handleFailure(serviceName, routerError, req, next);
1679
- } catch (fallbackError) {
1680
- console.error(fallbackError);
1681
- throw error;
1682
- }
1683
- }
1684
- throw error;
1685
- }
1686
- };
1687
- }
1688
- var DegradationStrategies = {
1689
- cacheFirst: (ttl = 3600, staleWhileRevalidate = true) => ({
1690
- type: "cache",
1691
- priority: 1,
1692
- timeout: 5000,
1693
- retries: 2,
1694
- backoff: { type: "exponential", delay: 1000, maxDelay: 5000 },
1695
- cacheConfig: {
1696
- key: "{service}:{url}:{method}",
1697
- ttl,
1698
- staleWhileRevalidate
1699
- }
1700
- }),
1701
- staticFallback: (body, status = 200) => ({
1702
- type: "static",
1703
- priority: 2,
1704
- timeout: 1000,
1705
- retries: 0,
1706
- backoff: { type: "fixed", delay: 0 },
1707
- staticResponse: {
1708
- status,
1709
- body,
1710
- headers: { "Content-Type": "application/json" }
1711
- }
1712
- }),
1713
- simplifiedView: () => ({
1714
- type: "simplified",
1715
- priority: 3,
1716
- timeout: 1000,
1717
- retries: 0,
1718
- backoff: { type: "fixed", delay: 0 }
1719
- }),
1720
- redirectToMaintenance: (url) => ({
1721
- type: "redirect",
1722
- priority: 4,
1723
- timeout: 1000,
1724
- retries: 0,
1725
- backoff: { type: "fixed", delay: 0 },
1726
- redirectConfig: {
1727
- url,
1728
- permanent: false
1729
- }
1730
- })
1731
- };
1732
-
1733
- // src/errors/error-handler.ts
1734
- class AdvancedErrorHandler {
1735
- errorReporting;
1736
- gracefulDegradation;
1737
- circuitBreakerRegistry;
1738
- config;
1739
- constructor(config2 = {}) {
1740
- this.config = {
1741
- development: false,
1742
- includeStackTrace: false,
1743
- sanitizeErrors: true,
1744
- logErrors: true,
1745
- ...config2
1746
- };
1747
- this.initializeComponents();
1748
- }
1749
- initializeComponents() {
1750
- if (this.config.errorReporting?.length) {
1751
- this.errorReporting = new ErrorReportingManager(this.config.errorReporting);
1752
- }
1753
- if (this.config.gracefulDegradation?.enabled) {
1754
- this.gracefulDegradation = new GracefulDegradationManager(this.config.gracefulDegradation);
1755
- }
1756
- if (this.config.circuitBreakers?.length) {
1757
- this.circuitBreakerRegistry = new CircuitBreakerRegistry;
1758
- for (const config2 of this.config.circuitBreakers) {
1759
- this.circuitBreakerRegistry.register(config2);
1760
- }
1761
- }
1762
- }
1763
- middleware() {
1764
- return async (req, next) => {
1765
- try {
1766
- const errorContext = {
1767
- requestId: req.requestId,
1768
- userId: req.user?.id,
1769
- traceId: req.traceId,
1770
- spanId: req.spanId,
1771
- route: req.params?.route || req.url,
1772
- method: req.method,
1773
- url: req.url,
1774
- userAgent: req.userAgent?.() || undefined,
1775
- ip: req.ip?.() || undefined,
1776
- timestamp: new Date
1777
- };
1778
- req.context = { ...req.context, errorContext };
1779
- if (this.circuitBreakerRegistry && req.context?.serviceName) {
1780
- const breaker = this.circuitBreakerRegistry.get(req.context.serviceName);
1781
- if (breaker) {
1782
- return await breaker.execute({
1783
- execute: next,
1784
- context: errorContext
1785
- });
1786
- }
1787
- }
1788
- return await next();
1789
- } catch (error) {
1790
- return this.handleError(error, req);
1791
- }
1792
- };
1793
- }
1794
- async handleError(error, req) {
1795
- const routerError = this.normalizeError(error, req);
1796
- if (this.errorReporting) {
1797
- this.errorReporting.addBreadcrumb(`Error in ${req.method} ${req.url}`, "error", {
1798
- code: routerError.code,
1799
- severity: routerError.severity,
1800
- statusCode: routerError.statusCode
1801
- });
1802
- }
1803
- if (this.config.logErrors) {
1804
- this.logError(routerError, req);
1805
- }
1806
- if (this.gracefulDegradation && req.context?.serviceName) {
1807
- try {
1808
- return await this.gracefulDegradation.handleFailure(req.context.serviceName, routerError, req, async () => {
1809
- throw routerError;
1810
- });
1811
- } catch {}
1812
- }
1813
- if (this.errorReporting) {
1814
- try {
1815
- await this.errorReporting.report(routerError, routerError.context);
1816
- } catch (reportingError) {
1817
- console.error("Failed to report error:", reportingError);
1818
- }
1819
- }
1820
- if (this.config.globalErrorHandler) {
1821
- try {
1822
- return await this.config.globalErrorHandler(routerError, req);
1823
- } catch (handlerError) {
1824
- console.error("Global error handler failed:", handlerError);
1825
- }
1826
- }
1827
- const customPage = this.config.customErrorPages?.[routerError.statusCode];
1828
- if (customPage) {
1829
- try {
1830
- if (typeof customPage === "string") {
1831
- return new Response(customPage, {
1832
- status: routerError.statusCode,
1833
- headers: { "Content-Type": "text/html" }
1834
- });
1835
- } else {
1836
- return await customPage(routerError, req);
1837
- }
1838
- } catch (pageError) {
1839
- console.error("Custom error page failed:", pageError);
1840
- }
1841
- }
1842
- return this.createErrorResponse(routerError, req);
1843
- }
1844
- normalizeError(error, req) {
1845
- const errorContext = {
1846
- requestId: req.requestId,
1847
- userId: req.user?.id,
1848
- traceId: req.traceId,
1849
- spanId: req.spanId,
1850
- route: req.params?.route || req.url,
1851
- method: req.method,
1852
- url: req.url,
1853
- userAgent: req.userAgent?.() || undefined,
1854
- ip: req.ip?.() || undefined,
1855
- timestamp: new Date,
1856
- metadata: req.context
213
+ keyName: "X-API-Key",
214
+ keyPrefix: "bun_",
215
+ ...options
1857
216
  };
1858
- if (error instanceof RouterException) {
1859
- return error;
1860
- }
1861
- if (error instanceof Error) {
1862
- if (error.name === "ValidationError") {
1863
- return ErrorFactory.validation(error.message, {}, errorContext);
1864
- }
1865
- if (error.name === "UnauthorizedError" || error.message.includes("unauthorized")) {
1866
- return ErrorFactory.authentication(error.message, errorContext);
1867
- }
1868
- if (error.name === "ForbiddenError" || error.message.includes("forbidden")) {
1869
- return ErrorFactory.authorization(error.message, [], errorContext);
1870
- }
1871
- if (error.name === "NotFoundError" || error.message.includes("not found")) {
1872
- return ErrorFactory.notFound(undefined, undefined, errorContext);
1873
- }
1874
- if (error.name === "TimeoutError" || error.message.includes("timeout")) {
1875
- return ErrorFactory.timeout(30000, error.message, errorContext);
1876
- }
1877
- return ErrorFactory.internalServer(error.message, error, errorContext);
1878
- }
1879
- if (typeof error === "string") {
1880
- return ErrorFactory.internalServer(error, undefined, errorContext);
1881
- }
1882
- return ErrorFactory.internalServer("An unknown error occurred", undefined, errorContext);
1883
217
  }
1884
- createErrorResponse(error, _req) {
1885
- const isDevelopment = this.config.development;
1886
- const includeStackTrace = this.config.includeStackTrace || isDevelopment;
1887
- const sanitizeErrors = this.config.sanitizeErrors && !isDevelopment;
1888
- let message = error.message;
1889
- if (sanitizeErrors && error.statusCode >= 500) {
1890
- message = "An internal server error occurred";
1891
- }
1892
- const errorResponse = {
1893
- error: {
1894
- code: error.code,
1895
- message,
1896
- statusCode: error.statusCode,
1897
- timestamp: error.timestamp.toISOString(),
1898
- requestId: error.context.requestId,
1899
- traceId: error.context.traceId
1900
- }
1901
- };
1902
- if (isDevelopment) {
1903
- errorResponse.error.category = error.category;
1904
- errorResponse.error.severity = error.severity;
1905
- errorResponse.error.retryable = error.retryable;
1906
- errorResponse.error.context = error.context;
1907
- }
1908
- if (includeStackTrace) {
1909
- errorResponse.error.stack = error.stack;
1910
- if (error.cause) {
1911
- errorResponse.error.cause = {
1912
- name: error.cause.name,
1913
- message: error.cause.message,
1914
- stack: error.cause.stack
1915
- };
1916
- }
1917
- }
1918
- if ("fields" in error) {
1919
- errorResponse.error.fields = error.fields;
1920
- }
1921
- if ("allowedMethods" in error) {
1922
- errorResponse.error.allowedMethods = error.allowedMethods;
1923
- }
1924
- if ("retryAfter" in error) {
1925
- errorResponse.error.retryAfter = error.retryAfter;
1926
- }
1927
- const headers = new Headers({
1928
- "Content-Type": "application/json",
1929
- "X-Error-Code": error.code,
1930
- "X-Request-ID": error.context.requestId || "",
1931
- "X-Trace-ID": error.context.traceId || ""
218
+ generateKey(owner, scopes = [], expiresIn) {
219
+ const key = `${this.options.keyPrefix}${this.randomString(32)}`;
220
+ this.keys.set(key, {
221
+ owner,
222
+ scopes,
223
+ expiresAt: expiresIn ? new Date(Date.now() + expiresIn * 1000) : undefined
1932
224
  });
1933
- if ("allowedMethods" in error) {
1934
- headers.set("Allow", error.allowedMethods.join(", "));
225
+ return key;
226
+ }
227
+ validateKey(key, requiredScopes = []) {
228
+ const keyInfo = this.keys.get(key);
229
+ if (!keyInfo) {
230
+ return false;
1935
231
  }
1936
- if ("retryAfter" in error) {
1937
- headers.set("Retry-After", error.retryAfter.toString());
232
+ if (keyInfo.expiresAt && keyInfo.expiresAt < new Date) {
233
+ return false;
1938
234
  }
1939
- if ("limit" in error) {
1940
- headers.set("X-RateLimit-Limit", error.limit.toString());
1941
- headers.set("X-RateLimit-Remaining", error.remaining.toString());
235
+ if (requiredScopes.length > 0) {
236
+ return requiredScopes.every((scope) => keyInfo.scopes.includes(scope));
1942
237
  }
1943
- return new Response(JSON.stringify(errorResponse), {
1944
- status: error.statusCode,
1945
- headers
1946
- });
238
+ return true;
1947
239
  }
1948
- logError(error, req) {
1949
- const logData = {
1950
- error: error.toJSON(),
1951
- request: {
1952
- method: req.method,
1953
- url: req.url,
1954
- headers: Object.fromEntries(req.headers.entries()),
1955
- userAgent: req.userAgent?.() || undefined,
1956
- ip: req.ip?.() || undefined,
1957
- requestId: req.requestId,
1958
- traceId: req.traceId
1959
- }
1960
- };
1961
- switch (error.severity) {
1962
- case "critical":
1963
- console.error("[CRITICAL ERROR]", logData);
1964
- break;
1965
- case "high":
1966
- console.error("[ERROR]", logData);
1967
- break;
1968
- case "medium":
1969
- console.warn("[WARNING]", logData);
1970
- break;
1971
- case "low":
1972
- console.warn("[INFO]", logData);
1973
- break;
1974
- default:
1975
- console.error("[ERROR]", logData);
1976
- }
240
+ getKeyInfo(key) {
241
+ return this.keys.get(key) || null;
1977
242
  }
1978
- getStats() {
1979
- return {
1980
- errorReporting: this.errorReporting ? {
1981
- breadcrumbs: this.errorReporting.getBreadcrumbs().length
1982
- } : undefined,
1983
- gracefulDegradation: this.gracefulDegradation ? this.gracefulDegradation.getSystemHealth() : undefined,
1984
- circuitBreakers: this.circuitBreakerRegistry ? this.circuitBreakerRegistry.getAllMetrics() : undefined
1985
- };
243
+ revokeKey(key) {
244
+ return this.keys.delete(key);
1986
245
  }
1987
- async cleanup() {
1988
- if (this.errorReporting) {
1989
- await this.errorReporting.close();
1990
- }
1991
- if (this.gracefulDegradation) {
1992
- this.gracefulDegradation.stop();
246
+ extractFromRequest(req) {
247
+ const { source, keyName } = this.options;
248
+ switch (source) {
249
+ case "header":
250
+ return req.headers.get(keyName || "X-API-Key") || null;
251
+ case "query":
252
+ return new URL(req.url).searchParams.get(keyName || "api_key") || null;
253
+ case "cookie":
254
+ if (req.cookies) {
255
+ const key = keyName || "api_key";
256
+ if (typeof req.cookies.get === "function") {
257
+ return req.cookies.get(key) || null;
258
+ }
259
+ return req.cookies[key] || null;
260
+ }
261
+ return null;
262
+ default:
263
+ return null;
1993
264
  }
1994
265
  }
1995
- }
1996
- function createAdvancedErrorHandler(config2 = {}) {
1997
- const handler = new AdvancedErrorHandler(config2);
1998
- return {
1999
- middleware: handler.middleware(),
2000
- getStats: () => handler.getStats(),
2001
- cleanup: () => handler.cleanup()
2002
- };
2003
- }
2004
- // src/request/context.ts
2005
- import { AsyncLocalStorage } from "async_hooks";
2006
- var storage = new AsyncLocalStorage;
2007
- var contextEnabled = false;
2008
- var syncCurrent = null;
2009
- function enableRequestContext() {
2010
- contextEnabled = true;
2011
- }
2012
- function runWithRequest(initial, fn) {
2013
- if (!contextEnabled) {
2014
- syncCurrent = initial;
2015
- const result = fn();
2016
- if (result instanceof Promise) {
2017
- return result.finally(() => {
2018
- syncCurrent = null;
2019
- });
266
+ randomString(length) {
267
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
268
+ let result = "";
269
+ for (let i = 0;i < length; i++) {
270
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
2020
271
  }
2021
- syncCurrent = null;
2022
272
  return result;
2023
273
  }
2024
- return storage.run({ current: initial }, fn);
2025
274
  }
2026
- function setCurrentRequest(req) {
2027
- const store = storage.getStore();
2028
- if (store) {
2029
- store.current = req;
2030
- return;
275
+
276
+ class OAuth2Helper {
277
+ config;
278
+ constructor(config2) {
279
+ this.config = config2;
2031
280
  }
2032
- if (syncCurrent !== null) {
2033
- syncCurrent = req;
281
+ getAuthorizationUrl(additionalParams = {}) {
282
+ const params = new URLSearchParams({
283
+ client_id: this.config.clientId,
284
+ redirect_uri: this.config.redirectUri,
285
+ response_type: "code",
286
+ ...additionalParams
287
+ });
288
+ if (this.config.scope) {
289
+ params.set("scope", this.config.scope);
290
+ }
291
+ if (this.config.state) {
292
+ params.set("state", this.config.state);
293
+ }
294
+ return `${this.config.authorizeUrl}?${params.toString()}`;
2034
295
  }
2035
- }
2036
- function getCurrentRequest() {
2037
- if (!contextEnabled) {
2038
- contextEnabled = true;
2039
- return syncCurrent ?? undefined;
296
+ async exchangeCodeForToken(code) {
297
+ const params = new URLSearchParams({
298
+ grant_type: "authorization_code",
299
+ code,
300
+ redirect_uri: this.config.redirectUri,
301
+ client_id: this.config.clientId,
302
+ client_secret: this.config.clientSecret
303
+ });
304
+ const response = await fetch(this.config.tokenUrl, {
305
+ method: "POST",
306
+ headers: {
307
+ "Content-Type": "application/x-www-form-urlencoded"
308
+ },
309
+ body: params.toString()
310
+ });
311
+ if (!response.ok) {
312
+ throw new Error(`OAuth token exchange failed: ${response.status} ${response.statusText}`);
313
+ }
314
+ return await response.json();
2040
315
  }
2041
- return storage.getStore()?.current ?? syncCurrent ?? undefined;
2042
- }
2043
- function request() {
2044
- const req = getCurrentRequest();
2045
- if (!req) {
2046
- throw new Error("request() called outside a request scope. It can only be invoked from within a route handler or middleware.");
316
+ async refreshToken(refreshToken) {
317
+ const params = new URLSearchParams({
318
+ grant_type: "refresh_token",
319
+ refresh_token: refreshToken,
320
+ client_id: this.config.clientId,
321
+ client_secret: this.config.clientSecret
322
+ });
323
+ const response = await fetch(this.config.tokenUrl, {
324
+ method: "POST",
325
+ headers: {
326
+ "Content-Type": "application/x-www-form-urlencoded"
327
+ },
328
+ body: params.toString()
329
+ });
330
+ if (!response.ok) {
331
+ throw new Error(`OAuth token refresh failed: ${response.status} ${response.statusText}`);
332
+ }
333
+ return await response.json();
2047
334
  }
2048
- return req;
2049
335
  }
2050
336
  // src/response/response-factory.ts
2051
337
  var response = {
@@ -2143,8 +429,8 @@ var response = {
2143
429
  redirectTemporary: (url) => {
2144
430
  return response.redirect(url, 302);
2145
431
  },
2146
- back: (request2, fallback = "/") => {
2147
- const referer = request2.headers.get("referer") || fallback;
432
+ back: (request, fallback = "/") => {
433
+ const referer = request.headers.get("referer") || fallback;
2148
434
  return response.redirect(referer);
2149
435
  },
2150
436
  view: (html, status = 200, headers = {}) => {
@@ -2389,7 +675,7 @@ class MiddlewarePipeline {
2389
675
  }
2390
676
  this.compiledPipelines.set(routeKey, compiled);
2391
677
  }
2392
- async execute(routeKey, request2, finalHandler) {
678
+ async execute(routeKey, request, finalHandler) {
2393
679
  const startTime = performance.now();
2394
680
  this.stats.totalExecutions++;
2395
681
  const pipeline = this.compiledPipelines.get(routeKey);
@@ -2399,7 +685,7 @@ class MiddlewarePipeline {
2399
685
  }
2400
686
  this.stats.cacheHits++;
2401
687
  const context = {
2402
- request: request2,
688
+ request,
2403
689
  dependencies: new Map,
2404
690
  metadata: {}
2405
691
  };
@@ -2414,7 +700,7 @@ class MiddlewarePipeline {
2414
700
  this.stats.skippedMiddleware++;
2415
701
  return await next();
2416
702
  }
2417
- const result2 = await middleware2.handler(request2, next);
703
+ const result2 = await middleware2.handler(request, next);
2418
704
  if (result2 instanceof Response && middleware2.canShortCircuit) {
2419
705
  this.stats.shortCircuits++;
2420
706
  return result2;
@@ -3492,12 +1778,12 @@ class StreamHandler {
3492
1778
  }
3493
1779
  }
3494
1780
  class FileStreamHandler {
3495
- static async streamFile(filePath, request2, options = {}) {
1781
+ static async streamFile(filePath, request, options = {}) {
3496
1782
  const { contentType, enableRanges = true, chunkSize: _chunkSize = 64 * 1024 } = options;
3497
1783
  try {
3498
1784
  const file = Bun.file(filePath);
3499
1785
  const fileSize = file.size;
3500
- const range = enableRanges ? request2.headers.get("range") : null;
1786
+ const range = enableRanges ? request.headers.get("range") : null;
3501
1787
  if (range && enableRanges) {
3502
1788
  return FileStreamHandler.handleRangeRequest(file, range, contentType);
3503
1789
  }
@@ -3576,8 +1862,8 @@ function closeReadableStreamAfterError(controller, error) {
3576
1862
  function registerFileStreaming(RouterClass) {
3577
1863
  Object.defineProperties(RouterClass.prototype, {
3578
1864
  streamFile: {
3579
- value(filePath, request2, options) {
3580
- return FileStreamHandler.streamFile(filePath, request2, options);
1865
+ value(filePath, request, options) {
1866
+ return FileStreamHandler.streamFile(filePath, request, options);
3581
1867
  },
3582
1868
  writable: true,
3583
1869
  configurable: true
@@ -4932,7 +3218,6 @@ var SubdomainPatterns = {
4932
3218
  };
4933
3219
 
4934
3220
  // src/url.ts
4935
- import process3 from "process";
4936
3221
  var namedRouteRegistry = new Map;
4937
3222
  function registerNamedRoute(name, path) {
4938
3223
  namedRouteRegistry.set(name, path);
@@ -4940,54 +3225,6 @@ function registerNamedRoute(name, path) {
4940
3225
  function getNamedRoutePath(name) {
4941
3226
  return namedRouteRegistry.get(name);
4942
3227
  }
4943
- function getNamedRoutes() {
4944
- return new Map(namedRouteRegistry);
4945
- }
4946
- function clearNamedRoutes() {
4947
- namedRouteRegistry.clear();
4948
- }
4949
- function url(name, params = {}, options = {}) {
4950
- const path = namedRouteRegistry.get(name);
4951
- if (!path) {
4952
- const known = [...namedRouteRegistry.keys()].sort().join(", ") || "(none registered)";
4953
- throw new Error(`Named route '${name}' is not defined. Known routes: ${known}`);
4954
- }
4955
- const consumed = new Set;
4956
- let resolved = path;
4957
- for (const [key, value] of Object.entries(params)) {
4958
- const required = `{${key}}`;
4959
- const optional = `{${key}?}`;
4960
- const encoded = encodeURIComponent(String(value));
4961
- if (resolved.includes(required)) {
4962
- resolved = resolved.replaceAll(required, encoded);
4963
- consumed.add(key);
4964
- } else if (resolved.includes(optional)) {
4965
- resolved = resolved.replaceAll(optional, encoded);
4966
- consumed.add(key);
4967
- }
4968
- }
4969
- if (resolved.includes("?}")) {
4970
- resolved = resolved.replace(/\{[^}]+\?\}/g, "").replace(/\/{2,}/g, "/");
4971
- if (resolved.length > 1 && resolved.endsWith("/"))
4972
- resolved = resolved.slice(0, -1);
4973
- }
4974
- const missing = resolved.match(/\{([^}?]+)\}/g);
4975
- if (missing && missing.length > 0)
4976
- throw new Error(`Missing required params for route '${name}': ${missing.join(", ")}`);
4977
- const leftover = Object.entries(params).filter(([k]) => !consumed.has(k));
4978
- if (leftover.length > 0) {
4979
- const qs = new URLSearchParams;
4980
- for (const [k, v] of leftover)
4981
- qs.set(k, String(v));
4982
- resolved += `?${qs.toString()}`;
4983
- }
4984
- if (options.absolute) {
4985
- const base = (options.base ?? process3.env.APP_URL ?? "http://localhost").replace(/\/$/, "");
4986
- const prefixed = base.startsWith("http") ? base : `https://${base}`;
4987
- return `${prefixed}${resolved.startsWith("/") ? resolved : `/${resolved}`}`;
4988
- }
4989
- return resolved;
4990
- }
4991
3228
 
4992
3229
  // src/utils.ts
4993
3230
  import { join as join3 } from "path";
@@ -5541,24 +3778,24 @@ class FluentRouter {
5541
3778
  getRoutes() {
5542
3779
  return [...this.routes, ...this.subdomainRouter.getAllRoutes()];
5543
3780
  }
5544
- async handle(request2) {
5545
- const url2 = new URL(request2.url);
5546
- const domain = url2.hostname;
3781
+ async handle(request) {
3782
+ const url = new URL(request.url);
3783
+ const domain = url.hostname;
5547
3784
  const match = this.subdomainRouter.findDomainGroup(domain);
5548
3785
  if (match) {
5549
- const enhancedReq = request2;
3786
+ const enhancedReq = request;
5550
3787
  enhancedReq.domainParams = match.parameters;
5551
3788
  for (const route of match.group.getRoutes()) {
5552
- const params = this.matchRouteParams(route, request2.method, url2.pathname);
3789
+ const params = this.matchRouteParams(route, request.method, url.pathname);
5553
3790
  if (params) {
5554
3791
  return await this.executeRoute(route, enhancedReq, params);
5555
3792
  }
5556
3793
  }
5557
3794
  }
5558
3795
  for (const route of this.routes) {
5559
- const params = this.matchRouteParams(route, request2.method, url2.pathname);
3796
+ const params = this.matchRouteParams(route, request.method, url.pathname);
5560
3797
  if (params) {
5561
- return await this.executeRoute(route, request2, params);
3798
+ return await this.executeRoute(route, request, params);
5562
3799
  }
5563
3800
  }
5564
3801
  return null;
@@ -5570,11 +3807,11 @@ class FluentRouter {
5570
3807
  const params = {};
5571
3808
  return matchPath(route.path, pathname, params) ? params : null;
5572
3809
  }
5573
- async executeRoute(route, request2, params) {
5574
- request2.params = params;
3810
+ async executeRoute(route, request, params) {
3811
+ request.params = params;
5575
3812
  const allMiddleware = [...this.globalMiddleware];
5576
3813
  for (const conditional of this.conditionalMiddleware) {
5577
- if (await conditional.condition(request2)) {
3814
+ if (await conditional.condition(request)) {
5578
3815
  allMiddleware.push(...conditional.middleware);
5579
3816
  }
5580
3817
  }
@@ -5583,10 +3820,10 @@ class FluentRouter {
5583
3820
  const next = async () => {
5584
3821
  if (index < allMiddleware.length) {
5585
3822
  const middleware2 = allMiddleware[index++];
5586
- const result = await middleware2(request2, next);
3823
+ const result = await middleware2(request, next);
5587
3824
  return result ?? new Response(null);
5588
3825
  }
5589
- return await route.handler(request2);
3826
+ return await route.handler(request);
5590
3827
  };
5591
3828
  return await next();
5592
3829
  }
@@ -5624,27 +3861,27 @@ var RouteFactory = {
5624
3861
  };
5625
3862
  var RouterUtils = {
5626
3863
  route: (name, params = {}, query = {}) => {
5627
- let url2 = getNamedRoutePath(name) ?? `/${name}`;
3864
+ let url = getNamedRoutePath(name) ?? `/${name}`;
5628
3865
  for (const [key, value] of Object.entries(params)) {
5629
3866
  const encoded = encodeURIComponent(value);
5630
- url2 = url2.replace(`{${key}}`, encoded);
5631
- url2 = url2.replace(`{${key}?}`, encoded);
3867
+ url = url.replace(`{${key}}`, encoded);
3868
+ url = url.replace(`{${key}?}`, encoded);
5632
3869
  }
5633
- if (url2.includes("?}")) {
5634
- url2 = url2.replace(/\{[^}]+\?\}/g, "").replace(/\/{2,}/g, "/");
5635
- if (url2.length > 1 && url2.endsWith("/"))
5636
- url2 = url2.slice(0, -1);
3870
+ if (url.includes("?}")) {
3871
+ url = url.replace(/\{[^}]+\?\}/g, "").replace(/\/{2,}/g, "/");
3872
+ if (url.length > 1 && url.endsWith("/"))
3873
+ url = url.slice(0, -1);
5637
3874
  }
5638
3875
  const queryString = new URLSearchParams(query).toString();
5639
3876
  if (queryString) {
5640
- url2 += `?${queryString}`;
3877
+ url += `?${queryString}`;
5641
3878
  }
5642
- return url2;
3879
+ return url;
5643
3880
  },
5644
- redirect: (url2, status = 302) => {
3881
+ redirect: (url, status = 302) => {
5645
3882
  return new Response(null, {
5646
3883
  status,
5647
- headers: { Location: url2 }
3884
+ headers: { Location: url }
5648
3885
  });
5649
3886
  },
5650
3887
  json: (data, status = 200) => {
@@ -5842,10 +4079,10 @@ function registerHttpMethods(RouterClass) {
5842
4079
  route.constraints = constraints;
5843
4080
  }
5844
4081
  route.pattern = {
5845
- exec: (url2) => {
4082
+ exec: (url) => {
5846
4083
  const params = {};
5847
4084
  const constraintsRecord = route.constraints && !Array.isArray(route.constraints) ? route.constraints : undefined;
5848
- const isMatch = matchPath(routePath, url2.pathname, params, constraintsRecord);
4085
+ const isMatch = matchPath(routePath, url.pathname, params, constraintsRecord);
5849
4086
  if (!isMatch) {
5850
4087
  return null;
5851
4088
  }
@@ -5972,18 +4209,18 @@ function registerHttpMethods(RouterClass) {
5972
4209
  if (!route) {
5973
4210
  throw new Error(`Route with name "${name}" not found`);
5974
4211
  }
5975
- let url2 = route.path;
4212
+ let url = route.path;
5976
4213
  for (const [param, value] of Object.entries(params)) {
5977
- url2 = url2.replace(`{${param}}`, encodeURIComponent(value));
5978
- url2 = url2.replace(`{${param}?}`, encodeURIComponent(value));
4214
+ url = url.replace(`{${param}}`, encodeURIComponent(value));
4215
+ url = url.replace(`{${param}?}`, encodeURIComponent(value));
5979
4216
  }
5980
- if (url2.includes("{")) {
5981
- url2 = url2.replace(/\{[^}]+\?\}/g, "").replace(/\/{2,}/g, "/");
5982
- if (url2.length > 1 && url2.endsWith("/")) {
5983
- url2 = url2.slice(0, -1);
4217
+ if (url.includes("{")) {
4218
+ url = url.replace(/\{[^}]+\?\}/g, "").replace(/\/{2,}/g, "/");
4219
+ if (url.length > 1 && url.endsWith("/")) {
4220
+ url = url.slice(0, -1);
5984
4221
  }
5985
4222
  }
5986
- return url2;
4223
+ return url;
5987
4224
  },
5988
4225
  writable: true,
5989
4226
  configurable: true
@@ -6001,9 +4238,9 @@ function registerHttpMethods(RouterClass) {
6001
4238
  function registerRedirectMethods(RouterClass) {
6002
4239
  Object.defineProperties(RouterClass.prototype, {
6003
4240
  redirect: {
6004
- value(url2, status = 302) {
4241
+ value(url, status = 302) {
6005
4242
  const headers = new Headers;
6006
- headers.set("Location", url2);
4243
+ headers.set("Location", url);
6007
4244
  return new Response(null, {
6008
4245
  status,
6009
4246
  headers
@@ -6013,8 +4250,8 @@ function registerRedirectMethods(RouterClass) {
6013
4250
  configurable: true
6014
4251
  },
6015
4252
  permanentRedirect: {
6016
- value(url2) {
6017
- return this.redirect(url2, 301);
4253
+ value(url) {
4254
+ return this.redirect(url, 301);
6018
4255
  },
6019
4256
  writable: true,
6020
4257
  configurable: true
@@ -7012,8 +5249,8 @@ class RouteCompiler {
7012
5249
  }
7013
5250
  const pattern = this.convertToURLPattern(route.path, route.constraints && !Array.isArray(route.constraints) ? route.constraints : undefined);
7014
5251
  const urlPatternAdapter = {
7015
- exec: (url2) => {
7016
- const pathname = url2.pathname;
5252
+ exec: (url) => {
5253
+ const pathname = url.pathname;
7017
5254
  const result = pattern.exec(pathname);
7018
5255
  if (!result)
7019
5256
  return null;
@@ -7384,8 +5621,8 @@ function registerOptimizedRouteMatching(RouterClass) {
7384
5621
  },
7385
5622
  fallbackMatchRoute: {
7386
5623
  value(path, method, domain) {
7387
- const url2 = new URL(path, "http://localhost");
7388
- const cacheKey = `${domain || ""}:${method}:${url2.pathname}`;
5624
+ const url = new URL(path, "http://localhost");
5625
+ const cacheKey = `${domain || ""}:${method}:${url.pathname}`;
7389
5626
  if (this.routeCache.has(cacheKey)) {
7390
5627
  return this.routeCache.get(cacheKey);
7391
5628
  }
@@ -7393,7 +5630,7 @@ function registerOptimizedRouteMatching(RouterClass) {
7393
5630
  this.routeCache.clear();
7394
5631
  }
7395
5632
  if (this.staticRoutes.has(method)) {
7396
- const staticRoute = this.staticRoutes.get(method).get(url2.pathname);
5633
+ const staticRoute = this.staticRoutes.get(method).get(url.pathname);
7397
5634
  if (staticRoute && (!domain || !staticRoute.domain || staticRoute.domain === domain)) {
7398
5635
  const result = {
7399
5636
  route: staticRoute,
@@ -7406,7 +5643,7 @@ function registerOptimizedRouteMatching(RouterClass) {
7406
5643
  const potentialRoutes = domain && this.domains[domain] ? this.domains[domain] : this.routes;
7407
5644
  const methodRoutes = potentialRoutes.filter((route) => route.method === method);
7408
5645
  for (const route of methodRoutes) {
7409
- if (route.path === url2.pathname) {
5646
+ if (route.path === url.pathname) {
7410
5647
  const result = {
7411
5648
  route,
7412
5649
  params: {}
@@ -7417,7 +5654,7 @@ function registerOptimizedRouteMatching(RouterClass) {
7417
5654
  }
7418
5655
  for (const route of methodRoutes) {
7419
5656
  if (route.pattern) {
7420
- const match = route.pattern.exec(url2);
5657
+ const match = route.pattern.exec(url);
7421
5658
  if (match) {
7422
5659
  const result = {
7423
5660
  route,
@@ -7435,10 +5672,10 @@ function registerOptimizedRouteMatching(RouterClass) {
7435
5672
  continue;
7436
5673
  }
7437
5674
  const basePath = route.path.slice(0, -1);
7438
- if (url2.pathname.startsWith(basePath)) {
5675
+ if (url.pathname.startsWith(basePath)) {
7439
5676
  const result = {
7440
5677
  route,
7441
- params: { wildcard: url2.pathname.slice(basePath.length) }
5678
+ params: { wildcard: url.pathname.slice(basePath.length) }
7442
5679
  };
7443
5680
  this.routeCache.set(cacheKey, result);
7444
5681
  return result;
@@ -7560,10 +5797,10 @@ function registerRouteBuilding(RouterClass) {
7560
5797
  } catch {}
7561
5798
  }
7562
5799
  bunRoutes[path] = async (req) => {
7563
- const url2 = new URL(req.url);
5800
+ const url = new URL(req.url);
7564
5801
  const params = {};
7565
5802
  const constraintsRecord = route.constraints && !Array.isArray(route.constraints) ? route.constraints : undefined;
7566
- matchPath(path, url2.pathname, params, constraintsRecord);
5803
+ matchPath(path, url.pathname, params, constraintsRecord);
7567
5804
  const enhancedReq = this.enhanceRequest(req, params);
7568
5805
  const routeHandlerMiddleware = async (req2, _next) => await this.resolveHandler(route.handler, req2);
7569
5806
  const middlewareStack = [...this.globalMiddleware, ...route.middleware, routeHandlerMiddleware];
@@ -7575,10 +5812,10 @@ function registerRouteBuilding(RouterClass) {
7575
5812
  for (const route of routes) {
7576
5813
  const method = route.method;
7577
5814
  methodHandlers[method] = async (req) => {
7578
- const url2 = new URL(req.url);
5815
+ const url = new URL(req.url);
7579
5816
  const params = {};
7580
5817
  const constraintsRecord = route.constraints && !Array.isArray(route.constraints) ? route.constraints : undefined;
7581
- matchPath(path, url2.pathname, params, constraintsRecord);
5818
+ matchPath(path, url.pathname, params, constraintsRecord);
7582
5819
  const enhancedReq = this.enhanceRequest(req, params);
7583
5820
  const routeHandlerMiddleware = async (req2, _next) => await this.resolveHandler(route.handler, req2);
7584
5821
  const middlewareStack = [...this.globalMiddleware, ...route.middleware, routeHandlerMiddleware];
@@ -7672,13 +5909,13 @@ function registerRouteMatching(RouterClass) {
7672
5909
  Object.defineProperties(RouterClass.prototype, {
7673
5910
  matchRoute: {
7674
5911
  value(path, method, domain) {
7675
- const url2 = new URL(path, "http://localhost");
7676
- const cacheKey = `${domain || ""}:${method}:${url2.pathname}`;
5912
+ const url = new URL(path, "http://localhost");
5913
+ const cacheKey = `${domain || ""}:${method}:${url.pathname}`;
7677
5914
  if (this.routeCache.has(cacheKey)) {
7678
5915
  return this.routeCache.get(cacheKey);
7679
5916
  }
7680
5917
  if (this.staticRoutes.has(method)) {
7681
- const staticRoute = this.staticRoutes.get(method).get(url2.pathname);
5918
+ const staticRoute = this.staticRoutes.get(method).get(url.pathname);
7682
5919
  if (staticRoute && (!domain || !staticRoute.domain || staticRoute.domain === domain)) {
7683
5920
  const result = {
7684
5921
  route: staticRoute,
@@ -7691,7 +5928,7 @@ function registerRouteMatching(RouterClass) {
7691
5928
  const potentialRoutes = domain && this.domains[domain] ? this.domains[domain] : this.routes;
7692
5929
  const methodRoutes = potentialRoutes.filter((route) => route.method === method);
7693
5930
  for (const route of methodRoutes) {
7694
- if (route.path === url2.pathname) {
5931
+ if (route.path === url.pathname) {
7695
5932
  const result = {
7696
5933
  route,
7697
5934
  params: {}
@@ -7702,7 +5939,7 @@ function registerRouteMatching(RouterClass) {
7702
5939
  }
7703
5940
  for (const route of methodRoutes) {
7704
5941
  if (route.pattern) {
7705
- const match = route.pattern.exec(url2);
5942
+ const match = route.pattern.exec(url);
7706
5943
  if (match) {
7707
5944
  const result = {
7708
5945
  route,
@@ -7717,11 +5954,11 @@ function registerRouteMatching(RouterClass) {
7717
5954
  const wildcardRoutes = this.domains[domain].filter((route) => route.method === method && route.path.endsWith("*"));
7718
5955
  for (const route of wildcardRoutes) {
7719
5956
  const basePath = route.path.slice(0, -1);
7720
- if (url2.pathname.startsWith(basePath)) {
5957
+ if (url.pathname.startsWith(basePath)) {
7721
5958
  const result = {
7722
5959
  route,
7723
5960
  params: {
7724
- wildcard: url2.pathname.slice(basePath.length)
5961
+ wildcard: url.pathname.slice(basePath.length)
7725
5962
  }
7726
5963
  };
7727
5964
  this.routeCache.set(cacheKey, result);
@@ -7732,11 +5969,11 @@ function registerRouteMatching(RouterClass) {
7732
5969
  const globalWildcardRoutes = this.routes.filter((route) => route.method === method && route.path.endsWith("*") && (!domain || !route.domain));
7733
5970
  for (const route of globalWildcardRoutes) {
7734
5971
  const basePath = route.path.slice(0, -1);
7735
- if (url2.pathname.startsWith(basePath)) {
5972
+ if (url.pathname.startsWith(basePath)) {
7736
5973
  const result = {
7737
5974
  route,
7738
5975
  params: {
7739
- wildcard: url2.pathname.slice(basePath.length)
5976
+ wildcard: url.pathname.slice(basePath.length)
7740
5977
  }
7741
5978
  };
7742
5979
  this.routeCache.set(cacheKey, result);
@@ -7837,9 +6074,9 @@ function registerRouteMatching(RouterClass) {
7837
6074
  const routePath = lastRoute.path;
7838
6075
  const constraintsRecord = lastRoute.constraints && !Array.isArray(lastRoute.constraints) ? lastRoute.constraints : undefined;
7839
6076
  lastRoute.pattern = {
7840
- exec: (url2) => {
6077
+ exec: (url) => {
7841
6078
  const params2 = {};
7842
- const isMatch = matchPath(routePath, url2.pathname, params2, constraintsRecord);
6079
+ const isMatch = matchPath(routePath, url.pathname, params2, constraintsRecord);
7843
6080
  if (!isMatch) {
7844
6081
  return null;
7845
6082
  }
@@ -8090,10 +6327,10 @@ class Router {
8090
6327
  route.constraints = constraints;
8091
6328
  }
8092
6329
  route.pattern = {
8093
- exec: (url2) => {
6330
+ exec: (url) => {
8094
6331
  const params = {};
8095
6332
  const constraintsRecord = route.constraints && !Array.isArray(route.constraints) ? route.constraints : undefined;
8096
- const isMatch = matchPath(routePath, url2.pathname, params, constraintsRecord);
6333
+ const isMatch = matchPath(routePath, url.pathname, params, constraintsRecord);
8097
6334
  if (!isMatch) {
8098
6335
  return null;
8099
6336
  }
@@ -8174,27 +6411,27 @@ class Router {
8174
6411
  if (!route) {
8175
6412
  throw new Error(`Route with name "${name}" not found`);
8176
6413
  }
8177
- let url2 = route.path;
6414
+ let url = route.path;
8178
6415
  for (const [param, value] of Object.entries(params)) {
8179
- url2 = url2.replace(`{${param}}`, encodeURIComponent(value));
8180
- url2 = url2.replace(`{${param}?}`, encodeURIComponent(value));
6416
+ url = url.replace(`{${param}}`, encodeURIComponent(value));
6417
+ url = url.replace(`{${param}?}`, encodeURIComponent(value));
8181
6418
  }
8182
- return url2;
6419
+ return url;
8183
6420
  }
8184
6421
  onError(handler) {
8185
6422
  this.errorHandler = handler;
8186
6423
  return this;
8187
6424
  }
8188
- redirect(url2, status = 302) {
6425
+ redirect(url, status = 302) {
8189
6426
  const headers = new Headers;
8190
- headers.set("Location", url2);
6427
+ headers.set("Location", url);
8191
6428
  return new Response(null, {
8192
6429
  status,
8193
6430
  headers
8194
6431
  });
8195
6432
  }
8196
- permanentRedirect(url2) {
8197
- return this.redirect(url2, 301);
6433
+ permanentRedirect(url) {
6434
+ return this.redirect(url, 301);
8198
6435
  }
8199
6436
  redirectRoute(from, to, status = 302) {
8200
6437
  this.get(from, (_req) => {
@@ -8221,13 +6458,13 @@ class Router {
8221
6458
  }
8222
6459
  async handleRequest(req) {
8223
6460
  try {
8224
- const url2 = new URL(req.url);
6461
+ const url = new URL(req.url);
8225
6462
  if (this.config.verbose) {
8226
- console.log(`${req.method} ${url2.pathname}`);
6463
+ console.log(`${req.method} ${url.pathname}`);
8227
6464
  }
8228
6465
  if (req.method === "OPTIONS") {
8229
- const hostname2 = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
8230
- const optionsMatch = this.matchRoute(url2.pathname, "OPTIONS", hostname2);
6466
+ const hostname2 = url.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
6467
+ const optionsMatch = this.matchRoute(url.pathname, "OPTIONS", hostname2);
8231
6468
  if (!optionsMatch) {
8232
6469
  return new Response(null, {
8233
6470
  status: 204,
@@ -8240,8 +6477,8 @@ class Router {
8240
6477
  });
8241
6478
  }
8242
6479
  }
8243
- const hostname = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
8244
- const match = this.matchRoute(url2.pathname, req.method, hostname);
6480
+ const hostname = url.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
6481
+ const match = this.matchRoute(url.pathname, req.method, hostname);
8245
6482
  const enhancedReq = this.enhanceRequest(req, match?.params || {});
8246
6483
  if (match) {
8247
6484
  enhancedReq.route = match.route;
@@ -8259,12 +6496,12 @@ class Router {
8259
6496
  }
8260
6497
  return new Response("No response from middleware chain", { status: 500 });
8261
6498
  }
8262
- const allowedMethods = this.getAllowedMethods(url2.pathname, hostname);
6499
+ const allowedMethods = this.getAllowedMethods(url.pathname, hostname);
8263
6500
  if (allowedMethods.length > 0) {
8264
6501
  const methodNotAllowedHandler = async (_req, _next) => {
8265
6502
  return new Response(JSON.stringify({
8266
6503
  error: "Method Not Allowed",
8267
- path: url2.pathname,
6504
+ path: url.pathname,
8268
6505
  method: req.method,
8269
6506
  allowed: allowedMethods
8270
6507
  }), {
@@ -8291,7 +6528,7 @@ class Router {
8291
6528
  }
8292
6529
  return new Response(JSON.stringify({
8293
6530
  error: "Not Found",
8294
- path: url2.pathname,
6531
+ path: url.pathname,
8295
6532
  method: req.method
8296
6533
  }), {
8297
6534
  status: 404,
@@ -8310,7 +6547,7 @@ class Router {
8310
6547
  }
8311
6548
  return new Response(JSON.stringify({
8312
6549
  error: "Not Found",
8313
- path: url2.pathname,
6550
+ path: url.pathname,
8314
6551
  method: req.method
8315
6552
  }), {
8316
6553
  status: 404,
@@ -8353,15 +6590,15 @@ class Router {
8353
6590
  }
8354
6591
  }
8355
6592
  getAllowedMethods(path, domain) {
8356
- const url2 = new URL(path, "http://localhost");
8357
- const cacheKey = `${domain || ""}:${url2.pathname}`;
6593
+ const url = new URL(path, "http://localhost");
6594
+ const cacheKey = `${domain || ""}:${url.pathname}`;
8358
6595
  const cached = this._allowedMethodsCache.get(cacheKey);
8359
6596
  if (cached) {
8360
6597
  return cached;
8361
6598
  }
8362
6599
  const methods = new Set;
8363
6600
  for (const [method, routesByPath] of this.staticRoutes) {
8364
- const staticRoute = routesByPath.get(url2.pathname);
6601
+ const staticRoute = routesByPath.get(url.pathname);
8365
6602
  if (staticRoute && (!domain || !staticRoute.domain || staticRoute.domain === domain)) {
8366
6603
  methods.add(method);
8367
6604
  }
@@ -8371,15 +6608,15 @@ class Router {
8371
6608
  if (methods.has(route.method)) {
8372
6609
  continue;
8373
6610
  }
8374
- if (route.path === url2.pathname) {
6611
+ if (route.path === url.pathname) {
8375
6612
  methods.add(route.method);
8376
6613
  continue;
8377
6614
  }
8378
- if (route.pattern && route.pattern.exec(url2)) {
6615
+ if (route.pattern && route.pattern.exec(url)) {
8379
6616
  methods.add(route.method);
8380
6617
  continue;
8381
6618
  }
8382
- if (route.path.endsWith("*") && url2.pathname.startsWith(route.path.slice(0, -1))) {
6619
+ if (route.path.endsWith("*") && url.pathname.startsWith(route.path.slice(0, -1))) {
8383
6620
  methods.add(route.method);
8384
6621
  }
8385
6622
  }
@@ -8394,13 +6631,13 @@ class Router {
8394
6631
  return result;
8395
6632
  }
8396
6633
  matchRoute(path, method, domain) {
8397
- const url2 = new URL(path, "http://localhost");
8398
- const cacheKey = `${domain || ""}:${method}:${url2.pathname}`;
6634
+ const url = new URL(path, "http://localhost");
6635
+ const cacheKey = `${domain || ""}:${method}:${url.pathname}`;
8399
6636
  if (this.routeCache.has(cacheKey)) {
8400
6637
  return this.routeCache.get(cacheKey);
8401
6638
  }
8402
6639
  if (this.staticRoutes.has(method)) {
8403
- const staticRoute = this.staticRoutes.get(method).get(url2.pathname);
6640
+ const staticRoute = this.staticRoutes.get(method).get(url.pathname);
8404
6641
  if (staticRoute && (!domain || !staticRoute.domain || staticRoute.domain === domain)) {
8405
6642
  const result = {
8406
6643
  route: staticRoute,
@@ -8413,7 +6650,7 @@ class Router {
8413
6650
  const potentialRoutes = domain && this.domains[domain] ? this.domains[domain] : this.routes;
8414
6651
  const methodRoutes = potentialRoutes.filter((route) => route.method === method);
8415
6652
  for (const route of methodRoutes) {
8416
- if (route.path === url2.pathname) {
6653
+ if (route.path === url.pathname) {
8417
6654
  const result = {
8418
6655
  route,
8419
6656
  params: {}
@@ -8424,7 +6661,7 @@ class Router {
8424
6661
  }
8425
6662
  for (const route of methodRoutes) {
8426
6663
  if (route.pattern) {
8427
- const match = route.pattern.exec(url2);
6664
+ const match = route.pattern.exec(url);
8428
6665
  if (match) {
8429
6666
  const result = {
8430
6667
  route,
@@ -8439,11 +6676,11 @@ class Router {
8439
6676
  const wildcardRoutes = this.domains[domain].filter((route) => route.method === method && route.path.endsWith("*"));
8440
6677
  for (const route of wildcardRoutes) {
8441
6678
  const basePath = route.path.slice(0, -1);
8442
- if (url2.pathname.startsWith(basePath)) {
6679
+ if (url.pathname.startsWith(basePath)) {
8443
6680
  const result = {
8444
6681
  route,
8445
6682
  params: {
8446
- wildcard: url2.pathname.slice(basePath.length)
6683
+ wildcard: url.pathname.slice(basePath.length)
8447
6684
  }
8448
6685
  };
8449
6686
  this.routeCache.set(cacheKey, result);
@@ -8454,11 +6691,11 @@ class Router {
8454
6691
  const globalWildcardRoutes = this.routes.filter((route) => route.method === method && route.path.endsWith("*") && (!domain || !route.domain));
8455
6692
  for (const route of globalWildcardRoutes) {
8456
6693
  const basePath = route.path.slice(0, -1);
8457
- if (url2.pathname.startsWith(basePath)) {
6694
+ if (url.pathname.startsWith(basePath)) {
8458
6695
  const result = {
8459
6696
  route,
8460
6697
  params: {
8461
- wildcard: url2.pathname.slice(basePath.length)
6698
+ wildcard: url.pathname.slice(basePath.length)
8462
6699
  }
8463
6700
  };
8464
6701
  this.routeCache.set(cacheKey, result);
@@ -8487,9 +6724,9 @@ class Router {
8487
6724
  }
8488
6725
  return parsedCookies;
8489
6726
  };
8490
- const url2 = new URL(req.url);
6727
+ const url = new URL(req.url);
8491
6728
  const query = {};
8492
- url2.searchParams.forEach((value, key) => {
6729
+ url.searchParams.forEach((value, key) => {
8493
6730
  query[key] = value;
8494
6731
  });
8495
6732
  const enhancedReq = {
@@ -8866,7 +7103,7 @@ class Router {
8866
7103
  }
8867
7104
  return this;
8868
7105
  }
8869
- async executeMiddleware(middleware2, request2, handler) {
7106
+ async executeMiddleware(middleware2, request, handler) {
8870
7107
  if (middleware2.length === 0) {
8871
7108
  return handler();
8872
7109
  }
@@ -8876,7 +7113,7 @@ class Router {
8876
7113
  return handler();
8877
7114
  }
8878
7115
  const mw = middleware2[currentIndex++];
8879
- const result = await mw(request2, next);
7116
+ const result = await mw(request, next);
8880
7117
  return result || new Response("No response from middleware", { status: 500 });
8881
7118
  };
8882
7119
  return next();
@@ -9038,16 +7275,16 @@ class RequestWithMacros {
9038
7275
  static macroAccessor(name, getter) {
9039
7276
  requestMacroRegistry.register(name, getter, true);
9040
7277
  }
9041
- static applyMacros(request2) {
7278
+ static applyMacros(request) {
9042
7279
  const epoch = requestMacroRegistry.getEpoch();
9043
7280
  if (epoch !== macroProtoEpoch) {
9044
7281
  macroProtoCache = new WeakMap;
9045
7282
  macroProtoEpoch = epoch;
9046
7283
  }
9047
- let base = Object.getPrototypeOf(request2) ?? Object.prototype;
7284
+ let base = Object.getPrototypeOf(request) ?? Object.prototype;
9048
7285
  if (base[MACRO_PROTO]) {
9049
7286
  if (macroProtoCache.get(base[MACRO_BASE]) === base) {
9050
- return request2;
7287
+ return request;
9051
7288
  }
9052
7289
  base = base[MACRO_BASE];
9053
7290
  }
@@ -9076,8 +7313,8 @@ class RequestWithMacros {
9076
7313
  Object.defineProperty(proto, MACRO_BASE, { value: base });
9077
7314
  macroProtoCache.set(base, proto);
9078
7315
  }
9079
- Object.setPrototypeOf(request2, proto);
9080
- return request2;
7316
+ Object.setPrototypeOf(request, proto);
7317
+ return request;
9081
7318
  }
9082
7319
  static hasMacro(name) {
9083
7320
  return requestMacroRegistry.has(name);
@@ -9271,8 +7508,8 @@ var BuiltInRequestMacros = {
9271
7508
  return this.url;
9272
7509
  },
9273
7510
  root() {
9274
- const url2 = getParsedURL(this);
9275
- return `${url2.protocol}//${url2.host}`;
7511
+ const url = getParsedURL(this);
7512
+ return `${url.protocol}//${url.host}`;
9276
7513
  },
9277
7514
  is(pattern) {
9278
7515
  const path = this.path();
@@ -9291,11 +7528,11 @@ var BuiltInRequestMacros = {
9291
7528
  return this.routeName || null;
9292
7529
  },
9293
7530
  fingerprint() {
9294
- const url2 = getParsedURL(this);
7531
+ const url = getParsedURL(this);
9295
7532
  const data = {
9296
7533
  method: this.method,
9297
- path: url2.pathname,
9298
- query: url2.search,
7534
+ path: url.pathname,
7535
+ query: url.search,
9299
7536
  ip: BuiltInRequestMacros.ip.call(this)
9300
7537
  };
9301
7538
  return btoa(JSON.stringify(data));
@@ -9346,6 +7583,36 @@ function registerBuiltInRequestMacros() {
9346
7583
  }
9347
7584
  registerBuiltInRequestMacros();
9348
7585
 
7586
+ // src/request/context.ts
7587
+ import { AsyncLocalStorage } from "async_hooks";
7588
+ var storage = new AsyncLocalStorage;
7589
+ var contextEnabled = false;
7590
+ var syncCurrent = null;
7591
+ function runWithRequest(initial, fn) {
7592
+ if (!contextEnabled) {
7593
+ syncCurrent = initial;
7594
+ const result = fn();
7595
+ if (result instanceof Promise) {
7596
+ return result.finally(() => {
7597
+ syncCurrent = null;
7598
+ });
7599
+ }
7600
+ syncCurrent = null;
7601
+ return result;
7602
+ }
7603
+ return storage.run({ current: initial }, fn);
7604
+ }
7605
+ function setCurrentRequest(req) {
7606
+ const store = storage.getStore();
7607
+ if (store) {
7608
+ store.current = req;
7609
+ return;
7610
+ }
7611
+ if (syncCurrent !== null) {
7612
+ syncCurrent = req;
7613
+ }
7614
+ }
7615
+
9349
7616
  // src/router/server.ts
9350
7617
  if (!RequestWithMacros.hasMacro("getParam")) {
9351
7618
  RequestWithMacros.macro("getParam", function(name, defaultValue) {
@@ -9631,10 +7898,10 @@ function registerServerHandling(RouterClass) {
9631
7898
  handleRequestImpl: {
9632
7899
  async value(req) {
9633
7900
  try {
9634
- const url2 = new URL(req.url);
9635
- req._parsedURL = url2;
9636
- const hostname = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
9637
- const match = this.matchRoute(url2.pathname, req.method, hostname);
7901
+ const url = new URL(req.url);
7902
+ req._parsedURL = url;
7903
+ const hostname = url.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
7904
+ const match = this.matchRoute(url.pathname, req.method, hostname);
9638
7905
  if (req.method === "OPTIONS" && !match) {
9639
7906
  const origin = req.headers.get("origin");
9640
7907
  const preflightHeaders = {
@@ -9654,7 +7921,7 @@ function registerServerHandling(RouterClass) {
9654
7921
  }
9655
7922
  const enhancedReq = this.enhanceRequest(req, {});
9656
7923
  setCurrentRequest(enhancedReq);
9657
- const allowedMethods = this.getAllowedMethods(url2.pathname, hostname);
7924
+ const allowedMethods = this.getAllowedMethods(url.pathname, hostname);
9658
7925
  const corsHeaders = {
9659
7926
  "Content-Type": "application/json",
9660
7927
  "Access-Control-Allow-Origin": "*",
@@ -9666,7 +7933,7 @@ function registerServerHandling(RouterClass) {
9666
7933
  return new Response(JSON.stringify({
9667
7934
  success: false,
9668
7935
  message: "Method Not Allowed",
9669
- path: url2.pathname,
7936
+ path: url.pathname,
9670
7937
  method: req.method,
9671
7938
  allowed: allowedMethods
9672
7939
  }), {
@@ -9690,7 +7957,7 @@ function registerServerHandling(RouterClass) {
9690
7957
  return new Response(JSON.stringify({
9691
7958
  success: false,
9692
7959
  message: "Not Found",
9693
- path: url2.pathname,
7960
+ path: url.pathname,
9694
7961
  method: req.method
9695
7962
  }), { status: 404, headers: corsHeaders });
9696
7963
  };
@@ -9985,34 +8252,34 @@ function registerWebSocketHandling(RouterClass) {
9985
8252
  configurable: true
9986
8253
  },
9987
8254
  upgrade: {
9988
- value(request2, options) {
8255
+ value(request, options) {
9989
8256
  if (!this.serverInstance) {
9990
8257
  throw new Error("Server not started, cannot upgrade to WebSocket");
9991
8258
  }
9992
8259
  if (!this.wsConfig) {
9993
8260
  throw new Error("WebSocket configuration not set, use router.websocket() to configure");
9994
8261
  }
9995
- return this.serverInstance.upgrade(request2, options);
8262
+ return this.serverInstance.upgrade(request, options);
9996
8263
  },
9997
8264
  writable: true,
9998
8265
  configurable: true
9999
8266
  },
10000
8267
  timeout: {
10001
- value(request2, seconds) {
8268
+ value(request, seconds) {
10002
8269
  if (!this.serverInstance) {
10003
8270
  throw new Error("Server not started, cannot set timeout");
10004
8271
  }
10005
- this.serverInstance.timeout(request2, seconds);
8272
+ this.serverInstance.timeout(request, seconds);
10006
8273
  },
10007
8274
  writable: true,
10008
8275
  configurable: true
10009
8276
  },
10010
8277
  requestIP: {
10011
- value(request2) {
8278
+ value(request) {
10012
8279
  if (!this.serverInstance) {
10013
8280
  throw new Error("Server not started, cannot get request IP");
10014
8281
  }
10015
- return this.serverInstance.requestIP(request2);
8282
+ return this.serverInstance.requestIP(request);
10016
8283
  },
10017
8284
  writable: true,
10018
8285
  configurable: true
@@ -10020,7 +8287,7 @@ function registerWebSocketHandling(RouterClass) {
10020
8287
  });
10021
8288
  }
10022
8289
  // src/response/macros.ts
10023
- import process4 from "process";
8290
+ import process3 from "process";
10024
8291
 
10025
8292
  class ResponseMacroRegistry {
10026
8293
  macros = new Map;
@@ -10201,16 +8468,16 @@ var BuiltInResponseMacros = {
10201
8468
  }
10202
8469
  });
10203
8470
  },
10204
- redirect: (url2, status = 302) => {
8471
+ redirect: (url, status = 302) => {
10205
8472
  return new Response(null, {
10206
8473
  status,
10207
8474
  headers: {
10208
- Location: url2
8475
+ Location: url
10209
8476
  }
10210
8477
  });
10211
8478
  },
10212
- permanentRedirect: (url2) => {
10213
- return BuiltInResponseMacros.redirect(url2, 301);
8479
+ permanentRedirect: (url) => {
8480
+ return BuiltInResponseMacros.redirect(url, 301);
10214
8481
  },
10215
8482
  download: (data, filename, contentType = "application/octet-stream") => {
10216
8483
  return new Response(data, {
@@ -10281,7 +8548,7 @@ var BuiltInResponseMacros = {
10281
8548
  status,
10282
8549
  timestamp: new Date().toISOString(),
10283
8550
  checks,
10284
- uptime: process4.uptime?.() || 0
8551
+ uptime: process3.uptime?.() || 0
10285
8552
  };
10286
8553
  return new Response(JSON.stringify(response2), {
10287
8554
  status: statusCode,
@@ -10345,8 +8612,8 @@ var BuiltInRules = {
10345
8612
  name: "url",
10346
8613
  validate: (value) => {
10347
8614
  try {
10348
- const url2 = new URL(value);
10349
- return Boolean(url2);
8615
+ const url = new URL(value);
8616
+ return Boolean(url);
10350
8617
  } catch {
10351
8618
  return false;
10352
8619
  }
@@ -11005,119 +9272,6 @@ registerRouteBuilding(Router);
11005
9272
  registerModelBinding(Router);
11006
9273
  registerFileBasedRouting(Router);
11007
9274
  registerApiRoutes(Router);
11008
- // src/router/middleware-groups.ts
11009
- class MiddlewareGroupRegistry {
11010
- groups = new Map;
11011
- globalMiddleware = [];
11012
- middlewarePriority = new Map;
11013
- define(name, middleware2) {
11014
- this.groups.set(name, [...middleware2]);
11015
- return this;
11016
- }
11017
- extend(name, middleware2) {
11018
- const existing = this.groups.get(name) || [];
11019
- this.groups.set(name, [...existing, ...middleware2]);
11020
- return this;
11021
- }
11022
- prepend(name, middleware2) {
11023
- const existing = this.groups.get(name) || [];
11024
- this.groups.set(name, [...middleware2, ...existing]);
11025
- return this;
11026
- }
11027
- get(name) {
11028
- return this.groups.get(name) || [];
11029
- }
11030
- has(name) {
11031
- return this.groups.has(name);
11032
- }
11033
- remove(name) {
11034
- this.groups.delete(name);
11035
- return this;
11036
- }
11037
- resolve(middleware2) {
11038
- if (typeof middleware2 === "function") {
11039
- return [middleware2];
11040
- }
11041
- if (this.groups.has(middleware2)) {
11042
- return this.get(middleware2);
11043
- }
11044
- return [middleware2];
11045
- }
11046
- resolveAll(middleware2) {
11047
- const resolved = [];
11048
- for (const mw of middleware2) {
11049
- resolved.push(...this.resolve(mw));
11050
- }
11051
- return resolved;
11052
- }
11053
- pushGlobal(...middleware2) {
11054
- this.globalMiddleware.push(...middleware2);
11055
- return this;
11056
- }
11057
- prependGlobal(...middleware2) {
11058
- this.globalMiddleware.unshift(...middleware2);
11059
- return this;
11060
- }
11061
- removeGlobal(middleware2) {
11062
- this.globalMiddleware = this.globalMiddleware.filter((mw) => typeof mw === "string" ? mw !== middleware2 : true);
11063
- return this;
11064
- }
11065
- getGlobal() {
11066
- return [...this.globalMiddleware];
11067
- }
11068
- setPriority(name, priority) {
11069
- this.middlewarePriority.set(name, priority);
11070
- return this;
11071
- }
11072
- sortByPriority(middleware2) {
11073
- return [...middleware2].sort((a, b) => {
11074
- const nameA = typeof a === "string" ? a.split(":")[0] : "";
11075
- const nameB = typeof b === "string" ? b.split(":")[0] : "";
11076
- const priorityA = this.middlewarePriority.get(nameA) ?? 100;
11077
- const priorityB = this.middlewarePriority.get(nameB) ?? 100;
11078
- return priorityA - priorityB;
11079
- });
11080
- }
11081
- filter(middleware2, options) {
11082
- if (options.only && options.only.length > 0) {
11083
- return middleware2.filter((mw) => {
11084
- if (typeof mw === "string") {
11085
- const name = mw.split(":")[0];
11086
- return options.only.includes(name);
11087
- }
11088
- return false;
11089
- });
11090
- }
11091
- if (options.except && options.except.length > 0) {
11092
- return middleware2.filter((mw) => {
11093
- if (typeof mw === "string") {
11094
- const name = mw.split(":")[0];
11095
- return !options.except.includes(name);
11096
- }
11097
- return true;
11098
- });
11099
- }
11100
- return middleware2;
11101
- }
11102
- flush() {
11103
- this.groups.clear();
11104
- this.globalMiddleware = [];
11105
- this.middlewarePriority.clear();
11106
- }
11107
- }
11108
- var middlewareGroups = new MiddlewareGroupRegistry;
11109
- middlewareGroups.define("web", ["session", "csrf", "cors"]);
11110
- middlewareGroups.define("api", ["throttle:60,1", "cors"]);
11111
- middlewareGroups.setPriority("session", 10);
11112
- middlewareGroups.setPriority("cors", 20);
11113
- middlewareGroups.setPriority("csrf", 30);
11114
- middlewareGroups.setPriority("auth", 40);
11115
- middlewareGroups.setPriority("throttle", 50);
11116
- middlewareGroups.setPriority("role", 60);
11117
- middlewareGroups.setPriority("permission", 60);
11118
- middlewareGroups.setPriority("can", 70);
11119
- middlewareGroups.setPriority("abilities", 70);
11120
- middlewareGroups.setPriority("verified", 80);
11121
9275
  // src/testing/auth-testing.ts
11122
9276
  import { mock } from "bun:test";
11123
9277
 
@@ -11336,8 +9490,8 @@ function createCookieAccessor2(cookies) {
11336
9490
 
11337
9491
  class AuthTester {
11338
9492
  request;
11339
- constructor(request2) {
11340
- this.request = request2 || createMockRequest();
9493
+ constructor(request) {
9494
+ this.request = request || createMockRequest();
11341
9495
  }
11342
9496
  as(user) {
11343
9497
  this.request.user = user;
@@ -11565,16 +9719,16 @@ var authMocks = {
11565
9719
  })
11566
9720
  };
11567
9721
  var authHelpers = {
11568
- authenticatedRequest: (user, request2) => {
11569
- return new AuthTester(request2).as(user).getRequest();
9722
+ authenticatedRequest: (user, request) => {
9723
+ return new AuthTester(request).as(user).getRequest();
11570
9724
  },
11571
- jwtRequest: (payload = {}, request2) => {
9725
+ jwtRequest: (payload = {}, request) => {
11572
9726
  const jwt = new JWTTester;
11573
9727
  const token = jwt.generateToken(payload);
11574
- return new AuthTester(request2).withJWT(token).getRequest();
9728
+ return new AuthTester(request).withJWT(token).getRequest();
11575
9729
  },
11576
- sessionRequest: (sessionData = {}, request2) => {
11577
- return new AuthTester(request2).withSession(sessionData).getRequest();
9730
+ sessionRequest: (sessionData = {}, request) => {
9731
+ return new AuthTester(request).withSession(sessionData).getRequest();
11578
9732
  },
11579
9733
  verifyJWTStructure: (token) => {
11580
9734
  const parts = token.split(".");
@@ -11589,8 +9743,8 @@ var authHelpers = {
11589
9743
  }
11590
9744
  }
11591
9745
  };
11592
- function createAuthTester(request2) {
11593
- return new AuthTester(request2);
9746
+ function createAuthTester(request) {
9747
+ return new AuthTester(request);
11594
9748
  }
11595
9749
  function createJWTTester(options) {
11596
9750
  return new JWTTester(options);
@@ -11598,13 +9752,14 @@ function createJWTTester(options) {
11598
9752
  function createSessionTester(options) {
11599
9753
  return new SessionTester(options);
11600
9754
  }
9755
+
11601
9756
  // src/testing/file-upload-testing.ts
11602
9757
  import { mock as mock2 } from "bun:test";
11603
9758
  class FileUploadTester {
11604
9759
  request;
11605
9760
  files;
11606
- constructor(request2) {
11607
- this.request = request2 || createMockRequest();
9761
+ constructor(request) {
9762
+ this.request = request || createMockRequest();
11608
9763
  this.files = [];
11609
9764
  }
11610
9765
  addFile(testFile) {
@@ -11836,8 +9991,8 @@ var fileUploadScenarios = {
11836
9991
  }
11837
9992
  };
11838
9993
  var fileUploadAssertions = {
11839
- hasFiles: (request2, count) => {
11840
- const files = request2.files || [];
9994
+ hasFiles: (request, count) => {
9995
+ const files = request.files || [];
11841
9996
  if (count !== undefined && files.length !== count) {
11842
9997
  throw new Error(`Expected ${count} files, but got ${files.length}`);
11843
9998
  }
@@ -11845,8 +10000,8 @@ var fileUploadAssertions = {
11845
10000
  throw new Error("Expected request to have files");
11846
10001
  }
11847
10002
  },
11848
- hasFileWithField: (request2, fieldName) => {
11849
- const files = request2.files || [];
10003
+ hasFileWithField: (request, fieldName) => {
10004
+ const files = request.files || [];
11850
10005
  const hasField = files.some((file) => file.fieldName === fieldName);
11851
10006
  if (!hasField) {
11852
10007
  throw new Error(`Expected file with field name '${fieldName}'`);
@@ -11870,8 +10025,8 @@ var fileUploadAssertions = {
11870
10025
  }
11871
10026
  }
11872
10027
  };
11873
- function createFileUploadTester(request2) {
11874
- return new FileUploadTester(request2);
10028
+ function createFileUploadTester(request) {
10029
+ return new FileUploadTester(request);
11875
10030
  }
11876
10031
  var testFiles = {
11877
10032
  image: (fieldName = "image", filename = "test.jpg") => ({
@@ -11905,6 +10060,7 @@ var testFiles = {
11905
10060
  mimetype: "application/x-executable"
11906
10061
  })
11907
10062
  };
10063
+
11908
10064
  // src/testing/middleware-testing.ts
11909
10065
  import { mock as mock3, spyOn } from "bun:test";
11910
10066
  class MiddlewareTester {
@@ -11915,15 +10071,15 @@ class MiddlewareTester {
11915
10071
  this.context = this.createContext();
11916
10072
  }
11917
10073
  createContext() {
11918
- const request2 = createMockRequest();
10074
+ const request = createMockRequest();
11919
10075
  const next = mock3(() => Promise.resolve(new Response("Next called", { status: 200 })));
11920
10076
  return {
11921
- request: request2,
10077
+ request,
11922
10078
  next
11923
10079
  };
11924
10080
  }
11925
- withRequest(request2) {
11926
- this.context.request = request2;
10081
+ withRequest(request) {
10082
+ this.context.request = request;
11927
10083
  return this;
11928
10084
  }
11929
10085
  withNext(nextFn) {
@@ -12021,8 +10177,8 @@ class MiddlewareChainTester {
12021
10177
  this.request = createMockRequest();
12022
10178
  this.finalHandler = mock3(async () => new Response("Final handler", { status: 200 }));
12023
10179
  }
12024
- withRequest(request2) {
12025
- this.request = request2;
10180
+ withRequest(request) {
10181
+ this.request = request;
12026
10182
  return this;
12027
10183
  }
12028
10184
  withFinalHandler(handler) {
@@ -12062,11 +10218,11 @@ function createMiddlewareSpy() {
12062
10218
  }
12063
10219
  async function benchmarkMiddleware(middleware2, iterations = 1000) {
12064
10220
  const times = [];
12065
- const request2 = createMockRequest();
10221
+ const request = createMockRequest();
12066
10222
  const next = mock3(async () => new Response("OK"));
12067
10223
  for (let i = 0;i < iterations; i++) {
12068
10224
  const start = performance.now();
12069
- await middleware2(request2, next);
10225
+ await middleware2(request, next);
12070
10226
  const end = performance.now();
12071
10227
  times.push(end - start);
12072
10228
  }
@@ -12081,6 +10237,7 @@ async function benchmarkMiddleware(middleware2, iterations = 1000) {
12081
10237
  totalTime
12082
10238
  };
12083
10239
  }
10240
+
12084
10241
  // src/testing/model-binding-testing.ts
12085
10242
  import { mock as mock4 } from "bun:test";
12086
10243
  class GenericMockModel {
@@ -12164,8 +10321,8 @@ class ModelBindingTester {
12164
10321
  this.options = options;
12165
10322
  this.request = createMockRequest();
12166
10323
  }
12167
- withRequest(request2) {
12168
- this.request = request2;
10324
+ withRequest(request) {
10325
+ this.request = request;
12169
10326
  return this;
12170
10327
  }
12171
10328
  withParams(params) {
@@ -12303,18 +10460,18 @@ var modelMocks = {
12303
10460
  };
12304
10461
  var constraintHelpers = {
12305
10462
  belongsTo: (userIdField = "userId") => {
12306
- const fn = async (model, request2) => {
12307
- if (!request2.user)
10463
+ const fn = async (model, request) => {
10464
+ if (!request.user)
12308
10465
  return false;
12309
- return model[userIdField] === request2.user.id;
10466
+ return model[userIdField] === request.user.id;
12310
10467
  };
12311
10468
  return mock4(fn);
12312
10469
  },
12313
10470
  belongsToUser: () => {
12314
- const fn = async (model, request2) => {
12315
- if (!request2.user)
10471
+ const fn = async (model, request) => {
10472
+ if (!request.user)
12316
10473
  return false;
12317
- return model.userId === request2.user.id;
10474
+ return model.userId === request.user.id;
12318
10475
  };
12319
10476
  return mock4(fn);
12320
10477
  },
@@ -12334,9 +10491,9 @@ var constraintHelpers = {
12334
10491
  return mock4(constraintFn);
12335
10492
  },
12336
10493
  combine: (...constraints) => {
12337
- const fn = async (model, request2) => {
10494
+ const fn = async (model, request) => {
12338
10495
  for (const constraint of constraints) {
12339
- if (!await constraint(model, request2)) {
10496
+ if (!await constraint(model, request)) {
12340
10497
  return false;
12341
10498
  }
12342
10499
  }
@@ -12393,24 +10550,24 @@ class ModelResolverTester {
12393
10550
  }
12394
10551
  var routeModelHelpers = {
12395
10552
  testRoute: async (path, params, modelBindings) => {
12396
- const request2 = createMockRequest("GET", path);
12397
- request2.params = params;
10553
+ const request = createMockRequest("GET", path);
10554
+ request.params = params;
12398
10555
  for (const [key, model] of Object.entries(modelBindings)) {
12399
- request2.context = request2.context || {};
12400
- request2.context[key] = model;
10556
+ request.context = request.context || {};
10557
+ request.context[key] = model;
12401
10558
  }
12402
- return request2;
10559
+ return request;
12403
10560
  },
12404
10561
  createHandler: (expectedModels) => {
12405
- const fn = async (request2) => {
12406
- const context = request2.context || {};
10562
+ const fn = async (request) => {
10563
+ const context = request.context || {};
12407
10564
  for (const modelKey of expectedModels) {
12408
10565
  if (!context[modelKey]) {
12409
10566
  return new Response(`Model ${modelKey} not found`, { status: 404 });
12410
10567
  }
12411
10568
  }
12412
10569
  return new Response(JSON.stringify({
12413
- params: request2.params,
10570
+ params: request.params,
12414
10571
  models: Object.fromEntries(expectedModels.map((key) => [key, context[key]]))
12415
10572
  }), {
12416
10573
  headers: { "Content-Type": "application/json" }
@@ -12419,16 +10576,16 @@ var routeModelHelpers = {
12419
10576
  return mock4(fn);
12420
10577
  },
12421
10578
  testBindingMiddleware: (bindings) => {
12422
- const fn = async (request2, next) => {
12423
- request2.context = request2.context || {};
10579
+ const fn = async (request, next) => {
10580
+ request.context = request.context || {};
12424
10581
  for (const [key, resolver] of Object.entries(bindings)) {
12425
- const paramValue = request2.params[key];
10582
+ const paramValue = request.params[key];
12426
10583
  if (paramValue) {
12427
10584
  try {
12428
10585
  if (typeof resolver === "function") {
12429
- request2.context[key] = await resolver(paramValue);
10586
+ request.context[key] = await resolver(paramValue);
12430
10587
  } else {
12431
- request2.context[key] = resolver;
10588
+ request.context[key] = resolver;
12432
10589
  }
12433
10590
  } catch (error) {
12434
10591
  console.error(`Error resolving model ${key}:`, error);
@@ -12463,9 +10620,10 @@ var scopedBindingHelpers = {
12463
10620
  return requiredPermissions.every((permission) => user.permissions.includes(permission));
12464
10621
  }
12465
10622
  };
10623
+
12466
10624
  // src/testing/performance-testing.ts
12467
10625
  import { mock as mock5 } from "bun:test";
12468
- import process5 from "process";
10626
+ import process4 from "process";
12469
10627
  class PerformanceTester {
12470
10628
  metrics = [];
12471
10629
  startTime = 0;
@@ -12584,10 +10742,10 @@ class LoadTester {
12584
10742
  var benchmarkTests = {
12585
10743
  routeHandler: async (handler, iterations = 1000) => {
12586
10744
  const times = [];
12587
- const request2 = createMockRequest();
10745
+ const request = createMockRequest();
12588
10746
  for (let i = 0;i < iterations; i++) {
12589
10747
  const start = performance.now();
12590
- await handler(request2);
10748
+ await handler(request);
12591
10749
  const end = performance.now();
12592
10750
  times.push(end - start);
12593
10751
  }
@@ -12606,11 +10764,11 @@ var benchmarkTests = {
12606
10764
  },
12607
10765
  middleware: async (middleware2, iterations = 1000) => {
12608
10766
  const times = [];
12609
- const request2 = createMockRequest();
10767
+ const request = createMockRequest();
12610
10768
  const next = mock5(async () => new Response("OK"));
12611
10769
  for (let i = 0;i < iterations; i++) {
12612
10770
  const start = performance.now();
12613
- await middleware2(request2, next);
10771
+ await middleware2(request, next);
12614
10772
  const end = performance.now();
12615
10773
  times.push(end - start);
12616
10774
  }
@@ -12683,11 +10841,11 @@ var memoryTests = {
12683
10841
  if (globalThis.gc) {
12684
10842
  globalThis.gc();
12685
10843
  }
12686
- const initialHeap = process5.memoryUsage().heapUsed;
10844
+ const initialHeap = process4.memoryUsage().heapUsed;
12687
10845
  for (let i = 0;i < iterations; i++) {
12688
10846
  const result = await fn();
12689
10847
  results.push(result);
12690
- const memUsage = process5.memoryUsage();
10848
+ const memUsage = process4.memoryUsage();
12691
10849
  heapUsages.push(memUsage.heapUsed);
12692
10850
  if (i % 10 === 0 && globalThis.gc) {
12693
10851
  globalThis.gc();
@@ -12696,7 +10854,7 @@ var memoryTests = {
12696
10854
  if (globalThis.gc) {
12697
10855
  globalThis.gc();
12698
10856
  }
12699
- const finalHeap = process5.memoryUsage().heapUsed;
10857
+ const finalHeap = process4.memoryUsage().heapUsed;
12700
10858
  const averageHeapUsed = heapUsages.reduce((sum, usage) => sum + usage, 0) / heapUsages.length;
12701
10859
  const maxHeapUsed = Math.max(...heapUsages);
12702
10860
  const memoryLeakDetected = finalHeap - initialHeap > 1024 * 1024;
@@ -12710,17 +10868,17 @@ var memoryTests = {
12710
10868
  testRouteHandlerMemoryLeaks: async (handler, iterations = 1000) => {
12711
10869
  if (globalThis.gc)
12712
10870
  globalThis.gc();
12713
- const initialMemory = process5.memoryUsage().heapUsed;
10871
+ const initialMemory = process4.memoryUsage().heapUsed;
12714
10872
  for (let i = 0;i < iterations; i++) {
12715
- const request2 = createMockRequest();
12716
- await handler(request2);
10873
+ const request = createMockRequest();
10874
+ await handler(request);
12717
10875
  if (i % 100 === 0 && globalThis.gc) {
12718
10876
  globalThis.gc();
12719
10877
  }
12720
10878
  }
12721
10879
  if (globalThis.gc)
12722
10880
  globalThis.gc();
12723
- const finalMemory = process5.memoryUsage().heapUsed;
10881
+ const finalMemory = process4.memoryUsage().heapUsed;
12724
10882
  const memoryGrowth = finalMemory - initialMemory;
12725
10883
  const memoryLeakDetected = memoryGrowth > 5 * 1024 * 1024;
12726
10884
  return {
@@ -12812,7 +10970,7 @@ function createLoadTester(client, config2) {
12812
10970
  }
12813
10971
  var performanceUtils = {
12814
10972
  getCurrentMemoryUsage: () => {
12815
- const usage = process5.memoryUsage();
10973
+ const usage = process4.memoryUsage();
12816
10974
  return {
12817
10975
  heapUsed: usage.heapUsed,
12818
10976
  heapTotal: usage.heapTotal,
@@ -12821,7 +10979,7 @@ var performanceUtils = {
12821
10979
  };
12822
10980
  },
12823
10981
  getCPUUsage: () => {
12824
- const usage = process5.cpuUsage();
10982
+ const usage = process4.cpuUsage();
12825
10983
  return {
12826
10984
  user: usage.user,
12827
10985
  system: usage.system
@@ -12836,6 +10994,7 @@ var performanceUtils = {
12836
10994
  return JSON.stringify(results, null, 2);
12837
10995
  }
12838
10996
  };
10997
+
12839
10998
  // src/testing/test-response.ts
12840
10999
  class TestResponseWrapper {
12841
11000
  status;
@@ -12956,14 +11115,14 @@ class TestResponseWrapper {
12956
11115
  }
12957
11116
  return this;
12958
11117
  }
12959
- expectRedirect(url2) {
11118
+ expectRedirect(url) {
12960
11119
  if (this.status < 300 || this.status >= 400) {
12961
11120
  throw new Error(`Expected redirect status (3xx), got ${this.status}`);
12962
11121
  }
12963
- if (url2) {
11122
+ if (url) {
12964
11123
  const location = this.headers.location;
12965
- if (location !== url2) {
12966
- throw new Error(`Expected redirect to '${url2}', got '${location}'`);
11124
+ if (location !== url) {
11125
+ throw new Error(`Expected redirect to '${url}', got '${location}'`);
12967
11126
  }
12968
11127
  }
12969
11128
  return this;
@@ -13106,24 +11265,24 @@ class TestClient {
13106
11265
  async request(method, path, options = {}) {
13107
11266
  const headers = { ...this.defaultHeaders, ...options.headers };
13108
11267
  const requestOptions = { ...options, headers };
13109
- const request2 = new TestRequestBuilder(method, path, requestOptions).build();
11268
+ const request = new TestRequestBuilder(method, path, requestOptions).build();
13110
11269
  try {
13111
- const response2 = await this.handleRequest(request2);
11270
+ const response2 = await this.handleRequest(request);
13112
11271
  return await createTestResponse(response2);
13113
11272
  } catch (error) {
13114
11273
  throw new Error(`Test request failed: ${error instanceof Error ? error.message : "Unknown error"}`);
13115
11274
  }
13116
11275
  }
13117
- async handleRequest(request2) {
13118
- const url2 = new URL(request2.url);
13119
- const matchResult = this.findMatchingRoute(request2.method, url2.pathname);
11276
+ async handleRequest(request) {
11277
+ const url = new URL(request.url);
11278
+ const matchResult = this.findMatchingRoute(request.method, url.pathname);
13120
11279
  if (!matchResult) {
13121
11280
  return new Response("Not Found", { status: 404 });
13122
11281
  }
13123
11282
  const { route, params } = matchResult;
13124
- request2.params = { ...request2.params, ...params };
11283
+ request.params = { ...request.params, ...params };
13125
11284
  try {
13126
- const response2 = await this.executeMiddlewareChain(request2, route);
11285
+ const response2 = await this.executeMiddlewareChain(request, route);
13127
11286
  return response2 || new Response("No response", { status: 500 });
13128
11287
  } catch (error) {
13129
11288
  return new Response(JSON.stringify({ error: error instanceof Error ? error.message : "Internal Server Error" }), {
@@ -13163,28 +11322,28 @@ class TestClient {
13163
11322
  }
13164
11323
  return params;
13165
11324
  }
13166
- async executeMiddlewareChain(request2, route) {
11325
+ async executeMiddlewareChain(request, route) {
13167
11326
  const middlewares = [...this.router.globalMiddleware, ...route.middleware];
13168
11327
  let index = 0;
13169
11328
  const next = async () => {
13170
11329
  if (index < middlewares.length) {
13171
11330
  const middleware2 = middlewares[index++];
13172
- return await middleware2(request2, next);
11331
+ return await middleware2(request, next);
13173
11332
  } else {
13174
- return await this.executeHandler(route.handler, request2);
11333
+ return await this.executeHandler(route.handler, request);
13175
11334
  }
13176
11335
  };
13177
11336
  const result = await next();
13178
11337
  return result || new Response("No response", { status: 500 });
13179
11338
  }
13180
- async executeHandler(handler, request2) {
11339
+ async executeHandler(handler, request) {
13181
11340
  if (typeof handler === "function") {
13182
- return await handler(request2);
11341
+ return await handler(request);
13183
11342
  } else if (typeof handler === "string") {
13184
11343
  throw new TypeError(`String handlers not supported in test environment: ${handler}`);
13185
11344
  } else if (typeof handler === "object" && handler.handle) {
13186
11345
  const instance = new handler;
13187
- return await instance.handle(request2);
11346
+ return await instance.handle(request);
13188
11347
  }
13189
11348
  throw new Error("Invalid handler type");
13190
11349
  }
@@ -13225,6 +11384,7 @@ var testClient = {
13225
11384
  withAuth: (token, type = "Bearer") => (router2) => createTestClient(router2).auth(token, type),
13226
11385
  withHeaders: (headers) => (router2) => createTestClient(router2).withHeaders(headers)
13227
11386
  };
11387
+
13228
11388
  // src/testing/websocket-testing.ts
13229
11389
  import { mock as mock6 } from "bun:test";
13230
11390
 
@@ -13579,13 +11739,13 @@ export {
13579
11739
  spyOnMiddleware,
13580
11740
  singleFileUpload,
13581
11741
  signRequest,
13582
- setCurrentRequest,
11742
+ setCurrentRequest2 as setCurrentRequest,
13583
11743
  setContainer,
13584
11744
  session,
13585
11745
  securitySuite,
13586
11746
  security,
13587
11747
  scopedBindingHelpers,
13588
- runWithRequest,
11748
+ runWithRequest2 as runWithRequest,
13589
11749
  rule,
13590
11750
  router,
13591
11751
  routeModelHelpers,
@@ -13598,7 +11758,7 @@ export {
13598
11758
  requestSigning,
13599
11759
  requestId,
13600
11760
  request,
13601
- registerNamedRoute,
11761
+ registerNamedRoute2 as registerNamedRoute,
13602
11762
  rateLimitRegistry,
13603
11763
  rateLimit,
13604
11764
  processHtmlTemplate,
@@ -13628,7 +11788,7 @@ export {
13628
11788
  globalValidator,
13629
11789
  globalMiddlewarePipeline,
13630
11790
  getNamedRoutes,
13631
- getNamedRoutePath,
11791
+ getNamedRoutePath2 as getNamedRoutePath,
13632
11792
  getCurrentRequest,
13633
11793
  getContainer,
13634
11794
  getConfig,
@@ -13759,7 +11919,7 @@ export {
13759
11919
  FileUpload,
13760
11920
  FileSessionStore,
13761
11921
  FileSecurity,
13762
- exports_errors as Errors,
11922
+ Errors,
13763
11923
  EnvironmentManager,
13764
11924
  EnhancementPresets,
13765
11925
  DomainMatcher,
@@ -13779,6 +11939,6 @@ export {
13779
11939
  BuiltInRules,
13780
11940
  BindingBuilder,
13781
11941
  AuthTester,
13782
- auth_default as Auth,
11942
+ default2 as Auth,
13783
11943
  ApiKeyManager
13784
11944
  };