@stacksjs/bun-router 0.0.17 → 0.0.18
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/cli.js +1 -1
- package/dist/container/service-provider.d.ts +3 -3
- package/dist/development/performance-profiler.d.ts +3 -3
- package/dist/development/route-debugger.d.ts +1 -1
- package/dist/development/route-inspector.d.ts +2 -2
- package/dist/observability/integration.d.ts +1 -1
- package/dist/response/macros.d.ts +1 -1
- package/dist/router/index.d.ts +7 -7
- package/dist/router/router.d.ts +0 -32
- package/dist/testing/performance-testing.d.ts +2 -2
- package/dist/testing/test-client.d.ts +1 -1
- package/dist/testing/websocket-testing.d.ts +1 -1
- package/package.json +2 -3
- package/src/auth.ts +0 -489
- 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 -786
- package/src/container/contextual-binding.ts +0 -606
- package/src/container/decorators.ts +0 -368
- package/src/container/index.ts +0 -49
- 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 -733
- 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 -623
- package/src/errors/index.ts +0 -21
- package/src/errors/router-errors.ts +0 -632
- package/src/file-serving/static-files.ts +0 -718
- package/src/index.ts +0 -66
- package/src/middleware/auth.ts +0 -220
- package/src/middleware/content_security_policy.ts +0 -215
- package/src/middleware/cors.ts +0 -142
- package/src/middleware/csrf.ts +0 -166
- package/src/middleware/ddos_protection.ts +0 -279
- package/src/middleware/file_security.ts +0 -191
- package/src/middleware/file_upload.ts +0 -275
- package/src/middleware/helmet.ts +0 -268
- package/src/middleware/index.ts +0 -117
- package/src/middleware/input_validation.ts +0 -453
- package/src/middleware/json_body.ts +0 -37
- package/src/middleware/performance_alerting.ts +0 -540
- package/src/middleware/performance_dashboard.ts +0 -661
- package/src/middleware/performance_monitor.ts +0 -945
- package/src/middleware/pipeline.ts +0 -489
- package/src/middleware/rate_limit.ts +0 -251
- package/src/middleware/request_id.ts +0 -36
- package/src/middleware/request_signing.ts +0 -638
- package/src/middleware/request_tracer.ts +0 -653
- package/src/middleware/response_cache.ts +0 -778
- 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 -109
- package/src/request/enhanced-request.ts +0 -876
- package/src/request/macros.ts +0 -822
- package/src/response/macros.ts +0 -665
- package/src/response/response-factory.ts +0 -596
- package/src/router/api-routes.ts +0 -243
- package/src/router/file-based-routing.ts +0 -697
- package/src/router/file-streaming.ts +0 -383
- package/src/router/fluent-routing.ts +0 -866
- package/src/router/group-organization.ts +0 -229
- package/src/router/handler-resolver.ts +0 -327
- package/src/router/http-methods.ts +0 -407
- package/src/router/index.ts +0 -193
- package/src/router/middleware-groups.ts +0 -222
- package/src/router/middleware-integration.ts +0 -399
- package/src/router/middleware.ts +0 -234
- package/src/router/model-binding.ts +0 -215
- package/src/router/optimized-route-matching.ts +0 -302
- package/src/router/route-building.ts +0 -221
- package/src/router/route-compiler.ts +0 -691
- package/src/router/route-matching.ts +0 -368
- package/src/router/route-trie.ts +0 -450
- package/src/router/router.ts +0 -1616
- package/src/router/server.ts +0 -684
- package/src/router/validation-integration.ts +0 -449
- 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 -448
- 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 -422
- package/src/types/request-response-augmentation.ts +0 -489
- package/src/types/route-inference.ts +0 -372
- package/src/types.ts +0 -2123
- package/src/url.ts +0 -131
- package/src/utils/index.ts +0 -1
- package/src/utils/query-preservation.ts +0 -201
- package/src/utils.ts +0 -395
- package/src/validation/validator.ts +0 -688
- package/src/websocket/clustering.ts +0 -769
|
@@ -1,717 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Development Tools - Performance Profiler
|
|
3
|
-
*
|
|
4
|
-
* Performance profiling, benchmarking, and optimization analysis
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { EnhancedRequest } from '../types'
|
|
8
|
-
import process from 'node:process'
|
|
9
|
-
|
|
10
|
-
export interface ProfileConfig {
|
|
11
|
-
enabled?: boolean
|
|
12
|
-
sampleRate?: number // 0-1, percentage of requests to profile
|
|
13
|
-
includeMemory?: boolean
|
|
14
|
-
includeCpu?: boolean
|
|
15
|
-
includeGc?: boolean
|
|
16
|
-
trackSlowQueries?: boolean
|
|
17
|
-
slowQueryThreshold?: number // ms
|
|
18
|
-
maxProfiles?: number
|
|
19
|
-
outputFormat?: 'console' | 'json' | 'flamegraph'
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface ProfilePoint {
|
|
23
|
-
name: string
|
|
24
|
-
timestamp: number
|
|
25
|
-
duration?: number
|
|
26
|
-
memory?: NodeJS.MemoryUsage
|
|
27
|
-
cpu?: NodeJS.CpuUsage
|
|
28
|
-
metadata?: Record<string, any>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface RouteProfile {
|
|
32
|
-
id: string
|
|
33
|
-
requestId: string
|
|
34
|
-
method: string
|
|
35
|
-
path: string
|
|
36
|
-
pattern: string
|
|
37
|
-
startTime: number
|
|
38
|
-
endTime?: number
|
|
39
|
-
totalDuration?: number
|
|
40
|
-
phases: {
|
|
41
|
-
routeMatching: ProfilePoint[]
|
|
42
|
-
middlewareExecution: ProfilePoint[]
|
|
43
|
-
handlerExecution: ProfilePoint[]
|
|
44
|
-
responseGeneration: ProfilePoint[]
|
|
45
|
-
}
|
|
46
|
-
memory: {
|
|
47
|
-
initial: NodeJS.MemoryUsage
|
|
48
|
-
peak: NodeJS.MemoryUsage
|
|
49
|
-
final?: NodeJS.MemoryUsage
|
|
50
|
-
allocations: number
|
|
51
|
-
}
|
|
52
|
-
cpu: {
|
|
53
|
-
initial: NodeJS.CpuUsage
|
|
54
|
-
final?: NodeJS.CpuUsage
|
|
55
|
-
userTime: number
|
|
56
|
-
systemTime: number
|
|
57
|
-
}
|
|
58
|
-
gc?: {
|
|
59
|
-
collections: number
|
|
60
|
-
duration: number
|
|
61
|
-
reclaimedMemory: number
|
|
62
|
-
}
|
|
63
|
-
queries?: Array<{
|
|
64
|
-
query: string
|
|
65
|
-
duration: number
|
|
66
|
-
timestamp: number
|
|
67
|
-
}>
|
|
68
|
-
warnings: string[]
|
|
69
|
-
recommendations: string[]
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface PerformanceMetrics {
|
|
73
|
-
averageResponseTime: number
|
|
74
|
-
p50ResponseTime: number
|
|
75
|
-
p95ResponseTime: number
|
|
76
|
-
p99ResponseTime: number
|
|
77
|
-
requestsPerSecond: number
|
|
78
|
-
memoryUsageAverage: number
|
|
79
|
-
memoryUsagePeak: number
|
|
80
|
-
cpuUsageAverage: number
|
|
81
|
-
gcPressure: number
|
|
82
|
-
slowestRoutes: Array<{
|
|
83
|
-
pattern: string
|
|
84
|
-
averageTime: number
|
|
85
|
-
count: number
|
|
86
|
-
}>
|
|
87
|
-
memoryLeaks: Array<{
|
|
88
|
-
route: string
|
|
89
|
-
memoryGrowth: number
|
|
90
|
-
requests: number
|
|
91
|
-
}>
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Performance profiler for route analysis
|
|
96
|
-
*/
|
|
97
|
-
export class PerformanceProfiler {
|
|
98
|
-
private config: ProfileConfig
|
|
99
|
-
private profiles = new Map<string, RouteProfile>()
|
|
100
|
-
private activeProfiles = new Map<string, RouteProfile>()
|
|
101
|
-
private metrics: PerformanceMetrics
|
|
102
|
-
private gcObserver?: PerformanceObserver
|
|
103
|
-
private memoryBaseline: NodeJS.MemoryUsage
|
|
104
|
-
|
|
105
|
-
constructor(config: ProfileConfig = {}) {
|
|
106
|
-
this.config = {
|
|
107
|
-
enabled: true,
|
|
108
|
-
sampleRate: 0.1, // 10% sampling by default
|
|
109
|
-
includeMemory: true,
|
|
110
|
-
includeCpu: true,
|
|
111
|
-
includeGc: true,
|
|
112
|
-
trackSlowQueries: true,
|
|
113
|
-
slowQueryThreshold: 100,
|
|
114
|
-
maxProfiles: 1000,
|
|
115
|
-
outputFormat: 'console',
|
|
116
|
-
...config,
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
this.metrics = this.initializeMetrics()
|
|
120
|
-
this.memoryBaseline = process.memoryUsage()
|
|
121
|
-
|
|
122
|
-
if (this.config.includeGc) {
|
|
123
|
-
this.setupGcObserver()
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Start profiling a request
|
|
129
|
-
*/
|
|
130
|
-
startProfiling(req: EnhancedRequest, pattern: string): string | null {
|
|
131
|
-
if (!this.config.enabled || !this.shouldProfile()) {
|
|
132
|
-
return null
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const profileId = this.generateProfileId()
|
|
136
|
-
const startTime = performance.now()
|
|
137
|
-
|
|
138
|
-
const profile: RouteProfile = {
|
|
139
|
-
id: profileId,
|
|
140
|
-
requestId: this.extractRequestId(req),
|
|
141
|
-
method: req.method,
|
|
142
|
-
path: new URL(req.url).pathname,
|
|
143
|
-
pattern,
|
|
144
|
-
startTime,
|
|
145
|
-
phases: {
|
|
146
|
-
routeMatching: [],
|
|
147
|
-
middlewareExecution: [],
|
|
148
|
-
handlerExecution: [],
|
|
149
|
-
responseGeneration: [],
|
|
150
|
-
},
|
|
151
|
-
memory: {
|
|
152
|
-
initial: process.memoryUsage(),
|
|
153
|
-
peak: process.memoryUsage(),
|
|
154
|
-
allocations: 0,
|
|
155
|
-
},
|
|
156
|
-
cpu: {
|
|
157
|
-
initial: process.cpuUsage(),
|
|
158
|
-
userTime: 0,
|
|
159
|
-
systemTime: 0,
|
|
160
|
-
},
|
|
161
|
-
queries: [],
|
|
162
|
-
warnings: [],
|
|
163
|
-
recommendations: [],
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
this.activeProfiles.set(profileId, profile)
|
|
167
|
-
|
|
168
|
-
// Add profile reference to request
|
|
169
|
-
;(req as any).profileId = profileId
|
|
170
|
-
;(req as any).profiler = this
|
|
171
|
-
|
|
172
|
-
return profileId
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Record a profile point
|
|
177
|
-
*/
|
|
178
|
-
recordPoint(
|
|
179
|
-
profileId: string,
|
|
180
|
-
phase: keyof RouteProfile['phases'],
|
|
181
|
-
name: string,
|
|
182
|
-
metadata?: Record<string, any>,
|
|
183
|
-
): void {
|
|
184
|
-
const profile = this.activeProfiles.get(profileId)
|
|
185
|
-
if (!profile)
|
|
186
|
-
return
|
|
187
|
-
|
|
188
|
-
const timestamp = performance.now()
|
|
189
|
-
const point: ProfilePoint = {
|
|
190
|
-
name,
|
|
191
|
-
timestamp,
|
|
192
|
-
metadata,
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (this.config.includeMemory) {
|
|
196
|
-
const currentMemory = process.memoryUsage()
|
|
197
|
-
point.memory = currentMemory
|
|
198
|
-
|
|
199
|
-
// Update peak memory
|
|
200
|
-
if (currentMemory.heapUsed > profile.memory.peak.heapUsed) {
|
|
201
|
-
profile.memory.peak = currentMemory
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (this.config.includeCpu) {
|
|
206
|
-
point.cpu = process.cpuUsage()
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
profile.phases[phase].push(point)
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Start timing a specific operation
|
|
214
|
-
*/
|
|
215
|
-
startTiming(profileId: string, phase: keyof RouteProfile['phases'], name: string): string {
|
|
216
|
-
const timingId = `${profileId}_${phase}_${name}_${Date.now()}`
|
|
217
|
-
this.recordPoint(profileId, phase, `${name}_start`, { timingId })
|
|
218
|
-
return timingId
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* End timing a specific operation
|
|
223
|
-
*/
|
|
224
|
-
endTiming(profileId: string, phase: keyof RouteProfile['phases'], timingId: string): void {
|
|
225
|
-
const profile = this.activeProfiles.get(profileId)
|
|
226
|
-
if (!profile)
|
|
227
|
-
return
|
|
228
|
-
|
|
229
|
-
const endTime = performance.now()
|
|
230
|
-
const startPoint = profile.phases[phase].find(p =>
|
|
231
|
-
p.metadata?.timingId === timingId && p.name.endsWith('_start'),
|
|
232
|
-
)
|
|
233
|
-
|
|
234
|
-
if (startPoint) {
|
|
235
|
-
const duration = endTime - startPoint.timestamp
|
|
236
|
-
const endName = startPoint.name.replace('_start', '_end')
|
|
237
|
-
|
|
238
|
-
this.recordPoint(profileId, phase, endName, {
|
|
239
|
-
timingId,
|
|
240
|
-
duration,
|
|
241
|
-
startTime: startPoint.timestamp,
|
|
242
|
-
endTime,
|
|
243
|
-
})
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Record database query
|
|
249
|
-
*/
|
|
250
|
-
recordQuery(profileId: string, query: string, duration: number): void {
|
|
251
|
-
if (!this.config.trackSlowQueries)
|
|
252
|
-
return
|
|
253
|
-
|
|
254
|
-
const profile = this.activeProfiles.get(profileId)
|
|
255
|
-
if (!profile)
|
|
256
|
-
return
|
|
257
|
-
|
|
258
|
-
profile.queries!.push({
|
|
259
|
-
query: query.length > 200 ? `${query.substring(0, 200)}...` : query,
|
|
260
|
-
duration,
|
|
261
|
-
timestamp: performance.now(),
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
if (duration > (this.config.slowQueryThreshold || 100)) {
|
|
265
|
-
profile.warnings.push(`Slow query detected: ${duration.toFixed(2)}ms`)
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Finish profiling a request
|
|
271
|
-
*/
|
|
272
|
-
finishProfiling(profileId: string, _response?: Response): RouteProfile | null {
|
|
273
|
-
const profile = this.activeProfiles.get(profileId)
|
|
274
|
-
if (!profile)
|
|
275
|
-
return null
|
|
276
|
-
|
|
277
|
-
const endTime = performance.now()
|
|
278
|
-
profile.endTime = endTime
|
|
279
|
-
profile.totalDuration = endTime - profile.startTime
|
|
280
|
-
|
|
281
|
-
// Finalize memory and CPU metrics
|
|
282
|
-
if (this.config.includeMemory) {
|
|
283
|
-
profile.memory.final = process.memoryUsage()
|
|
284
|
-
profile.memory.allocations = profile.memory.final.heapUsed - profile.memory.initial.heapUsed
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (this.config.includeCpu) {
|
|
288
|
-
const finalCpu = process.cpuUsage()
|
|
289
|
-
profile.cpu.final = finalCpu
|
|
290
|
-
profile.cpu.userTime = finalCpu.user - profile.cpu.initial.user
|
|
291
|
-
profile.cpu.systemTime = finalCpu.system - profile.cpu.initial.system
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Generate recommendations
|
|
295
|
-
this.generateRecommendations(profile)
|
|
296
|
-
|
|
297
|
-
// Store completed profile
|
|
298
|
-
this.profiles.set(profileId, profile)
|
|
299
|
-
this.activeProfiles.delete(profileId)
|
|
300
|
-
|
|
301
|
-
// Update metrics
|
|
302
|
-
this.updateMetrics(profile)
|
|
303
|
-
|
|
304
|
-
// Cleanup old profiles if needed
|
|
305
|
-
this.cleanupProfiles()
|
|
306
|
-
|
|
307
|
-
// Log profile if configured
|
|
308
|
-
this.logProfile(profile)
|
|
309
|
-
|
|
310
|
-
return profile
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Get profile by ID
|
|
315
|
-
*/
|
|
316
|
-
getProfile(profileId: string): RouteProfile | undefined {
|
|
317
|
-
return this.profiles.get(profileId) || this.activeProfiles.get(profileId)
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Get all profiles
|
|
322
|
-
*/
|
|
323
|
-
getProfiles(filter?: {
|
|
324
|
-
method?: string
|
|
325
|
-
pattern?: string
|
|
326
|
-
minDuration?: number
|
|
327
|
-
maxDuration?: number
|
|
328
|
-
hasWarnings?: boolean
|
|
329
|
-
}): RouteProfile[] {
|
|
330
|
-
let profiles = Array.from(this.profiles.values())
|
|
331
|
-
|
|
332
|
-
if (filter) {
|
|
333
|
-
if (filter.method) {
|
|
334
|
-
profiles = profiles.filter(p => p.method === filter.method)
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (filter.pattern) {
|
|
338
|
-
profiles = profiles.filter(p => p.pattern.includes(filter.pattern!))
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (filter.minDuration !== undefined) {
|
|
342
|
-
profiles = profiles.filter(p => (p.totalDuration || 0) >= filter.minDuration!)
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (filter.maxDuration !== undefined) {
|
|
346
|
-
profiles = profiles.filter(p => (p.totalDuration || 0) <= filter.maxDuration!)
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (filter.hasWarnings) {
|
|
350
|
-
profiles = profiles.filter(p => p.warnings.length > 0)
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return profiles.sort((a, b) => (b.totalDuration || 0) - (a.totalDuration || 0))
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Get performance metrics
|
|
359
|
-
*/
|
|
360
|
-
getMetrics(): PerformanceMetrics {
|
|
361
|
-
return { ...this.metrics }
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Generate performance report
|
|
366
|
-
*/
|
|
367
|
-
generateReport(): string {
|
|
368
|
-
const profiles = this.getProfiles()
|
|
369
|
-
const metrics = this.getMetrics()
|
|
370
|
-
|
|
371
|
-
let report = '# Performance Profile Report\n\n'
|
|
372
|
-
|
|
373
|
-
// Summary
|
|
374
|
-
report += '## Summary\n'
|
|
375
|
-
report += `- Total Profiles: ${profiles.length}\n`
|
|
376
|
-
report += `- Average Response Time: ${metrics.averageResponseTime.toFixed(2)}ms\n`
|
|
377
|
-
report += `- P95 Response Time: ${metrics.p95ResponseTime.toFixed(2)}ms\n`
|
|
378
|
-
report += `- P99 Response Time: ${metrics.p99ResponseTime.toFixed(2)}ms\n`
|
|
379
|
-
report += `- Requests/Second: ${metrics.requestsPerSecond.toFixed(2)}\n`
|
|
380
|
-
report += `- Average Memory Usage: ${(metrics.memoryUsageAverage / 1024 / 1024).toFixed(2)}MB\n`
|
|
381
|
-
report += `- Peak Memory Usage: ${(metrics.memoryUsagePeak / 1024 / 1024).toFixed(2)}MB\n\n`
|
|
382
|
-
|
|
383
|
-
// Slowest routes
|
|
384
|
-
if (metrics.slowestRoutes.length > 0) {
|
|
385
|
-
report += '## Slowest Routes\n'
|
|
386
|
-
metrics.slowestRoutes.forEach((route, index) => {
|
|
387
|
-
report += `${index + 1}. ${route.pattern} - ${route.averageTime.toFixed(2)}ms (${route.count} requests)\n`
|
|
388
|
-
})
|
|
389
|
-
report += '\n'
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Memory leaks
|
|
393
|
-
if (metrics.memoryLeaks.length > 0) {
|
|
394
|
-
report += '## Potential Memory Leaks\n'
|
|
395
|
-
metrics.memoryLeaks.forEach((leak, index) => {
|
|
396
|
-
report += `${index + 1}. ${leak.route} - ${(leak.memoryGrowth / 1024 / 1024).toFixed(2)}MB growth over ${leak.requests} requests\n`
|
|
397
|
-
})
|
|
398
|
-
report += '\n'
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Warnings and recommendations
|
|
402
|
-
const profilesWithWarnings = profiles.filter(p => p.warnings.length > 0)
|
|
403
|
-
if (profilesWithWarnings.length > 0) {
|
|
404
|
-
report += '## Warnings\n'
|
|
405
|
-
profilesWithWarnings.forEach((profile) => {
|
|
406
|
-
report += `### ${profile.method} ${profile.pattern}\n`
|
|
407
|
-
profile.warnings.forEach((warning) => {
|
|
408
|
-
report += `- ${warning}\n`
|
|
409
|
-
})
|
|
410
|
-
profile.recommendations.forEach((rec) => {
|
|
411
|
-
report += `- 💡 ${rec}\n`
|
|
412
|
-
})
|
|
413
|
-
report += '\n'
|
|
414
|
-
})
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return report
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Clear all profiles and reset metrics
|
|
422
|
-
*/
|
|
423
|
-
clear(): void {
|
|
424
|
-
this.profiles.clear()
|
|
425
|
-
this.activeProfiles.clear()
|
|
426
|
-
this.metrics = this.initializeMetrics()
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Determine if request should be profiled
|
|
431
|
-
*/
|
|
432
|
-
private shouldProfile(): boolean {
|
|
433
|
-
return Math.random() < (this.config.sampleRate || 0.1)
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Generate unique profile ID
|
|
438
|
-
*/
|
|
439
|
-
private generateProfileId(): string {
|
|
440
|
-
return `prof_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Extract request ID from request
|
|
445
|
-
*/
|
|
446
|
-
private extractRequestId(req: EnhancedRequest): string {
|
|
447
|
-
return (req as any).id || (req as any).requestId || 'unknown'
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Initialize metrics structure
|
|
452
|
-
*/
|
|
453
|
-
private initializeMetrics(): PerformanceMetrics {
|
|
454
|
-
return {
|
|
455
|
-
averageResponseTime: 0,
|
|
456
|
-
p50ResponseTime: 0,
|
|
457
|
-
p95ResponseTime: 0,
|
|
458
|
-
p99ResponseTime: 0,
|
|
459
|
-
requestsPerSecond: 0,
|
|
460
|
-
memoryUsageAverage: 0,
|
|
461
|
-
memoryUsagePeak: 0,
|
|
462
|
-
cpuUsageAverage: 0,
|
|
463
|
-
gcPressure: 0,
|
|
464
|
-
slowestRoutes: [],
|
|
465
|
-
memoryLeaks: [],
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Setup garbage collection observer
|
|
471
|
-
*/
|
|
472
|
-
private setupGcObserver(): void {
|
|
473
|
-
try {
|
|
474
|
-
this.gcObserver = new (PerformanceObserver as any)((list: PerformanceObserverEntryList) => {
|
|
475
|
-
for (const entry of list.getEntries()) {
|
|
476
|
-
if (entry.entryType === 'gc') {
|
|
477
|
-
// Update GC metrics for active profiles
|
|
478
|
-
this.activeProfiles.forEach((profile) => {
|
|
479
|
-
if (!profile.gc) {
|
|
480
|
-
profile.gc = { collections: 0, duration: 0, reclaimedMemory: 0 }
|
|
481
|
-
}
|
|
482
|
-
profile.gc.collections++
|
|
483
|
-
profile.gc.duration += entry.duration
|
|
484
|
-
})
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
})
|
|
488
|
-
|
|
489
|
-
this.gcObserver?.observe({ entryTypes: ['gc'] })
|
|
490
|
-
}
|
|
491
|
-
catch (err) {
|
|
492
|
-
console.error('Failed to setup GC observer:', err)
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/**
|
|
497
|
-
* Generate recommendations for a profile
|
|
498
|
-
*/
|
|
499
|
-
private generateRecommendations(profile: RouteProfile): void {
|
|
500
|
-
const recommendations: string[] = []
|
|
501
|
-
|
|
502
|
-
// Check response time
|
|
503
|
-
if ((profile.totalDuration || 0) > 1000) {
|
|
504
|
-
recommendations.push('Consider optimizing this route - response time exceeds 1 second')
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// Check memory usage
|
|
508
|
-
if (profile.memory.allocations > 50 * 1024 * 1024) { // 50MB
|
|
509
|
-
recommendations.push('High memory allocation detected - consider memory optimization')
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Check CPU usage
|
|
513
|
-
if (profile.cpu.userTime > 100000) { // 100ms CPU time
|
|
514
|
-
recommendations.push('High CPU usage detected - consider algorithm optimization')
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Check slow queries
|
|
518
|
-
const slowQueries = profile.queries?.filter(q => q.duration > (this.config.slowQueryThreshold || 100)) || []
|
|
519
|
-
if (slowQueries.length > 0) {
|
|
520
|
-
recommendations.push(`${slowQueries.length} slow database queries detected - consider query optimization`)
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// Check middleware overhead
|
|
524
|
-
const middlewareTime = profile.phases.middlewareExecution.reduce((total, point) => {
|
|
525
|
-
return total + (point.duration || 0)
|
|
526
|
-
}, 0)
|
|
527
|
-
|
|
528
|
-
if (middlewareTime > (profile.totalDuration || 0) * 0.5) {
|
|
529
|
-
recommendations.push('Middleware overhead is high - consider middleware optimization')
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
profile.recommendations = recommendations
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
/**
|
|
536
|
-
* Update performance metrics
|
|
537
|
-
*/
|
|
538
|
-
private updateMetrics(_profile: RouteProfile): void {
|
|
539
|
-
const profiles = Array.from(this.profiles.values())
|
|
540
|
-
const durations = profiles.map(p => p.totalDuration || 0).sort((a, b) => a - b)
|
|
541
|
-
|
|
542
|
-
if (durations.length > 0) {
|
|
543
|
-
this.metrics.averageResponseTime = durations.reduce((sum, d) => sum + d, 0) / durations.length
|
|
544
|
-
this.metrics.p50ResponseTime = durations[Math.floor(durations.length * 0.5)]
|
|
545
|
-
this.metrics.p95ResponseTime = durations[Math.floor(durations.length * 0.95)]
|
|
546
|
-
this.metrics.p99ResponseTime = durations[Math.floor(durations.length * 0.99)]
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// Update memory metrics
|
|
550
|
-
const memoryUsages = profiles.map(p => p.memory.peak.heapUsed)
|
|
551
|
-
if (memoryUsages.length > 0) {
|
|
552
|
-
this.metrics.memoryUsageAverage = memoryUsages.reduce((sum, m) => sum + m, 0) / memoryUsages.length
|
|
553
|
-
this.metrics.memoryUsagePeak = Math.max(...memoryUsages)
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// Update slowest routes
|
|
557
|
-
const routeStats = new Map<string, { totalTime: number, count: number }>()
|
|
558
|
-
profiles.forEach((profile) => {
|
|
559
|
-
const key = profile.pattern
|
|
560
|
-
const existing = routeStats.get(key) || { totalTime: 0, count: 0 }
|
|
561
|
-
existing.totalTime += profile.totalDuration || 0
|
|
562
|
-
existing.count++
|
|
563
|
-
routeStats.set(key, existing)
|
|
564
|
-
})
|
|
565
|
-
|
|
566
|
-
this.metrics.slowestRoutes = Array.from(routeStats.entries())
|
|
567
|
-
.map(([pattern, stats]) => ({
|
|
568
|
-
pattern,
|
|
569
|
-
averageTime: stats.totalTime / stats.count,
|
|
570
|
-
count: stats.count,
|
|
571
|
-
}))
|
|
572
|
-
.sort((a, b) => b.averageTime - a.averageTime)
|
|
573
|
-
.slice(0, 10)
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* Cleanup old profiles
|
|
578
|
-
*/
|
|
579
|
-
private cleanupProfiles(): void {
|
|
580
|
-
if (this.profiles.size > (this.config.maxProfiles || 1000)) {
|
|
581
|
-
const profiles = Array.from(this.profiles.entries())
|
|
582
|
-
.sort(([, a], [, b]) => (a.startTime || 0) - (b.startTime || 0))
|
|
583
|
-
|
|
584
|
-
const toDelete = profiles.slice(0, profiles.length - (this.config.maxProfiles || 1000))
|
|
585
|
-
toDelete.forEach(([id]) => this.profiles.delete(id))
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/**
|
|
590
|
-
* Log profile information
|
|
591
|
-
*/
|
|
592
|
-
private logProfile(profile: RouteProfile): void {
|
|
593
|
-
if (this.config.outputFormat === 'json') {
|
|
594
|
-
console.log(JSON.stringify(profile, null, 2))
|
|
595
|
-
}
|
|
596
|
-
else if (this.config.outputFormat === 'console') {
|
|
597
|
-
console.log(`🔍 Profile: ${profile.method} ${profile.pattern} - ${(profile.totalDuration || 0).toFixed(2)}ms`)
|
|
598
|
-
|
|
599
|
-
if (profile.warnings.length > 0) {
|
|
600
|
-
console.log(' ⚠️ Warnings:', profile.warnings)
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
if (profile.recommendations.length > 0) {
|
|
604
|
-
console.log(' 💡 Recommendations:', profile.recommendations)
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
/**
|
|
611
|
-
* Global performance profiler instance
|
|
612
|
-
*/
|
|
613
|
-
let globalProfiler: PerformanceProfiler | null = null
|
|
614
|
-
|
|
615
|
-
/**
|
|
616
|
-
* Initialize global performance profiler
|
|
617
|
-
*/
|
|
618
|
-
export function initializePerformanceProfiler(config?: ProfileConfig): PerformanceProfiler {
|
|
619
|
-
globalProfiler = new PerformanceProfiler(config)
|
|
620
|
-
return globalProfiler
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
/**
|
|
624
|
-
* Get global performance profiler
|
|
625
|
-
*/
|
|
626
|
-
export function getPerformanceProfiler(): PerformanceProfiler | null {
|
|
627
|
-
return globalProfiler
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
/**
|
|
631
|
-
* Performance profiling middleware factory
|
|
632
|
-
*/
|
|
633
|
-
export function createPerformanceProfilingMiddleware(config?: ProfileConfig) {
|
|
634
|
-
const profiler = globalProfiler || new PerformanceProfiler(config)
|
|
635
|
-
|
|
636
|
-
return async (req: EnhancedRequest, next: () => Promise<Response>): Promise<Response> => {
|
|
637
|
-
const pattern = (req as any).route?.pattern || req.url
|
|
638
|
-
const profileId = profiler.startProfiling(req, pattern)
|
|
639
|
-
|
|
640
|
-
if (!profileId) {
|
|
641
|
-
return await next()
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
try {
|
|
645
|
-
// Record middleware execution start
|
|
646
|
-
const middlewareTimingId = profiler.startTiming(profileId, 'middlewareExecution', 'total')
|
|
647
|
-
|
|
648
|
-
const response = await next()
|
|
649
|
-
|
|
650
|
-
// Record middleware execution end
|
|
651
|
-
profiler.endTiming(profileId, 'middlewareExecution', middlewareTimingId)
|
|
652
|
-
|
|
653
|
-
profiler.finishProfiling(profileId, response)
|
|
654
|
-
|
|
655
|
-
return response
|
|
656
|
-
}
|
|
657
|
-
catch (error) {
|
|
658
|
-
profiler.finishProfiling(profileId)
|
|
659
|
-
throw error
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
/**
|
|
665
|
-
* Performance profiling helpers
|
|
666
|
-
*/
|
|
667
|
-
export const PerformanceProfilingHelpers = {
|
|
668
|
-
/**
|
|
669
|
-
* Record timing from request context
|
|
670
|
-
*/
|
|
671
|
-
startTiming: (req: EnhancedRequest, phase: keyof RouteProfile['phases'], name: string): string | null => {
|
|
672
|
-
const profileId = (req as any).profileId as string
|
|
673
|
-
const profiler = (req as any).profiler as PerformanceProfiler
|
|
674
|
-
|
|
675
|
-
if (profiler && profileId) {
|
|
676
|
-
return profiler.startTiming(profileId, phase, name)
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
return null
|
|
680
|
-
},
|
|
681
|
-
|
|
682
|
-
/**
|
|
683
|
-
* End timing from request context
|
|
684
|
-
*/
|
|
685
|
-
endTiming: (req: EnhancedRequest, phase: keyof RouteProfile['phases'], timingId: string): void => {
|
|
686
|
-
const profileId = (req as any).profileId as string
|
|
687
|
-
const profiler = (req as any).profiler as PerformanceProfiler
|
|
688
|
-
|
|
689
|
-
if (profiler && profileId && timingId) {
|
|
690
|
-
profiler.endTiming(profileId, phase, timingId)
|
|
691
|
-
}
|
|
692
|
-
},
|
|
693
|
-
|
|
694
|
-
/**
|
|
695
|
-
* Record database query from request context
|
|
696
|
-
*/
|
|
697
|
-
recordQuery: (req: EnhancedRequest, query: string, duration: number): void => {
|
|
698
|
-
const profileId = (req as any).profileId as string
|
|
699
|
-
const profiler = (req as any).profiler as PerformanceProfiler
|
|
700
|
-
|
|
701
|
-
if (profiler && profileId) {
|
|
702
|
-
profiler.recordQuery(profileId, query, duration)
|
|
703
|
-
}
|
|
704
|
-
},
|
|
705
|
-
|
|
706
|
-
/**
|
|
707
|
-
* Record profile point from request context
|
|
708
|
-
*/
|
|
709
|
-
recordPoint: (req: EnhancedRequest, phase: keyof RouteProfile['phases'], name: string, metadata?: Record<string, any>): void => {
|
|
710
|
-
const profileId = (req as any).profileId as string
|
|
711
|
-
const profiler = (req as any).profiler as PerformanceProfiler
|
|
712
|
-
|
|
713
|
-
if (profiler && profileId) {
|
|
714
|
-
profiler.recordPoint(profileId, phase, name, metadata)
|
|
715
|
-
}
|
|
716
|
-
},
|
|
717
|
-
}
|