@stacksjs/bun-router 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth.d.ts +134 -0
- package/dist/cache/lru-cache.d.ts +139 -0
- package/dist/cache/middleware-memoization.d.ts +151 -0
- package/dist/cache/route-cache-warmer.d.ts +161 -0
- package/dist/cache/sqlite-cache.d.ts +209 -0
- package/dist/cache/streaming-cache.d.ts +132 -0
- package/dist/cli/colors.d.ts +15 -0
- package/dist/cli/index.d.ts +10 -0
- package/dist/cli/middleware.d.ts +31 -0
- package/dist/cli/openapi.d.ts +17 -0
- package/dist/cli/router.d.ts +15 -0
- package/dist/cli/routes.d.ts +29 -0
- package/dist/cli/utils.d.ts +74 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/container/container.d.ts +273 -0
- package/dist/container/contextual-binding.d.ts +240 -0
- package/dist/container/decorators.d.ts +141 -0
- package/dist/container/service-provider.d.ts +285 -0
- package/dist/development/hot-reload.d.ts +187 -0
- package/dist/development/index.d.ts +167 -0
- package/dist/development/performance-profiler.d.ts +217 -0
- package/dist/development/route-debugger.d.ts +154 -0
- package/dist/development/route-inspector.d.ts +211 -0
- package/dist/development/typescript-utilities.d.ts +209 -0
- package/dist/docs.d.ts +11 -0
- package/dist/errors/circuit-breaker.d.ts +195 -0
- package/dist/errors/error-handler.d.ts +88 -0
- package/dist/errors/error-reporting.d.ts +144 -0
- package/dist/errors/exceptions.d.ts +184 -0
- package/dist/errors/graceful-degradation.d.ts +154 -0
- package/dist/errors/index.d.ts +9 -0
- package/dist/errors/router-errors.d.ts +110 -0
- package/dist/file-serving/static-files.d.ts +143 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +319 -205
- package/dist/middleware/auth.d.ts +63 -0
- package/dist/middleware/content_security_policy.d.ts +53 -0
- package/dist/middleware/cors.d.ts +5 -0
- package/dist/middleware/csrf.d.ts +7 -0
- package/dist/middleware/ddos_protection.d.ts +39 -0
- package/dist/middleware/file_security.d.ts +26 -0
- package/dist/middleware/file_upload.d.ts +38 -0
- package/dist/middleware/helmet.d.ts +54 -0
- package/dist/middleware/index.d.ts +44 -0
- package/dist/middleware/input_validation.d.ts +45 -0
- package/dist/middleware/json_body.d.ts +4 -0
- package/dist/middleware/performance_alerting.d.ts +87 -0
- package/dist/middleware/performance_dashboard.d.ts +87 -0
- package/dist/middleware/performance_monitor.d.ts +209 -0
- package/dist/middleware/pipeline.d.ts +131 -0
- package/dist/middleware/rate_limit.d.ts +36 -0
- package/dist/middleware/request_id.d.ts +4 -0
- package/dist/middleware/request_signing.d.ts +153 -0
- package/dist/middleware/request_tracer.d.ts +72 -0
- package/dist/middleware/response_cache.d.ts +97 -0
- package/dist/middleware/security.d.ts +69 -0
- package/dist/middleware/security_suite.d.ts +46 -0
- package/dist/middleware/session.d.ts +23 -0
- package/dist/model-binding/index.d.ts +2 -0
- package/dist/model-binding/model-middleware.d.ts +118 -0
- package/dist/model-binding/model-registry.d.ts +164 -0
- package/dist/model-binding.d.ts +185 -0
- package/dist/model-resolver-factory.d.ts +31 -0
- package/dist/observability/correlation.d.ts +187 -0
- package/dist/observability/health-checks.d.ts +186 -0
- package/dist/observability/index.d.ts +60 -0
- package/dist/observability/integration.d.ts +147 -0
- package/dist/observability/metrics.d.ts +184 -0
- package/dist/observability/tracing.d.ts +187 -0
- package/dist/optimization/bun-utilities.d.ts +224 -0
- package/dist/query-builder-integration.d.ts +36 -0
- package/dist/request/context.d.ts +34 -0
- package/dist/request/enhanced-request.d.ts +213 -0
- package/dist/request/macros.d.ts +300 -0
- package/dist/response/macros.d.ts +245 -0
- package/dist/response/response-factory.d.ts +133 -0
- package/dist/router/api-routes.d.ts +5 -0
- package/dist/router/file-based-routing.d.ts +118 -0
- package/dist/router/file-streaming.d.ts +5 -0
- package/dist/router/fluent-router.d.ts +315 -0
- package/dist/router/fluent-routing.d.ts +271 -0
- package/dist/router/group-organization.d.ts +5 -0
- package/dist/router/handler-resolver.d.ts +24 -0
- package/dist/router/http-methods.d.ts +10 -0
- package/dist/router/index.d.ts +113 -0
- package/dist/router/middleware-groups.d.ts +94 -0
- package/dist/router/middleware-integration.d.ts +112 -0
- package/dist/router/middleware.d.ts +5 -0
- package/dist/router/model-binding.d.ts +5 -0
- package/dist/router/optimized-route-matching.d.ts +6 -0
- package/dist/router/route-building.d.ts +5 -0
- package/dist/router/route-compiler.d.ts +153 -0
- package/dist/router/route-matching.d.ts +5 -0
- package/dist/router/route-trie.d.ts +114 -0
- package/dist/router/router.d.ts +276 -0
- package/dist/router/server.d.ts +5 -0
- package/dist/router/validation-integration.d.ts +170 -0
- package/dist/router/view-rendering.d.ts +5 -0
- package/dist/router/websocket.d.ts +5 -0
- package/dist/routing/route-caching.d.ts +129 -0
- package/dist/routing/route-throttling.d.ts +150 -0
- package/dist/routing/subdomain-routing.d.ts +206 -0
- package/dist/session/database-store.d.ts +51 -0
- package/dist/session/file-store.d.ts +22 -0
- package/dist/session/index.d.ts +106 -0
- package/dist/session/memory-store.d.ts +21 -0
- package/dist/session/redis-store.d.ts +31 -0
- package/dist/streaming/index.d.ts +3 -0
- package/dist/streaming/sse-handler.d.ts +138 -0
- package/dist/streaming/stream-handler.d.ts +114 -0
- package/dist/testing/auth-testing.d.ts +156 -0
- package/dist/testing/file-upload-testing.d.ts +190 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/middleware-testing.d.ts +138 -0
- package/dist/testing/model-binding-testing.d.ts +186 -0
- package/dist/testing/performance-testing.d.ts +235 -0
- package/dist/testing/test-client.d.ts +117 -0
- package/dist/testing/test-request.d.ts +85 -0
- package/dist/testing/test-response.d.ts +90 -0
- package/dist/testing/types.d.ts +207 -0
- package/dist/testing/websocket-testing.d.ts +227 -0
- package/dist/types/controller-types.d.ts +208 -0
- package/dist/types/core.d.ts +543 -0
- package/dist/types/middleware-types.d.ts +227 -0
- package/dist/types/request-response-augmentation.d.ts +261 -0
- package/dist/types/route-inference.d.ts +168 -0
- package/dist/types.d.ts +1776 -0
- package/dist/url.d.ts +56 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/query-preservation.d.ts +48 -0
- package/dist/utils.d.ts +69 -0
- package/dist/validation/validator.d.ts +140 -0
- package/dist/websocket/clustering.d.ts +185 -0
- package/package.json +3 -2
- package/src/auth.ts +469 -0
- package/src/cache/lru-cache.ts +457 -0
- package/src/cache/middleware-memoization.ts +531 -0
- package/src/cache/route-cache-warmer.ts +486 -0
- package/src/cache/sqlite-cache.ts +783 -0
- package/src/cache/streaming-cache.ts +572 -0
- package/src/cli/colors.ts +29 -0
- package/src/cli/index.ts +287 -0
- package/src/cli/middleware.ts +291 -0
- package/src/cli/openapi.ts +407 -0
- package/src/cli/router.ts +188 -0
- package/src/cli/routes.ts +265 -0
- package/src/cli/utils.ts +531 -0
- package/src/cli.ts +5 -0
- package/src/config.ts +322 -0
- package/src/container/container.ts +740 -0
- package/src/container/contextual-binding.ts +603 -0
- package/src/container/decorators.ts +359 -0
- package/src/container/service-provider.ts +596 -0
- package/src/development/hot-reload.ts +673 -0
- package/src/development/index.ts +499 -0
- package/src/development/performance-profiler.ts +717 -0
- package/src/development/route-debugger.ts +527 -0
- package/src/development/route-inspector.ts +749 -0
- package/src/development/typescript-utilities.ts +678 -0
- package/src/docs.ts +397 -0
- package/src/errors/circuit-breaker.ts +732 -0
- package/src/errors/error-handler.ts +569 -0
- package/src/errors/error-reporting.ts +672 -0
- package/src/errors/exceptions.ts +536 -0
- package/src/errors/graceful-degradation.ts +621 -0
- package/src/errors/index.ts +21 -0
- package/src/errors/router-errors.ts +632 -0
- package/src/file-serving/static-files.ts +581 -0
- package/src/index.ts +14 -0
- package/src/middleware/auth.ts +219 -0
- package/src/middleware/content_security_policy.ts +215 -0
- package/src/middleware/cors.ts +74 -0
- package/src/middleware/csrf.ts +108 -0
- package/src/middleware/ddos_protection.ts +255 -0
- package/src/middleware/file_security.ts +191 -0
- package/src/middleware/file_upload.ts +264 -0
- package/src/middleware/helmet.ts +268 -0
- package/src/middleware/index.ts +117 -0
- package/src/middleware/input_validation.ts +449 -0
- package/src/middleware/json_body.ts +37 -0
- package/src/middleware/performance_alerting.ts +538 -0
- package/src/middleware/performance_dashboard.ts +661 -0
- package/src/middleware/performance_monitor.ts +943 -0
- package/src/middleware/pipeline.ts +489 -0
- package/src/middleware/rate_limit.ts +245 -0
- package/src/middleware/request_id.ts +36 -0
- package/src/middleware/request_signing.ts +636 -0
- package/src/middleware/request_tracer.ts +636 -0
- package/src/middleware/response_cache.ts +743 -0
- package/src/middleware/security.ts +482 -0
- package/src/middleware/security_suite.ts +257 -0
- package/src/middleware/session.ts +91 -0
- package/src/model-binding/index.ts +18 -0
- package/src/model-binding/model-middleware.ts +425 -0
- package/src/model-binding/model-registry.ts +550 -0
- package/src/model-binding.ts +370 -0
- package/src/model-resolver-factory.ts +106 -0
- package/src/observability/correlation.ts +691 -0
- package/src/observability/health-checks.ts +729 -0
- package/src/observability/index.ts +184 -0
- package/src/observability/integration.ts +548 -0
- package/src/observability/metrics.ts +753 -0
- package/src/observability/tracing.ts +638 -0
- package/src/optimization/bun-utilities.ts +778 -0
- package/src/query-builder-integration.ts +137 -0
- package/src/request/context.ts +62 -0
- package/src/request/enhanced-request.ts +857 -0
- package/src/request/macros.ts +688 -0
- package/src/response/macros.ts +612 -0
- package/src/response/response-factory.ts +588 -0
- package/src/router/api-routes.ts +243 -0
- package/src/router/file-based-routing.ts +670 -0
- package/src/router/file-streaming.ts +372 -0
- package/src/router/fluent-router.ts +927 -0
- package/src/router/fluent-routing.ts +797 -0
- package/src/router/group-organization.ts +213 -0
- package/src/router/handler-resolver.ts +291 -0
- package/src/router/http-methods.ts +377 -0
- package/src/router/index.ts +185 -0
- package/src/router/middleware-groups.ts +222 -0
- package/src/router/middleware-integration.ts +399 -0
- package/src/router/middleware.ts +231 -0
- package/src/router/model-binding.ts +215 -0
- package/src/router/optimized-route-matching.ts +253 -0
- package/src/router/route-building.ts +212 -0
- package/src/router/route-compiler.ts +702 -0
- package/src/router/route-matching.ts +349 -0
- package/src/router/route-trie.ts +464 -0
- package/src/router/router.ts +1508 -0
- package/src/router/server.ts +406 -0
- package/src/router/validation-integration.ts +443 -0
- package/src/router/view-rendering.ts +233 -0
- package/src/router/websocket.ts +100 -0
- package/src/routing/route-caching.ts +402 -0
- package/src/routing/route-throttling.ts +469 -0
- package/src/routing/subdomain-routing.ts +492 -0
- package/src/session/database-store.ts +109 -0
- package/src/session/file-store.ts +148 -0
- package/src/session/index.ts +244 -0
- package/src/session/memory-store.ts +88 -0
- package/src/session/redis-store.ts +93 -0
- package/src/streaming/index.ts +17 -0
- package/src/streaming/sse-handler.ts +482 -0
- package/src/streaming/stream-handler.ts +552 -0
- package/src/testing/auth-testing.ts +446 -0
- package/src/testing/file-upload-testing.ts +543 -0
- package/src/testing/index.ts +10 -0
- package/src/testing/middleware-testing.ts +322 -0
- package/src/testing/model-binding-testing.ts +645 -0
- package/src/testing/performance-testing.ts +738 -0
- package/src/testing/test-client.ts +310 -0
- package/src/testing/test-request.ts +315 -0
- package/src/testing/test-response.ts +332 -0
- package/src/testing/types.ts +224 -0
- package/src/testing/websocket-testing.ts +590 -0
- package/src/types/controller-types.ts +385 -0
- package/src/types/core.ts +683 -0
- package/src/types/middleware-types.ts +419 -0
- package/src/types/request-response-augmentation.ts +489 -0
- package/src/types/route-inference.ts +357 -0
- package/src/types.ts +2049 -0
- package/src/url.ts +126 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/query-preservation.ts +201 -0
- package/src/utils.ts +327 -0
- package/src/validation/validator.ts +685 -0
- package/src/websocket/clustering.ts +762 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observability & Monitoring - Request Correlation IDs across Services
|
|
3
|
+
*
|
|
4
|
+
* Enterprise-grade request correlation for distributed systems tracing
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { EnhancedRequest } from '../types'
|
|
8
|
+
|
|
9
|
+
export interface CorrelationConfig {
|
|
10
|
+
headerName?: string
|
|
11
|
+
generateId?: () => string
|
|
12
|
+
propagateHeaders?: string[]
|
|
13
|
+
enableLogging?: boolean
|
|
14
|
+
logLevel?: 'debug' | 'info' | 'warn' | 'error'
|
|
15
|
+
maxIdLength?: number
|
|
16
|
+
validateId?: (id: string) => boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CorrelationContext {
|
|
20
|
+
correlationId: string
|
|
21
|
+
parentId?: string
|
|
22
|
+
spanId?: string
|
|
23
|
+
traceId?: string
|
|
24
|
+
userId?: string
|
|
25
|
+
sessionId?: string
|
|
26
|
+
requestId?: string
|
|
27
|
+
metadata: Record<string, any>
|
|
28
|
+
startTime: number
|
|
29
|
+
headers: Record<string, string>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ServiceCall {
|
|
33
|
+
serviceName: string
|
|
34
|
+
method: string
|
|
35
|
+
url: string
|
|
36
|
+
correlationId: string
|
|
37
|
+
parentId?: string
|
|
38
|
+
startTime: number
|
|
39
|
+
endTime?: number
|
|
40
|
+
duration?: number
|
|
41
|
+
status?: number
|
|
42
|
+
error?: string
|
|
43
|
+
metadata?: Record<string, any>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Correlation ID manager
|
|
48
|
+
*/
|
|
49
|
+
export class CorrelationManager {
|
|
50
|
+
private config: CorrelationConfig
|
|
51
|
+
private contexts = new Map<string, CorrelationContext>()
|
|
52
|
+
private serviceCalls = new Map<string, ServiceCall[]>()
|
|
53
|
+
private activeRequests = new Set<string>()
|
|
54
|
+
|
|
55
|
+
constructor(config: CorrelationConfig = {}) {
|
|
56
|
+
this.config = {
|
|
57
|
+
headerName: 'x-correlation-id',
|
|
58
|
+
generateId: this.generateUUID,
|
|
59
|
+
propagateHeaders: [
|
|
60
|
+
'x-correlation-id',
|
|
61
|
+
'x-request-id',
|
|
62
|
+
'x-trace-id',
|
|
63
|
+
'x-span-id',
|
|
64
|
+
'x-user-id',
|
|
65
|
+
'x-session-id',
|
|
66
|
+
],
|
|
67
|
+
enableLogging: true,
|
|
68
|
+
logLevel: 'info',
|
|
69
|
+
maxIdLength: 128,
|
|
70
|
+
validateId: this.isValidId,
|
|
71
|
+
...config,
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generate UUID v4
|
|
77
|
+
*/
|
|
78
|
+
private generateUUID(): string {
|
|
79
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
80
|
+
const r = Math.random() * 16 | 0
|
|
81
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8)
|
|
82
|
+
return v.toString(16)
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validate correlation ID format
|
|
88
|
+
*/
|
|
89
|
+
private isValidId(id: string): boolean {
|
|
90
|
+
return typeof id === 'string'
|
|
91
|
+
&& id.length > 0
|
|
92
|
+
&& id.length <= 128
|
|
93
|
+
&& /^[\w\-]+$/.test(id)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Extract correlation context from request
|
|
98
|
+
*/
|
|
99
|
+
extractContext(req: EnhancedRequest): CorrelationContext {
|
|
100
|
+
const headers = req.headers
|
|
101
|
+
|
|
102
|
+
// Try to get existing correlation ID
|
|
103
|
+
let correlationId = headers.get(this.config.headerName!)
|
|
104
|
+
|| headers.get('x-request-id')
|
|
105
|
+
|| headers.get('x-trace-id')
|
|
106
|
+
|
|
107
|
+
// Generate new ID if not found or invalid
|
|
108
|
+
if (!correlationId || !this.config.validateId!(correlationId)) {
|
|
109
|
+
correlationId = this.config.generateId!()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Extract other correlation headers
|
|
113
|
+
const parentId = headers.get('x-parent-id')
|
|
114
|
+
const spanId = headers.get('x-span-id')
|
|
115
|
+
const traceId = headers.get('x-trace-id')
|
|
116
|
+
const userId = headers.get('x-user-id')
|
|
117
|
+
const sessionId = headers.get('x-session-id')
|
|
118
|
+
const requestId = headers.get('x-request-id')
|
|
119
|
+
|
|
120
|
+
// Build propagation headers
|
|
121
|
+
const propagationHeaders: Record<string, string> = {}
|
|
122
|
+
for (const headerName of this.config.propagateHeaders!) {
|
|
123
|
+
const value = headers.get(headerName)
|
|
124
|
+
if (value) {
|
|
125
|
+
propagationHeaders[headerName] = value
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Ensure correlation ID is in propagation headers
|
|
130
|
+
propagationHeaders[this.config.headerName!] = correlationId
|
|
131
|
+
|
|
132
|
+
const context: CorrelationContext = {
|
|
133
|
+
correlationId,
|
|
134
|
+
parentId: parentId || undefined,
|
|
135
|
+
spanId: spanId || undefined,
|
|
136
|
+
traceId: traceId || correlationId,
|
|
137
|
+
userId: userId || undefined,
|
|
138
|
+
sessionId: sessionId || undefined,
|
|
139
|
+
requestId: requestId || correlationId,
|
|
140
|
+
metadata: {
|
|
141
|
+
method: req.method,
|
|
142
|
+
url: req.url,
|
|
143
|
+
userAgent: headers.get('user-agent') || '',
|
|
144
|
+
remoteAddr: headers.get('x-forwarded-for')
|
|
145
|
+
|| headers.get('x-real-ip')
|
|
146
|
+
|| 'unknown',
|
|
147
|
+
},
|
|
148
|
+
startTime: Date.now(),
|
|
149
|
+
headers: propagationHeaders,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Store context
|
|
153
|
+
this.contexts.set(correlationId, context)
|
|
154
|
+
this.activeRequests.add(correlationId)
|
|
155
|
+
|
|
156
|
+
// Log context creation
|
|
157
|
+
if (this.config.enableLogging) {
|
|
158
|
+
this.log('info', `Created correlation context: ${correlationId}`, {
|
|
159
|
+
correlationId,
|
|
160
|
+
method: req.method,
|
|
161
|
+
url: req.url,
|
|
162
|
+
parentId,
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return context
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get correlation context by ID
|
|
171
|
+
*/
|
|
172
|
+
getContext(correlationId: string): CorrelationContext | undefined {
|
|
173
|
+
return this.contexts.get(correlationId)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Update correlation context
|
|
178
|
+
*/
|
|
179
|
+
updateContext(correlationId: string, updates: Partial<CorrelationContext>): void {
|
|
180
|
+
const context = this.contexts.get(correlationId)
|
|
181
|
+
if (context) {
|
|
182
|
+
Object.assign(context, updates)
|
|
183
|
+
this.contexts.set(correlationId, context)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Add metadata to correlation context
|
|
189
|
+
*/
|
|
190
|
+
addMetadata(correlationId: string, metadata: Record<string, any>): void {
|
|
191
|
+
const context = this.contexts.get(correlationId)
|
|
192
|
+
if (context) {
|
|
193
|
+
Object.assign(context.metadata, metadata)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Create child correlation context
|
|
199
|
+
*/
|
|
200
|
+
createChildContext(parentId: string, serviceName: string): CorrelationContext {
|
|
201
|
+
const parent = this.contexts.get(parentId)
|
|
202
|
+
if (!parent) {
|
|
203
|
+
throw new Error(`Parent context not found: ${parentId}`)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const childId = this.config.generateId!()
|
|
207
|
+
const childContext: CorrelationContext = {
|
|
208
|
+
correlationId: childId,
|
|
209
|
+
parentId: parent.correlationId,
|
|
210
|
+
spanId: childId,
|
|
211
|
+
traceId: parent.traceId,
|
|
212
|
+
userId: parent.userId,
|
|
213
|
+
sessionId: parent.sessionId,
|
|
214
|
+
requestId: parent.requestId,
|
|
215
|
+
metadata: {
|
|
216
|
+
...parent.metadata,
|
|
217
|
+
serviceName,
|
|
218
|
+
parentService: parent.metadata.serviceName,
|
|
219
|
+
},
|
|
220
|
+
startTime: Date.now(),
|
|
221
|
+
headers: {
|
|
222
|
+
...parent.headers,
|
|
223
|
+
[this.config.headerName!]: childId,
|
|
224
|
+
'x-parent-id': parent.correlationId,
|
|
225
|
+
'x-span-id': childId,
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
this.contexts.set(childId, childContext)
|
|
230
|
+
this.activeRequests.add(childId)
|
|
231
|
+
|
|
232
|
+
return childContext
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Record service call
|
|
237
|
+
*/
|
|
238
|
+
recordServiceCall(call: Omit<ServiceCall, 'startTime'>): void {
|
|
239
|
+
const serviceCall: ServiceCall = {
|
|
240
|
+
...call,
|
|
241
|
+
startTime: Date.now(),
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const calls = this.serviceCalls.get(call.correlationId) || []
|
|
245
|
+
calls.push(serviceCall)
|
|
246
|
+
this.serviceCalls.set(call.correlationId, calls)
|
|
247
|
+
|
|
248
|
+
if (this.config.enableLogging) {
|
|
249
|
+
this.log('debug', `Service call started: ${call.serviceName}`, {
|
|
250
|
+
correlationId: call.correlationId,
|
|
251
|
+
service: call.serviceName,
|
|
252
|
+
method: call.method,
|
|
253
|
+
url: call.url,
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Complete service call
|
|
260
|
+
*/
|
|
261
|
+
completeServiceCall(
|
|
262
|
+
correlationId: string,
|
|
263
|
+
serviceName: string,
|
|
264
|
+
status: number,
|
|
265
|
+
error?: string,
|
|
266
|
+
metadata?: Record<string, any>,
|
|
267
|
+
): void {
|
|
268
|
+
const calls = this.serviceCalls.get(correlationId) || []
|
|
269
|
+
const call = calls.find(c => c.serviceName === serviceName && !c.endTime)
|
|
270
|
+
|
|
271
|
+
if (call) {
|
|
272
|
+
call.endTime = Date.now()
|
|
273
|
+
call.duration = call.endTime - call.startTime
|
|
274
|
+
call.status = status
|
|
275
|
+
call.error = error
|
|
276
|
+
call.metadata = metadata
|
|
277
|
+
|
|
278
|
+
if (this.config.enableLogging) {
|
|
279
|
+
const level = status >= 400 ? 'warn' : 'debug'
|
|
280
|
+
this.log(level, `Service call completed: ${serviceName}`, {
|
|
281
|
+
correlationId,
|
|
282
|
+
service: serviceName,
|
|
283
|
+
status,
|
|
284
|
+
duration: call.duration,
|
|
285
|
+
error,
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Get service calls for correlation ID
|
|
293
|
+
*/
|
|
294
|
+
getServiceCalls(correlationId: string): ServiceCall[] {
|
|
295
|
+
return this.serviceCalls.get(correlationId) || []
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Finish correlation context
|
|
300
|
+
*/
|
|
301
|
+
finishContext(correlationId: string, status?: number, error?: string): void {
|
|
302
|
+
const context = this.contexts.get(correlationId)
|
|
303
|
+
if (context) {
|
|
304
|
+
const duration = Date.now() - context.startTime
|
|
305
|
+
|
|
306
|
+
// Add completion metadata
|
|
307
|
+
context.metadata.endTime = Date.now()
|
|
308
|
+
context.metadata.duration = duration
|
|
309
|
+
context.metadata.status = status
|
|
310
|
+
context.metadata.error = error
|
|
311
|
+
|
|
312
|
+
this.activeRequests.delete(correlationId)
|
|
313
|
+
|
|
314
|
+
if (this.config.enableLogging) {
|
|
315
|
+
const level = status && status >= 400 ? 'warn' : 'info'
|
|
316
|
+
this.log(level, `Correlation context finished: ${correlationId}`, {
|
|
317
|
+
correlationId,
|
|
318
|
+
duration,
|
|
319
|
+
status,
|
|
320
|
+
error,
|
|
321
|
+
serviceCalls: this.getServiceCalls(correlationId).length,
|
|
322
|
+
})
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Create HTTP client with correlation propagation
|
|
329
|
+
*/
|
|
330
|
+
createHttpClient(correlationId: string): {
|
|
331
|
+
fetch: (url: string, options?: RequestInit) => Promise<Response>
|
|
332
|
+
get: (url: string, options?: RequestInit) => Promise<Response>
|
|
333
|
+
post: (url: string, options?: RequestInit) => Promise<Response>
|
|
334
|
+
put: (url: string, options?: RequestInit) => Promise<Response>
|
|
335
|
+
delete: (url: string, options?: RequestInit) => Promise<Response>
|
|
336
|
+
} {
|
|
337
|
+
const context = this.contexts.get(correlationId)
|
|
338
|
+
if (!context) {
|
|
339
|
+
throw new Error(`Correlation context not found: ${correlationId}`)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
fetch: async (url: string, options: RequestInit = {}): Promise<Response> => {
|
|
344
|
+
const serviceName = new URL(url).hostname
|
|
345
|
+
|
|
346
|
+
// Record service call start
|
|
347
|
+
this.recordServiceCall({
|
|
348
|
+
serviceName,
|
|
349
|
+
method: options.method || 'GET',
|
|
350
|
+
url,
|
|
351
|
+
correlationId,
|
|
352
|
+
parentId: context.correlationId,
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
// Add correlation headers
|
|
356
|
+
const headers = new Headers(options.headers)
|
|
357
|
+
Object.entries(context.headers).forEach(([key, value]) => {
|
|
358
|
+
headers.set(key, value)
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
const response = await fetch(url, {
|
|
363
|
+
...options,
|
|
364
|
+
headers,
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
// Record completion
|
|
368
|
+
this.completeServiceCall(
|
|
369
|
+
correlationId,
|
|
370
|
+
serviceName,
|
|
371
|
+
response.status,
|
|
372
|
+
response.ok ? undefined : `HTTP ${response.status}`,
|
|
373
|
+
{
|
|
374
|
+
responseHeaders: Object.fromEntries(response.headers.entries()),
|
|
375
|
+
},
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
return response
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
// Record error
|
|
382
|
+
this.completeServiceCall(
|
|
383
|
+
correlationId,
|
|
384
|
+
serviceName,
|
|
385
|
+
0,
|
|
386
|
+
error instanceof Error ? error.message : String(error),
|
|
387
|
+
)
|
|
388
|
+
throw error
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
get: (url: string, options?: RequestInit): Promise<Response> =>
|
|
393
|
+
this.createHttpClient(correlationId).fetch(url, { ...options, method: 'GET' }),
|
|
394
|
+
|
|
395
|
+
post: (url: string, options?: RequestInit): Promise<Response> =>
|
|
396
|
+
this.createHttpClient(correlationId).fetch(url, { ...options, method: 'POST' }),
|
|
397
|
+
|
|
398
|
+
put: (url: string, options?: RequestInit): Promise<Response> =>
|
|
399
|
+
this.createHttpClient(correlationId).fetch(url, { ...options, method: 'PUT' }),
|
|
400
|
+
|
|
401
|
+
delete: (url: string, options?: RequestInit): Promise<Response> =>
|
|
402
|
+
this.createHttpClient(correlationId).fetch(url, { ...options, method: 'DELETE' }),
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Get correlation trace
|
|
408
|
+
*/
|
|
409
|
+
getTrace(correlationId: string): {
|
|
410
|
+
context: CorrelationContext
|
|
411
|
+
serviceCalls: ServiceCall[]
|
|
412
|
+
children: string[]
|
|
413
|
+
} | null {
|
|
414
|
+
const context = this.contexts.get(correlationId)
|
|
415
|
+
if (!context)
|
|
416
|
+
return null
|
|
417
|
+
|
|
418
|
+
const serviceCalls = this.getServiceCalls(correlationId)
|
|
419
|
+
|
|
420
|
+
// Find child contexts
|
|
421
|
+
const children = Array.from(this.contexts.values())
|
|
422
|
+
.filter(ctx => ctx.parentId === correlationId)
|
|
423
|
+
.map(ctx => ctx.correlationId)
|
|
424
|
+
|
|
425
|
+
return {
|
|
426
|
+
context,
|
|
427
|
+
serviceCalls,
|
|
428
|
+
children,
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Get full trace tree
|
|
434
|
+
*/
|
|
435
|
+
getFullTrace(rootCorrelationId: string): any {
|
|
436
|
+
const buildTrace = (correlationId: string): any => {
|
|
437
|
+
const trace = this.getTrace(correlationId)
|
|
438
|
+
if (!trace)
|
|
439
|
+
return null
|
|
440
|
+
|
|
441
|
+
return {
|
|
442
|
+
...trace,
|
|
443
|
+
children: trace.children.map(childId => buildTrace(childId)).filter(Boolean),
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return buildTrace(rootCorrelationId)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Cleanup old contexts
|
|
452
|
+
*/
|
|
453
|
+
cleanup(maxAge = 3600000): void { // 1 hour default
|
|
454
|
+
const cutoff = Date.now() - maxAge
|
|
455
|
+
|
|
456
|
+
for (const [id, context] of this.contexts.entries()) {
|
|
457
|
+
if (context.startTime < cutoff && !this.activeRequests.has(id)) {
|
|
458
|
+
this.contexts.delete(id)
|
|
459
|
+
this.serviceCalls.delete(id)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Get statistics
|
|
466
|
+
*/
|
|
467
|
+
getStats(): {
|
|
468
|
+
activeContexts: number
|
|
469
|
+
totalContexts: number
|
|
470
|
+
totalServiceCalls: number
|
|
471
|
+
averageCallsPerContext: number
|
|
472
|
+
} {
|
|
473
|
+
const totalServiceCalls = Array.from(this.serviceCalls.values())
|
|
474
|
+
.reduce((sum, calls) => sum + calls.length, 0)
|
|
475
|
+
|
|
476
|
+
return {
|
|
477
|
+
activeContexts: this.activeRequests.size,
|
|
478
|
+
totalContexts: this.contexts.size,
|
|
479
|
+
totalServiceCalls,
|
|
480
|
+
averageCallsPerContext: this.contexts.size > 0 ? totalServiceCalls / this.contexts.size : 0,
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Log with correlation context
|
|
486
|
+
*/
|
|
487
|
+
private log(level: string, message: string, data: any): void {
|
|
488
|
+
if (!this.config.enableLogging)
|
|
489
|
+
return
|
|
490
|
+
|
|
491
|
+
const logData = {
|
|
492
|
+
timestamp: new Date().toISOString(),
|
|
493
|
+
level,
|
|
494
|
+
message,
|
|
495
|
+
...data,
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
console.log(`[CORRELATION] ${JSON.stringify(logData)}`)
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Global correlation manager
|
|
504
|
+
*/
|
|
505
|
+
let globalCorrelationManager: CorrelationManager | null = null
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Initialize global correlation manager
|
|
509
|
+
*/
|
|
510
|
+
export function initializeCorrelation(config?: CorrelationConfig): CorrelationManager {
|
|
511
|
+
globalCorrelationManager = new CorrelationManager(config)
|
|
512
|
+
return globalCorrelationManager
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Get global correlation manager
|
|
517
|
+
*/
|
|
518
|
+
export function getCorrelationManager(): CorrelationManager | null {
|
|
519
|
+
return globalCorrelationManager
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Correlation middleware factory
|
|
524
|
+
*/
|
|
525
|
+
export function createCorrelationMiddleware(config?: Partial<CorrelationConfig>) {
|
|
526
|
+
return async (req: EnhancedRequest, next: () => Promise<Response>): Promise<Response> => {
|
|
527
|
+
const manager = getCorrelationManager()
|
|
528
|
+
if (!manager) {
|
|
529
|
+
return await next()
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Extract correlation context
|
|
533
|
+
const context = manager.extractContext(req)
|
|
534
|
+
|
|
535
|
+
// Add context to request
|
|
536
|
+
;(req as any).correlationId = context.correlationId
|
|
537
|
+
;(req as any).correlationContext = context
|
|
538
|
+
;(req as any).httpClient = manager.createHttpClient(context.correlationId)
|
|
539
|
+
|
|
540
|
+
let response: Response
|
|
541
|
+
try {
|
|
542
|
+
response = await next()
|
|
543
|
+
|
|
544
|
+
// Finish context with success
|
|
545
|
+
manager.finishContext(context.correlationId, response.status)
|
|
546
|
+
}
|
|
547
|
+
catch (error) {
|
|
548
|
+
// Finish context with error
|
|
549
|
+
manager.finishContext(
|
|
550
|
+
context.correlationId,
|
|
551
|
+
500,
|
|
552
|
+
error instanceof Error ? error.message : String(error),
|
|
553
|
+
)
|
|
554
|
+
throw error
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Add correlation headers to response
|
|
558
|
+
const responseHeaders = new Headers(response.headers)
|
|
559
|
+
responseHeaders.set(config?.headerName || 'x-correlation-id', context.correlationId)
|
|
560
|
+
responseHeaders.set('x-request-id', context.requestId || context.correlationId)
|
|
561
|
+
|
|
562
|
+
if (context.traceId) {
|
|
563
|
+
responseHeaders.set('x-trace-id', context.traceId)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return new Response(response.body, {
|
|
567
|
+
status: response.status,
|
|
568
|
+
statusText: response.statusText,
|
|
569
|
+
headers: responseHeaders,
|
|
570
|
+
})
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Correlation helper functions
|
|
576
|
+
*/
|
|
577
|
+
export const CorrelationHelpers = {
|
|
578
|
+
/**
|
|
579
|
+
* Get correlation ID from request
|
|
580
|
+
*/
|
|
581
|
+
getId: (req: EnhancedRequest): string | null => {
|
|
582
|
+
return (req as any).correlationId || null
|
|
583
|
+
},
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Get correlation context from request
|
|
587
|
+
*/
|
|
588
|
+
getContext: (req: EnhancedRequest): CorrelationContext | null => {
|
|
589
|
+
return (req as any).correlationContext || null
|
|
590
|
+
},
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Get HTTP client from request
|
|
594
|
+
*/
|
|
595
|
+
getHttpClient: (req: EnhancedRequest): any => {
|
|
596
|
+
return (req as any).httpClient || null
|
|
597
|
+
},
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Add metadata to current correlation
|
|
601
|
+
*/
|
|
602
|
+
addMetadata: (req: EnhancedRequest, metadata: Record<string, any>): void => {
|
|
603
|
+
const manager = getCorrelationManager()
|
|
604
|
+
const correlationId = CorrelationHelpers.getId(req)
|
|
605
|
+
|
|
606
|
+
if (manager && correlationId) {
|
|
607
|
+
manager.addMetadata(correlationId, metadata)
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Create child context for service call
|
|
613
|
+
*/
|
|
614
|
+
createChild: (req: EnhancedRequest, serviceName: string): CorrelationContext | null => {
|
|
615
|
+
const manager = getCorrelationManager()
|
|
616
|
+
const correlationId = CorrelationHelpers.getId(req)
|
|
617
|
+
|
|
618
|
+
if (manager && correlationId) {
|
|
619
|
+
return manager.createChildContext(correlationId, serviceName)
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return null
|
|
623
|
+
},
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Get trace for current request
|
|
627
|
+
*/
|
|
628
|
+
getTrace: (req: EnhancedRequest): any => {
|
|
629
|
+
const manager = getCorrelationManager()
|
|
630
|
+
const correlationId = CorrelationHelpers.getId(req)
|
|
631
|
+
|
|
632
|
+
if (manager && correlationId) {
|
|
633
|
+
return manager.getFullTrace(correlationId)
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return null
|
|
637
|
+
},
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Correlation endpoint handlers
|
|
642
|
+
*/
|
|
643
|
+
export const CorrelationEndpoints = {
|
|
644
|
+
/**
|
|
645
|
+
* Get correlation trace endpoint
|
|
646
|
+
*/
|
|
647
|
+
trace: async (req: EnhancedRequest): Promise<Response> => {
|
|
648
|
+
const manager = getCorrelationManager()
|
|
649
|
+
if (!manager) {
|
|
650
|
+
return new Response('Correlation not initialized', { status: 500 })
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const url = new URL(req.url)
|
|
654
|
+
const correlationId = url.searchParams.get('id') || CorrelationHelpers.getId(req)
|
|
655
|
+
|
|
656
|
+
if (!correlationId) {
|
|
657
|
+
return new Response('Correlation ID required', { status: 400 })
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const trace = manager.getFullTrace(correlationId)
|
|
661
|
+
if (!trace) {
|
|
662
|
+
return new Response('Trace not found', { status: 404 })
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return new Response(JSON.stringify(trace, null, 2), {
|
|
666
|
+
headers: {
|
|
667
|
+
'Content-Type': 'application/json',
|
|
668
|
+
'Cache-Control': 'no-cache',
|
|
669
|
+
},
|
|
670
|
+
})
|
|
671
|
+
},
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Get correlation statistics endpoint
|
|
675
|
+
*/
|
|
676
|
+
stats: async (_req: EnhancedRequest): Promise<Response> => {
|
|
677
|
+
const manager = getCorrelationManager()
|
|
678
|
+
if (!manager) {
|
|
679
|
+
return new Response('Correlation not initialized', { status: 500 })
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const stats = manager.getStats()
|
|
683
|
+
|
|
684
|
+
return new Response(JSON.stringify(stats, null, 2), {
|
|
685
|
+
headers: {
|
|
686
|
+
'Content-Type': 'application/json',
|
|
687
|
+
'Cache-Control': 'no-cache',
|
|
688
|
+
},
|
|
689
|
+
})
|
|
690
|
+
},
|
|
691
|
+
}
|