@stacksjs/bun-router 0.0.13 → 0.0.14
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/CHANGELOG.md +770 -0
- package/README.md +584 -261
- package/package.json +43 -47
- package/bin/cli.ts +0 -9
- package/dist/auth.d.ts +0 -134
- package/dist/cache/lru-cache.d.ts +0 -139
- package/dist/cache/middleware-memoization.d.ts +0 -151
- package/dist/cache/route-cache-warmer.d.ts +0 -161
- package/dist/cache/sqlite-cache.d.ts +0 -209
- package/dist/cache/streaming-cache.d.ts +0 -132
- package/dist/chunk-1mvtv6jw.js +0 -13012
- package/dist/cli/colors.d.ts +0 -15
- package/dist/cli/index.d.ts +0 -10
- package/dist/cli/middleware.d.ts +0 -31
- package/dist/cli/openapi.d.ts +0 -17
- package/dist/cli/router.d.ts +0 -15
- package/dist/cli/routes.d.ts +0 -29
- package/dist/cli/utils.d.ts +0 -74
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -3195
- package/dist/config.d.ts +0 -7
- package/dist/container/container.d.ts +0 -273
- package/dist/container/contextual-binding.d.ts +0 -240
- package/dist/container/decorators.d.ts +0 -141
- package/dist/container/service-provider.d.ts +0 -285
- package/dist/development/hot-reload.d.ts +0 -187
- package/dist/development/index.d.ts +0 -167
- package/dist/development/performance-profiler.d.ts +0 -217
- package/dist/development/route-debugger.d.ts +0 -154
- package/dist/development/route-inspector.d.ts +0 -211
- package/dist/development/typescript-utilities.d.ts +0 -209
- package/dist/docs.d.ts +0 -11
- package/dist/errors/circuit-breaker.d.ts +0 -195
- package/dist/errors/error-handler.d.ts +0 -88
- package/dist/errors/error-reporting.d.ts +0 -144
- package/dist/errors/exceptions.d.ts +0 -184
- package/dist/errors/graceful-degradation.d.ts +0 -154
- package/dist/errors/index.d.ts +0 -9
- package/dist/errors/router-errors.d.ts +0 -110
- package/dist/file-serving/static-files.d.ts +0 -143
- package/dist/index.d.ts +0 -14
- package/dist/index.js +0 -13933
- package/dist/middleware/auth.d.ts +0 -63
- package/dist/middleware/content_security_policy.d.ts +0 -53
- package/dist/middleware/cors.d.ts +0 -5
- package/dist/middleware/csrf.d.ts +0 -7
- package/dist/middleware/ddos_protection.d.ts +0 -39
- package/dist/middleware/file_security.d.ts +0 -26
- package/dist/middleware/file_upload.d.ts +0 -38
- package/dist/middleware/helmet.d.ts +0 -54
- package/dist/middleware/index.d.ts +0 -44
- package/dist/middleware/input_validation.d.ts +0 -45
- package/dist/middleware/json_body.d.ts +0 -4
- package/dist/middleware/performance_alerting.d.ts +0 -87
- package/dist/middleware/performance_dashboard.d.ts +0 -87
- package/dist/middleware/performance_monitor.d.ts +0 -209
- package/dist/middleware/pipeline.d.ts +0 -131
- package/dist/middleware/rate_limit.d.ts +0 -36
- package/dist/middleware/request_id.d.ts +0 -4
- package/dist/middleware/request_signing.d.ts +0 -153
- package/dist/middleware/request_tracer.d.ts +0 -72
- package/dist/middleware/response_cache.d.ts +0 -97
- package/dist/middleware/security.d.ts +0 -69
- package/dist/middleware/security_suite.d.ts +0 -46
- package/dist/middleware/session.d.ts +0 -23
- package/dist/model-binding/index.d.ts +0 -2
- package/dist/model-binding/model-middleware.d.ts +0 -118
- package/dist/model-binding/model-registry.d.ts +0 -164
- package/dist/model-binding.d.ts +0 -185
- package/dist/model-resolver-factory.d.ts +0 -31
- package/dist/observability/correlation.d.ts +0 -187
- package/dist/observability/health-checks.d.ts +0 -186
- package/dist/observability/index.d.ts +0 -60
- package/dist/observability/integration.d.ts +0 -147
- package/dist/observability/metrics.d.ts +0 -184
- package/dist/observability/tracing.d.ts +0 -187
- package/dist/optimization/bun-utilities.d.ts +0 -224
- package/dist/query-builder-integration.d.ts +0 -36
- package/dist/request/context.d.ts +0 -34
- package/dist/request/enhanced-request.d.ts +0 -213
- package/dist/request/macros.d.ts +0 -300
- package/dist/response/macros.d.ts +0 -245
- package/dist/response/response-factory.d.ts +0 -133
- package/dist/router/api-routes.d.ts +0 -5
- package/dist/router/file-based-routing.d.ts +0 -118
- package/dist/router/file-streaming.d.ts +0 -5
- package/dist/router/fluent-router.d.ts +0 -315
- package/dist/router/fluent-routing.d.ts +0 -271
- package/dist/router/group-organization.d.ts +0 -5
- package/dist/router/handler-resolver.d.ts +0 -24
- package/dist/router/http-methods.d.ts +0 -10
- package/dist/router/index.d.ts +0 -114
- package/dist/router/middleware-groups.d.ts +0 -94
- package/dist/router/middleware-integration.d.ts +0 -112
- package/dist/router/middleware.d.ts +0 -5
- package/dist/router/model-binding.d.ts +0 -5
- package/dist/router/optimized-route-matching.d.ts +0 -6
- package/dist/router/route-building.d.ts +0 -5
- package/dist/router/route-compiler.d.ts +0 -153
- package/dist/router/route-matching.d.ts +0 -5
- package/dist/router/route-trie.d.ts +0 -114
- package/dist/router/router.d.ts +0 -294
- package/dist/router/server.d.ts +0 -5
- package/dist/router/validation-integration.d.ts +0 -170
- package/dist/router/view-rendering.d.ts +0 -5
- package/dist/router/websocket.d.ts +0 -5
- package/dist/routing/route-caching.d.ts +0 -129
- package/dist/routing/route-throttling.d.ts +0 -150
- package/dist/routing/subdomain-routing.d.ts +0 -206
- package/dist/session/database-store.d.ts +0 -51
- package/dist/session/file-store.d.ts +0 -22
- package/dist/session/index.d.ts +0 -106
- package/dist/session/memory-store.d.ts +0 -21
- package/dist/session/redis-store.d.ts +0 -31
- package/dist/streaming/index.d.ts +0 -3
- package/dist/streaming/sse-handler.d.ts +0 -138
- package/dist/streaming/stream-handler.d.ts +0 -114
- package/dist/testing/auth-testing.d.ts +0 -156
- package/dist/testing/file-upload-testing.d.ts +0 -190
- package/dist/testing/index.d.ts +0 -10
- package/dist/testing/middleware-testing.d.ts +0 -138
- package/dist/testing/model-binding-testing.d.ts +0 -186
- package/dist/testing/performance-testing.d.ts +0 -235
- package/dist/testing/test-client.d.ts +0 -117
- package/dist/testing/test-request.d.ts +0 -85
- package/dist/testing/test-response.d.ts +0 -90
- package/dist/testing/types.d.ts +0 -207
- package/dist/testing/websocket-testing.d.ts +0 -227
- package/dist/types/controller-types.d.ts +0 -208
- package/dist/types/core.d.ts +0 -543
- package/dist/types/middleware-types.d.ts +0 -227
- package/dist/types/request-response-augmentation.d.ts +0 -261
- package/dist/types/route-inference.d.ts +0 -168
- package/dist/types.d.ts +0 -1782
- package/dist/url.d.ts +0 -56
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/query-preservation.d.ts +0 -48
- package/dist/utils.d.ts +0 -69
- package/dist/validation/validator.d.ts +0 -140
- package/dist/websocket/clustering.d.ts +0 -185
- package/src/auth.ts +0 -469
- package/src/cache/lru-cache.ts +0 -457
- package/src/cache/middleware-memoization.ts +0 -531
- package/src/cache/route-cache-warmer.ts +0 -486
- package/src/cache/sqlite-cache.ts +0 -783
- package/src/cache/streaming-cache.ts +0 -572
- package/src/cli/colors.ts +0 -29
- package/src/cli/index.ts +0 -287
- package/src/cli/middleware.ts +0 -291
- package/src/cli/openapi.ts +0 -407
- package/src/cli/router.ts +0 -188
- package/src/cli/routes.ts +0 -265
- package/src/cli/utils.ts +0 -531
- package/src/cli.ts +0 -5
- package/src/config.ts +0 -322
- package/src/container/container.ts +0 -740
- package/src/container/contextual-binding.ts +0 -603
- package/src/container/decorators.ts +0 -359
- package/src/container/service-provider.ts +0 -596
- package/src/development/hot-reload.ts +0 -673
- package/src/development/index.ts +0 -499
- package/src/development/performance-profiler.ts +0 -717
- package/src/development/route-debugger.ts +0 -527
- package/src/development/route-inspector.ts +0 -749
- package/src/development/typescript-utilities.ts +0 -682
- package/src/docs.ts +0 -397
- package/src/errors/circuit-breaker.ts +0 -732
- package/src/errors/error-handler.ts +0 -569
- package/src/errors/error-reporting.ts +0 -672
- package/src/errors/exceptions.ts +0 -536
- package/src/errors/graceful-degradation.ts +0 -621
- package/src/errors/index.ts +0 -21
- package/src/errors/router-errors.ts +0 -632
- package/src/file-serving/static-files.ts +0 -581
- package/src/index.ts +0 -14
- package/src/middleware/auth.ts +0 -219
- package/src/middleware/content_security_policy.ts +0 -215
- package/src/middleware/cors.ts +0 -74
- package/src/middleware/csrf.ts +0 -108
- package/src/middleware/ddos_protection.ts +0 -255
- package/src/middleware/file_security.ts +0 -191
- package/src/middleware/file_upload.ts +0 -264
- package/src/middleware/helmet.ts +0 -268
- package/src/middleware/index.ts +0 -117
- package/src/middleware/input_validation.ts +0 -449
- package/src/middleware/json_body.ts +0 -37
- package/src/middleware/performance_alerting.ts +0 -538
- package/src/middleware/performance_dashboard.ts +0 -661
- package/src/middleware/performance_monitor.ts +0 -943
- package/src/middleware/pipeline.ts +0 -489
- package/src/middleware/rate_limit.ts +0 -245
- package/src/middleware/request_id.ts +0 -36
- package/src/middleware/request_signing.ts +0 -636
- package/src/middleware/request_tracer.ts +0 -636
- package/src/middleware/response_cache.ts +0 -743
- package/src/middleware/security.ts +0 -482
- package/src/middleware/security_suite.ts +0 -257
- package/src/middleware/session.ts +0 -91
- package/src/model-binding/index.ts +0 -18
- package/src/model-binding/model-middleware.ts +0 -425
- package/src/model-binding/model-registry.ts +0 -550
- package/src/model-binding.ts +0 -370
- package/src/model-resolver-factory.ts +0 -106
- package/src/observability/correlation.ts +0 -691
- package/src/observability/health-checks.ts +0 -729
- package/src/observability/index.ts +0 -184
- package/src/observability/integration.ts +0 -548
- package/src/observability/metrics.ts +0 -753
- package/src/observability/tracing.ts +0 -638
- package/src/optimization/bun-utilities.ts +0 -778
- package/src/query-builder-integration.ts +0 -137
- package/src/request/context.ts +0 -62
- package/src/request/enhanced-request.ts +0 -857
- package/src/request/macros.ts +0 -688
- package/src/response/macros.ts +0 -612
- package/src/response/response-factory.ts +0 -596
- package/src/router/api-routes.ts +0 -243
- package/src/router/file-based-routing.ts +0 -670
- package/src/router/file-streaming.ts +0 -372
- package/src/router/fluent-router.ts +0 -927
- package/src/router/fluent-routing.ts +0 -797
- package/src/router/group-organization.ts +0 -213
- package/src/router/handler-resolver.ts +0 -291
- package/src/router/http-methods.ts +0 -377
- package/src/router/index.ts +0 -187
- package/src/router/middleware-groups.ts +0 -222
- package/src/router/middleware-integration.ts +0 -399
- package/src/router/middleware.ts +0 -231
- package/src/router/model-binding.ts +0 -215
- package/src/router/optimized-route-matching.ts +0 -253
- package/src/router/route-building.ts +0 -212
- package/src/router/route-compiler.ts +0 -702
- package/src/router/route-matching.ts +0 -349
- package/src/router/route-trie.ts +0 -464
- package/src/router/router.ts +0 -1581
- package/src/router/server.ts +0 -462
- package/src/router/validation-integration.ts +0 -443
- package/src/router/view-rendering.ts +0 -233
- package/src/router/websocket.ts +0 -100
- package/src/routing/route-caching.ts +0 -402
- package/src/routing/route-throttling.ts +0 -469
- package/src/routing/subdomain-routing.ts +0 -492
- package/src/session/database-store.ts +0 -109
- package/src/session/file-store.ts +0 -148
- package/src/session/index.ts +0 -244
- package/src/session/memory-store.ts +0 -88
- package/src/session/redis-store.ts +0 -93
- package/src/streaming/index.ts +0 -17
- package/src/streaming/sse-handler.ts +0 -482
- package/src/streaming/stream-handler.ts +0 -552
- package/src/testing/auth-testing.ts +0 -446
- package/src/testing/file-upload-testing.ts +0 -543
- package/src/testing/index.ts +0 -10
- package/src/testing/middleware-testing.ts +0 -322
- package/src/testing/model-binding-testing.ts +0 -645
- package/src/testing/performance-testing.ts +0 -738
- package/src/testing/test-client.ts +0 -310
- package/src/testing/test-request.ts +0 -315
- package/src/testing/test-response.ts +0 -332
- package/src/testing/types.ts +0 -224
- package/src/testing/websocket-testing.ts +0 -590
- package/src/types/controller-types.ts +0 -385
- package/src/types/core.ts +0 -683
- package/src/types/middleware-types.ts +0 -419
- package/src/types/request-response-augmentation.ts +0 -489
- package/src/types/route-inference.ts +0 -357
- package/src/types.ts +0 -2055
- package/src/url.ts +0 -126
- package/src/utils/index.ts +0 -1
- package/src/utils/query-preservation.ts +0 -201
- package/src/utils.ts +0 -327
- package/src/validation/validator.ts +0 -685
- package/src/websocket/clustering.ts +0 -762
|
@@ -1,621 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Advanced Error Handling - Graceful Degradation
|
|
3
|
-
*
|
|
4
|
-
* Handles partial failures with fallback mechanisms and service degradation
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { EnhancedRequest } from '../types'
|
|
8
|
-
import type { ErrorContext, RouterException } from './exceptions'
|
|
9
|
-
|
|
10
|
-
export interface DegradationConfig {
|
|
11
|
-
enabled: boolean
|
|
12
|
-
fallbackStrategies: {
|
|
13
|
-
[serviceName: string]: FallbackStrategy
|
|
14
|
-
}
|
|
15
|
-
healthChecks: {
|
|
16
|
-
[serviceName: string]: HealthCheckConfig
|
|
17
|
-
}
|
|
18
|
-
monitoring: {
|
|
19
|
-
enabled: boolean
|
|
20
|
-
alertThresholds: {
|
|
21
|
-
errorRate: number
|
|
22
|
-
responseTime: number
|
|
23
|
-
availability: number
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface FallbackStrategy {
|
|
29
|
-
type: 'cache' | 'static' | 'simplified' | 'redirect' | 'custom'
|
|
30
|
-
priority: number
|
|
31
|
-
timeout: number
|
|
32
|
-
retries: number
|
|
33
|
-
backoff: {
|
|
34
|
-
type: 'fixed' | 'exponential'
|
|
35
|
-
delay: number
|
|
36
|
-
maxDelay?: number
|
|
37
|
-
}
|
|
38
|
-
fallbackHandler?: (error: RouterException, context: ErrorContext) => Promise<Response>
|
|
39
|
-
cacheConfig?: {
|
|
40
|
-
key: string
|
|
41
|
-
ttl: number
|
|
42
|
-
staleWhileRevalidate: boolean
|
|
43
|
-
}
|
|
44
|
-
staticResponse?: {
|
|
45
|
-
status: number
|
|
46
|
-
body: any
|
|
47
|
-
headers?: Record<string, string>
|
|
48
|
-
}
|
|
49
|
-
redirectConfig?: {
|
|
50
|
-
url: string
|
|
51
|
-
permanent: boolean
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface HealthCheckConfig {
|
|
56
|
-
enabled: boolean
|
|
57
|
-
endpoint: string
|
|
58
|
-
interval: number
|
|
59
|
-
timeout: number
|
|
60
|
-
retries: number
|
|
61
|
-
expectedStatus: number[]
|
|
62
|
-
expectedBody?: string | RegExp
|
|
63
|
-
headers?: Record<string, string>
|
|
64
|
-
onHealthy?: () => void
|
|
65
|
-
onUnhealthy?: (error: Error) => void
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface ServiceHealth {
|
|
69
|
-
name: string
|
|
70
|
-
status: 'healthy' | 'degraded' | 'unhealthy'
|
|
71
|
-
lastCheck: Date
|
|
72
|
-
responseTime: number
|
|
73
|
-
errorRate: number
|
|
74
|
-
availability: number
|
|
75
|
-
consecutiveFailures: number
|
|
76
|
-
metadata?: Record<string, any>
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export interface DegradationContext {
|
|
80
|
-
request: EnhancedRequest
|
|
81
|
-
serviceName: string
|
|
82
|
-
error: RouterException
|
|
83
|
-
attemptCount: number
|
|
84
|
-
startTime: Date
|
|
85
|
-
metadata?: Record<string, any>
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Service health monitor
|
|
90
|
-
*/
|
|
91
|
-
export class ServiceHealthMonitor {
|
|
92
|
-
private healthStatus = new Map<string, ServiceHealth>()
|
|
93
|
-
private healthCheckIntervals = new Map<string, ReturnType<typeof setInterval>>()
|
|
94
|
-
private config: DegradationConfig
|
|
95
|
-
|
|
96
|
-
constructor(config: DegradationConfig) {
|
|
97
|
-
this.config = config
|
|
98
|
-
this.initializeHealthChecks()
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private initializeHealthChecks(): void {
|
|
102
|
-
for (const [serviceName, healthConfig] of Object.entries(this.config.healthChecks)) {
|
|
103
|
-
if (!healthConfig.enabled)
|
|
104
|
-
continue
|
|
105
|
-
|
|
106
|
-
// Initialize health status
|
|
107
|
-
this.healthStatus.set(serviceName, {
|
|
108
|
-
name: serviceName,
|
|
109
|
-
status: 'healthy',
|
|
110
|
-
lastCheck: new Date(),
|
|
111
|
-
responseTime: 0,
|
|
112
|
-
errorRate: 0,
|
|
113
|
-
availability: 100,
|
|
114
|
-
consecutiveFailures: 0,
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
// Start health check interval
|
|
118
|
-
const interval = setInterval(
|
|
119
|
-
() => this.performHealthCheck(serviceName, healthConfig),
|
|
120
|
-
healthConfig.interval,
|
|
121
|
-
)
|
|
122
|
-
this.healthCheckIntervals.set(serviceName, interval)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private async performHealthCheck(serviceName: string, config: HealthCheckConfig): Promise<void> {
|
|
127
|
-
const startTime = Date.now()
|
|
128
|
-
let isHealthy = false
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
const controller = new AbortController()
|
|
132
|
-
const timeoutId = setTimeout(() => controller.abort(), config.timeout)
|
|
133
|
-
|
|
134
|
-
const response = await fetch(config.endpoint, {
|
|
135
|
-
signal: controller.signal,
|
|
136
|
-
headers: config.headers,
|
|
137
|
-
method: 'GET',
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
clearTimeout(timeoutId)
|
|
141
|
-
|
|
142
|
-
// Check status code
|
|
143
|
-
isHealthy = config.expectedStatus.includes(response.status)
|
|
144
|
-
|
|
145
|
-
// Check response body if configured
|
|
146
|
-
if (isHealthy && config.expectedBody) {
|
|
147
|
-
const body = await response.text()
|
|
148
|
-
if (typeof config.expectedBody === 'string') {
|
|
149
|
-
isHealthy = body.includes(config.expectedBody)
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
isHealthy = config.expectedBody.test(body)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
catch (error) {
|
|
157
|
-
console.error(error)
|
|
158
|
-
isHealthy = false
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const responseTime = Date.now() - startTime
|
|
162
|
-
this.updateHealthStatus(serviceName, isHealthy, responseTime)
|
|
163
|
-
|
|
164
|
-
// Call health callbacks
|
|
165
|
-
if (isHealthy && config.onHealthy) {
|
|
166
|
-
config.onHealthy()
|
|
167
|
-
}
|
|
168
|
-
else if (!isHealthy && config.onUnhealthy) {
|
|
169
|
-
config.onUnhealthy(new Error(`Health check failed for ${serviceName}`))
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private updateHealthStatus(serviceName: string, isHealthy: boolean, responseTime: number): void {
|
|
174
|
-
const current = this.healthStatus.get(serviceName)
|
|
175
|
-
if (!current)
|
|
176
|
-
return
|
|
177
|
-
|
|
178
|
-
const updated: ServiceHealth = {
|
|
179
|
-
...current,
|
|
180
|
-
lastCheck: new Date(),
|
|
181
|
-
responseTime,
|
|
182
|
-
consecutiveFailures: isHealthy ? 0 : current.consecutiveFailures + 1,
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Update status based on consecutive failures
|
|
186
|
-
if (updated.consecutiveFailures === 0) {
|
|
187
|
-
updated.status = 'healthy'
|
|
188
|
-
}
|
|
189
|
-
else if (updated.consecutiveFailures < 3) {
|
|
190
|
-
updated.status = 'degraded'
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
updated.status = 'unhealthy'
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Calculate availability (simple moving average)
|
|
197
|
-
const healthyCount = isHealthy ? 1 : 0
|
|
198
|
-
updated.availability = (current.availability * 0.9) + (healthyCount * 10)
|
|
199
|
-
|
|
200
|
-
this.healthStatus.set(serviceName, updated)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
getServiceHealth(serviceName: string): ServiceHealth | undefined {
|
|
204
|
-
return this.healthStatus.get(serviceName)
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
getAllServiceHealth(): ServiceHealth[] {
|
|
208
|
-
return Array.from(this.healthStatus.values())
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
isServiceHealthy(serviceName: string): boolean {
|
|
212
|
-
const health = this.healthStatus.get(serviceName)
|
|
213
|
-
return health?.status === 'healthy'
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
isServiceDegraded(serviceName: string): boolean {
|
|
217
|
-
const health = this.healthStatus.get(serviceName)
|
|
218
|
-
return health?.status === 'degraded'
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
stop(): void {
|
|
222
|
-
for (const interval of this.healthCheckIntervals.values()) {
|
|
223
|
-
clearInterval(interval)
|
|
224
|
-
}
|
|
225
|
-
this.healthCheckIntervals.clear()
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Graceful degradation manager
|
|
231
|
-
*/
|
|
232
|
-
export class GracefulDegradationManager {
|
|
233
|
-
private config: DegradationConfig
|
|
234
|
-
private healthMonitor: ServiceHealthMonitor
|
|
235
|
-
private cache = new Map<string, { data: any, expires: Date }>()
|
|
236
|
-
private metrics = new Map<string, { requests: number, errors: number, totalTime: number }>()
|
|
237
|
-
|
|
238
|
-
constructor(config: DegradationConfig) {
|
|
239
|
-
this.config = config
|
|
240
|
-
this.healthMonitor = new ServiceHealthMonitor(config)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Handle service failure with graceful degradation
|
|
245
|
-
*/
|
|
246
|
-
async handleFailure(
|
|
247
|
-
serviceName: string,
|
|
248
|
-
error: RouterException,
|
|
249
|
-
request: EnhancedRequest,
|
|
250
|
-
originalHandler: () => Promise<Response>,
|
|
251
|
-
): Promise<Response> {
|
|
252
|
-
if (!this.config.enabled) {
|
|
253
|
-
throw error
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const strategy = this.config.fallbackStrategies[serviceName]
|
|
257
|
-
if (!strategy) {
|
|
258
|
-
throw error
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const context: DegradationContext = {
|
|
262
|
-
request,
|
|
263
|
-
serviceName,
|
|
264
|
-
error,
|
|
265
|
-
attemptCount: 1,
|
|
266
|
-
startTime: new Date(),
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return this.executeFallbackStrategy(strategy, context, originalHandler)
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
private async executeFallbackStrategy(
|
|
273
|
-
strategy: FallbackStrategy,
|
|
274
|
-
context: DegradationContext,
|
|
275
|
-
originalHandler: () => Promise<Response>,
|
|
276
|
-
): Promise<Response> {
|
|
277
|
-
// Try with retries and backoff
|
|
278
|
-
if (context.attemptCount <= strategy.retries) {
|
|
279
|
-
try {
|
|
280
|
-
// Apply backoff delay
|
|
281
|
-
if (context.attemptCount > 1) {
|
|
282
|
-
const delay = this.calculateBackoffDelay(strategy.backoff, context.attemptCount)
|
|
283
|
-
await this.sleep(delay)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Retry original handler with timeout
|
|
287
|
-
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
288
|
-
setTimeout(() => reject(new Error('Timeout')), strategy.timeout)
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
const result = await Promise.race([originalHandler(), timeoutPromise])
|
|
292
|
-
|
|
293
|
-
// Success - update metrics and return
|
|
294
|
-
this.updateMetrics(context.serviceName, true, Date.now() - context.startTime.getTime())
|
|
295
|
-
return result
|
|
296
|
-
}
|
|
297
|
-
catch (retryError) {
|
|
298
|
-
console.error(retryError)
|
|
299
|
-
context.attemptCount++
|
|
300
|
-
if (context.attemptCount <= strategy.retries) {
|
|
301
|
-
return this.executeFallbackStrategy(strategy, context, originalHandler)
|
|
302
|
-
}
|
|
303
|
-
// Fall through to fallback strategies
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Update metrics for failure
|
|
308
|
-
this.updateMetrics(context.serviceName, false, Date.now() - context.startTime.getTime())
|
|
309
|
-
|
|
310
|
-
// Execute fallback based on strategy type
|
|
311
|
-
switch (strategy.type) {
|
|
312
|
-
case 'cache':
|
|
313
|
-
return this.handleCacheFallback(strategy, context)
|
|
314
|
-
case 'static':
|
|
315
|
-
return this.handleStaticFallback(strategy, context)
|
|
316
|
-
case 'simplified':
|
|
317
|
-
return this.handleSimplifiedFallback(strategy, context)
|
|
318
|
-
case 'redirect':
|
|
319
|
-
return this.handleRedirectFallback(strategy, context)
|
|
320
|
-
case 'custom':
|
|
321
|
-
return this.handleCustomFallback(strategy, context)
|
|
322
|
-
default:
|
|
323
|
-
throw context.error
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
private async handleCacheFallback(strategy: FallbackStrategy, context: DegradationContext): Promise<Response> {
|
|
328
|
-
if (!strategy.cacheConfig) {
|
|
329
|
-
throw context.error
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const cacheKey = this.generateCacheKey(strategy.cacheConfig.key, context)
|
|
333
|
-
const cached = this.cache.get(cacheKey)
|
|
334
|
-
|
|
335
|
-
if (cached && (cached.expires > new Date() || strategy.cacheConfig.staleWhileRevalidate)) {
|
|
336
|
-
// Return cached response
|
|
337
|
-
const headers = new Headers({
|
|
338
|
-
'Content-Type': 'application/json',
|
|
339
|
-
'X-Cache': 'HIT',
|
|
340
|
-
'X-Degraded': 'true',
|
|
341
|
-
'X-Service-Status': this.healthMonitor.getServiceHealth(context.serviceName)?.status || 'unknown',
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
if (strategy.cacheConfig.staleWhileRevalidate && cached.expires <= new Date()) {
|
|
345
|
-
headers.set('X-Cache', 'STALE')
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
return new Response(JSON.stringify(cached.data), {
|
|
349
|
-
status: 200,
|
|
350
|
-
headers,
|
|
351
|
-
})
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
throw context.error
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
private async handleStaticFallback(strategy: FallbackStrategy, context: DegradationContext): Promise<Response> {
|
|
358
|
-
if (!strategy.staticResponse) {
|
|
359
|
-
throw context.error
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const headers = new Headers(strategy.staticResponse.headers || {})
|
|
363
|
-
headers.set('X-Degraded', 'true')
|
|
364
|
-
headers.set('X-Fallback-Type', 'static')
|
|
365
|
-
headers.set('X-Service-Status', this.healthMonitor.getServiceHealth(context.serviceName)?.status || 'unknown')
|
|
366
|
-
|
|
367
|
-
return new Response(
|
|
368
|
-
typeof strategy.staticResponse.body === 'string'
|
|
369
|
-
? strategy.staticResponse.body
|
|
370
|
-
: JSON.stringify(strategy.staticResponse.body),
|
|
371
|
-
{
|
|
372
|
-
status: strategy.staticResponse.status,
|
|
373
|
-
headers,
|
|
374
|
-
},
|
|
375
|
-
)
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
private async handleSimplifiedFallback(strategy: FallbackStrategy, context: DegradationContext): Promise<Response> {
|
|
379
|
-
// Return a simplified version of the response
|
|
380
|
-
const simplifiedData = {
|
|
381
|
-
message: 'Service temporarily unavailable. Showing simplified view.',
|
|
382
|
-
status: 'degraded',
|
|
383
|
-
timestamp: new Date().toISOString(),
|
|
384
|
-
requestId: context.request.requestId,
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
return new Response(JSON.stringify(simplifiedData), {
|
|
388
|
-
status: 200,
|
|
389
|
-
headers: {
|
|
390
|
-
'Content-Type': 'application/json',
|
|
391
|
-
'X-Degraded': 'true',
|
|
392
|
-
'X-Fallback-Type': 'simplified',
|
|
393
|
-
'X-Service-Status': this.healthMonitor.getServiceHealth(context.serviceName)?.status || 'unknown',
|
|
394
|
-
},
|
|
395
|
-
})
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
private async handleRedirectFallback(strategy: FallbackStrategy, context: DegradationContext): Promise<Response> {
|
|
399
|
-
if (!strategy.redirectConfig) {
|
|
400
|
-
throw context.error
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return new Response(null, {
|
|
404
|
-
status: strategy.redirectConfig.permanent ? 301 : 302,
|
|
405
|
-
headers: {
|
|
406
|
-
'Location': strategy.redirectConfig.url,
|
|
407
|
-
'X-Degraded': 'true',
|
|
408
|
-
'X-Fallback-Type': 'redirect',
|
|
409
|
-
},
|
|
410
|
-
})
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
private async handleCustomFallback(strategy: FallbackStrategy, context: DegradationContext): Promise<Response> {
|
|
414
|
-
if (!strategy.fallbackHandler) {
|
|
415
|
-
throw context.error
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
try {
|
|
419
|
-
const response = await strategy.fallbackHandler(context.error, context.error.context)
|
|
420
|
-
|
|
421
|
-
// Add degradation headers
|
|
422
|
-
response.headers.set('X-Degraded', 'true')
|
|
423
|
-
response.headers.set('X-Fallback-Type', 'custom')
|
|
424
|
-
response.headers.set('X-Service-Status', this.healthMonitor.getServiceHealth(context.serviceName)?.status || 'unknown')
|
|
425
|
-
|
|
426
|
-
return response
|
|
427
|
-
}
|
|
428
|
-
catch (fallbackError) {
|
|
429
|
-
console.error(fallbackError)
|
|
430
|
-
throw context.error
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Cache successful responses for fallback use
|
|
436
|
-
*/
|
|
437
|
-
cacheResponse(serviceName: string, key: string, data: any, ttl: number): void {
|
|
438
|
-
const strategy = this.config.fallbackStrategies[serviceName]
|
|
439
|
-
if (strategy?.type === 'cache' && strategy.cacheConfig) {
|
|
440
|
-
const cacheKey = this.generateCacheKey(key, { serviceName } as DegradationContext)
|
|
441
|
-
this.cache.set(cacheKey, {
|
|
442
|
-
data,
|
|
443
|
-
expires: new Date(Date.now() + ttl * 1000),
|
|
444
|
-
})
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* Get service metrics
|
|
450
|
-
*/
|
|
451
|
-
getServiceMetrics(serviceName: string): { errorRate: number, avgResponseTime: number, totalRequests: number } {
|
|
452
|
-
const metrics = this.metrics.get(serviceName)
|
|
453
|
-
if (!metrics || metrics.requests === 0) {
|
|
454
|
-
return { errorRate: 0, avgResponseTime: 0, totalRequests: 0 }
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
errorRate: (metrics.errors / metrics.requests) * 100,
|
|
459
|
-
avgResponseTime: metrics.totalTime / metrics.requests,
|
|
460
|
-
totalRequests: metrics.requests,
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* Get overall system health
|
|
466
|
-
*/
|
|
467
|
-
getSystemHealth(): { status: 'healthy' | 'degraded' | 'unhealthy', services: ServiceHealth[] } {
|
|
468
|
-
const services = this.healthMonitor.getAllServiceHealth()
|
|
469
|
-
const unhealthyCount = services.filter(s => s.status === 'unhealthy').length
|
|
470
|
-
const degradedCount = services.filter(s => s.status === 'degraded').length
|
|
471
|
-
|
|
472
|
-
let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy'
|
|
473
|
-
|
|
474
|
-
if (unhealthyCount > 0) {
|
|
475
|
-
status = unhealthyCount > services.length / 2 ? 'unhealthy' : 'degraded'
|
|
476
|
-
}
|
|
477
|
-
else if (degradedCount > 0) {
|
|
478
|
-
status = 'degraded'
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
return { status, services }
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
private calculateBackoffDelay(backoff: FallbackStrategy['backoff'], attempt: number): number {
|
|
485
|
-
if (backoff.type === 'fixed') {
|
|
486
|
-
return backoff.delay
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// Exponential backoff
|
|
490
|
-
const delay = backoff.delay * 2 ** (attempt - 1)
|
|
491
|
-
return Math.min(delay, backoff.maxDelay || 30000)
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
private generateCacheKey(template: string, context: DegradationContext): string {
|
|
495
|
-
return template
|
|
496
|
-
.replace('{service}', context.serviceName)
|
|
497
|
-
.replace('{url}', context.request.url)
|
|
498
|
-
.replace('{method}', context.request.method)
|
|
499
|
-
.replace('{userId}', String(context.request.user?.id || 'anonymous'))
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
private updateMetrics(serviceName: string, success: boolean, responseTime: number): void {
|
|
503
|
-
const current = this.metrics.get(serviceName) || { requests: 0, errors: 0, totalTime: 0 }
|
|
504
|
-
|
|
505
|
-
current.requests++
|
|
506
|
-
current.totalTime += responseTime
|
|
507
|
-
if (!success) {
|
|
508
|
-
current.errors++
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
this.metrics.set(serviceName, current)
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
private sleep(ms: number): Promise<void> {
|
|
515
|
-
return new Promise(resolve => setTimeout(resolve, ms))
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
stop(): void {
|
|
519
|
-
this.healthMonitor.stop()
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* Middleware for graceful degradation
|
|
525
|
-
*/
|
|
526
|
-
export function createGracefulDegradationMiddleware(config: DegradationConfig) {
|
|
527
|
-
const manager = new GracefulDegradationManager(config)
|
|
528
|
-
|
|
529
|
-
return async (req: EnhancedRequest, next: () => Promise<Response>): Promise<Response> => {
|
|
530
|
-
try {
|
|
531
|
-
const response = await next()
|
|
532
|
-
|
|
533
|
-
// Cache successful responses if configured
|
|
534
|
-
const serviceName = req.context?.serviceName as string
|
|
535
|
-
if (serviceName && response.ok) {
|
|
536
|
-
const strategy = config.fallbackStrategies[serviceName]
|
|
537
|
-
if (strategy?.type === 'cache' && strategy.cacheConfig) {
|
|
538
|
-
try {
|
|
539
|
-
const data = await response.clone().json()
|
|
540
|
-
manager.cacheResponse(serviceName, strategy.cacheConfig.key, data, strategy.cacheConfig.ttl)
|
|
541
|
-
}
|
|
542
|
-
catch {
|
|
543
|
-
// Ignore cache errors
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
return response
|
|
549
|
-
}
|
|
550
|
-
catch (error) {
|
|
551
|
-
const serviceName = req.context?.serviceName as string
|
|
552
|
-
|
|
553
|
-
if (serviceName && error instanceof Error) {
|
|
554
|
-
// Convert to RouterException if needed
|
|
555
|
-
const routerError = error as RouterException
|
|
556
|
-
|
|
557
|
-
try {
|
|
558
|
-
return await manager.handleFailure(serviceName, routerError, req, next)
|
|
559
|
-
}
|
|
560
|
-
catch (fallbackError) {
|
|
561
|
-
console.error(fallbackError)
|
|
562
|
-
// If all fallbacks fail, throw original error
|
|
563
|
-
throw error
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
throw error
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Factory functions for common degradation strategies
|
|
574
|
-
*/
|
|
575
|
-
export const DegradationStrategies = {
|
|
576
|
-
cacheFirst: (ttl: number = 3600, staleWhileRevalidate = true): FallbackStrategy => ({
|
|
577
|
-
type: 'cache',
|
|
578
|
-
priority: 1,
|
|
579
|
-
timeout: 5000,
|
|
580
|
-
retries: 2,
|
|
581
|
-
backoff: { type: 'exponential', delay: 1000, maxDelay: 5000 },
|
|
582
|
-
cacheConfig: {
|
|
583
|
-
key: '{service}:{url}:{method}',
|
|
584
|
-
ttl,
|
|
585
|
-
staleWhileRevalidate,
|
|
586
|
-
},
|
|
587
|
-
}),
|
|
588
|
-
|
|
589
|
-
staticFallback: (body: any, status = 200): FallbackStrategy => ({
|
|
590
|
-
type: 'static',
|
|
591
|
-
priority: 2,
|
|
592
|
-
timeout: 1000,
|
|
593
|
-
retries: 0,
|
|
594
|
-
backoff: { type: 'fixed', delay: 0 },
|
|
595
|
-
staticResponse: {
|
|
596
|
-
status,
|
|
597
|
-
body,
|
|
598
|
-
headers: { 'Content-Type': 'application/json' },
|
|
599
|
-
},
|
|
600
|
-
}),
|
|
601
|
-
|
|
602
|
-
simplifiedView: (): FallbackStrategy => ({
|
|
603
|
-
type: 'simplified',
|
|
604
|
-
priority: 3,
|
|
605
|
-
timeout: 1000,
|
|
606
|
-
retries: 0,
|
|
607
|
-
backoff: { type: 'fixed', delay: 0 },
|
|
608
|
-
}),
|
|
609
|
-
|
|
610
|
-
redirectToMaintenance: (url: string): FallbackStrategy => ({
|
|
611
|
-
type: 'redirect',
|
|
612
|
-
priority: 4,
|
|
613
|
-
timeout: 1000,
|
|
614
|
-
retries: 0,
|
|
615
|
-
backoff: { type: 'fixed', delay: 0 },
|
|
616
|
-
redirectConfig: {
|
|
617
|
-
url,
|
|
618
|
-
permanent: false,
|
|
619
|
-
},
|
|
620
|
-
}),
|
|
621
|
-
}
|
package/src/errors/index.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Advanced Error Handling - Main Export File
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
// Circuit breaker
|
|
6
|
-
export * from './circuit-breaker'
|
|
7
|
-
|
|
8
|
-
// Main error handler middleware
|
|
9
|
-
export { createAdvancedErrorHandler } from './error-handler'
|
|
10
|
-
|
|
11
|
-
// Error reporting
|
|
12
|
-
export * from './error-reporting'
|
|
13
|
-
|
|
14
|
-
// Exception hierarchy
|
|
15
|
-
export * from './exceptions'
|
|
16
|
-
|
|
17
|
-
// Graceful degradation
|
|
18
|
-
export * from './graceful-degradation'
|
|
19
|
-
|
|
20
|
-
// Enhanced router errors
|
|
21
|
-
export * from './router-errors'
|