@stacksjs/bun-router 0.0.14 → 0.0.15
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/README.md +261 -584
- package/bin/cli.ts +9 -0
- 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/chunk-j0e7z7hd.js +18053 -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 -0
- package/dist/cli.js +3360 -0
- 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 +14 -0
- package/dist/index.js +13968 -0
- 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 +256 -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 +114 -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 +317 -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 +1794 -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 +45 -44
- 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 +682 -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 +220 -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 +275 -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 +665 -0
- package/src/response/response-factory.ts +596 -0
- package/src/router/api-routes.ts +243 -0
- package/src/router/file-based-routing.ts +690 -0
- package/src/router/file-streaming.ts +383 -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 +187 -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 +221 -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 +1604 -0
- package/src/router/server.ts +462 -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 +2067 -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
- package/CHANGELOG.md +0 -770
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import type { EnhancedRequest, MiddlewareHandler } from '../types'
|
|
2
|
+
import type { LRUCacheOptions } from './lru-cache'
|
|
3
|
+
import { LRUCache } from './lru-cache'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Middleware result memoization for expensive operations
|
|
7
|
+
* Caches middleware results based on request characteristics
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface MemoizationOptions extends Omit<LRUCacheOptions, 'maxSize'> {
|
|
11
|
+
maxSize: number
|
|
12
|
+
keyGenerator?: (req: EnhancedRequest) => string
|
|
13
|
+
shouldMemoize?: (req: EnhancedRequest, result: any) => boolean
|
|
14
|
+
resultSerializer?: (result: any) => any
|
|
15
|
+
resultDeserializer?: (serialized: any) => any
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MemoizedResult<T = any> {
|
|
19
|
+
value: T
|
|
20
|
+
timestamp: number
|
|
21
|
+
requestFingerprint: string
|
|
22
|
+
executionTime: number
|
|
23
|
+
hitCount: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface MemoizationStats {
|
|
27
|
+
totalRequests: number
|
|
28
|
+
cacheHits: number
|
|
29
|
+
cacheMisses: number
|
|
30
|
+
hitRate: number
|
|
31
|
+
averageExecutionTime: number
|
|
32
|
+
totalTimeSaved: number
|
|
33
|
+
memoryUsage: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Middleware memoization system for caching expensive operations
|
|
38
|
+
*/
|
|
39
|
+
export class MiddlewareMemoizer {
|
|
40
|
+
private cache: LRUCache<MemoizedResult>
|
|
41
|
+
private stats: MemoizationStats = {
|
|
42
|
+
totalRequests: 0,
|
|
43
|
+
cacheHits: 0,
|
|
44
|
+
cacheMisses: 0,
|
|
45
|
+
hitRate: 0,
|
|
46
|
+
averageExecutionTime: 0,
|
|
47
|
+
totalTimeSaved: 0,
|
|
48
|
+
memoryUsage: 0,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
constructor(private options: MemoizationOptions) {
|
|
52
|
+
this.cache = new LRUCache<MemoizedResult>({
|
|
53
|
+
maxSize: options.maxSize,
|
|
54
|
+
ttl: options.ttl,
|
|
55
|
+
onEvict: options.onEvict,
|
|
56
|
+
allowStale: options.allowStale,
|
|
57
|
+
updateAgeOnGet: options.updateAgeOnGet,
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create a memoized version of a middleware function
|
|
63
|
+
*/
|
|
64
|
+
memoize<_T = any>(
|
|
65
|
+
middleware: MiddlewareHandler,
|
|
66
|
+
options: {
|
|
67
|
+
name?: string
|
|
68
|
+
keyGenerator?: (req: EnhancedRequest) => string
|
|
69
|
+
shouldMemoize?: (req: EnhancedRequest, result: any) => boolean
|
|
70
|
+
ttl?: number
|
|
71
|
+
} = {},
|
|
72
|
+
): MiddlewareHandler {
|
|
73
|
+
const middlewareName = options.name || middleware.name || 'anonymous'
|
|
74
|
+
|
|
75
|
+
return async (req: EnhancedRequest, next) => {
|
|
76
|
+
this.stats.totalRequests++
|
|
77
|
+
|
|
78
|
+
// Generate cache key
|
|
79
|
+
const cacheKey = this.generateCacheKey(req, middlewareName, options.keyGenerator)
|
|
80
|
+
|
|
81
|
+
// Try to get cached result
|
|
82
|
+
const cachedResult = this.cache.get(cacheKey)
|
|
83
|
+
|
|
84
|
+
if (cachedResult) {
|
|
85
|
+
this.stats.cacheHits++
|
|
86
|
+
cachedResult.hitCount++
|
|
87
|
+
this.stats.totalTimeSaved += cachedResult.executionTime
|
|
88
|
+
this.updateHitRate()
|
|
89
|
+
|
|
90
|
+
// Deserialize and return cached result
|
|
91
|
+
const result = this.options.resultDeserializer
|
|
92
|
+
? this.options.resultDeserializer(cachedResult.value)
|
|
93
|
+
: cachedResult.value
|
|
94
|
+
|
|
95
|
+
// Clone Response objects to avoid "Body already used" errors
|
|
96
|
+
if (result instanceof Response) {
|
|
97
|
+
return result.clone()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return result
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Cache miss - execute middleware
|
|
104
|
+
this.stats.cacheMisses++
|
|
105
|
+
const startTime = performance.now()
|
|
106
|
+
|
|
107
|
+
const result = await middleware(req, next)
|
|
108
|
+
const executionTime = performance.now() - startTime
|
|
109
|
+
|
|
110
|
+
// Update average execution time
|
|
111
|
+
this.stats.averageExecutionTime
|
|
112
|
+
= (this.stats.averageExecutionTime * (this.stats.totalRequests - 1) + executionTime)
|
|
113
|
+
/ this.stats.totalRequests
|
|
114
|
+
|
|
115
|
+
// Check if result should be memoized
|
|
116
|
+
const shouldMemoize = options.shouldMemoize || this.options.shouldMemoize
|
|
117
|
+
if (!shouldMemoize || shouldMemoize(req, result)) {
|
|
118
|
+
// Serialize and cache result
|
|
119
|
+
let serializedResult = this.options.resultSerializer
|
|
120
|
+
? this.options.resultSerializer(result)
|
|
121
|
+
: result
|
|
122
|
+
|
|
123
|
+
// Clone Response objects before caching to preserve body
|
|
124
|
+
if (result instanceof Response && !this.options.resultSerializer) {
|
|
125
|
+
serializedResult = result.clone()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const memoizedResult: MemoizedResult = {
|
|
129
|
+
value: serializedResult,
|
|
130
|
+
timestamp: Date.now(),
|
|
131
|
+
requestFingerprint: this.generateRequestFingerprint(req),
|
|
132
|
+
executionTime,
|
|
133
|
+
hitCount: 0,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.cache.set(cacheKey, memoizedResult, options.ttl)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.updateHitRate()
|
|
140
|
+
return result
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Create a memoized async function (not middleware)
|
|
146
|
+
*/
|
|
147
|
+
memoizeFunction<TArgs extends any[], TResult>(
|
|
148
|
+
fn: (...args: TArgs) => Promise<TResult>,
|
|
149
|
+
options: {
|
|
150
|
+
name?: string
|
|
151
|
+
keyGenerator?: (...args: TArgs) => string
|
|
152
|
+
shouldMemoize?: (result: TResult, ...args: TArgs) => boolean
|
|
153
|
+
ttl?: number
|
|
154
|
+
} = {},
|
|
155
|
+
): (...args: TArgs) => Promise<TResult> {
|
|
156
|
+
const functionName = options.name || fn.name || 'anonymous'
|
|
157
|
+
|
|
158
|
+
return async (...args: TArgs): Promise<TResult> => {
|
|
159
|
+
this.stats.totalRequests++
|
|
160
|
+
|
|
161
|
+
// Generate cache key
|
|
162
|
+
const cacheKey = options.keyGenerator
|
|
163
|
+
? `${functionName}:${options.keyGenerator(...args)}`
|
|
164
|
+
: `${functionName}:${JSON.stringify(args)}`
|
|
165
|
+
|
|
166
|
+
// Try to get cached result
|
|
167
|
+
const cachedResult = this.cache.get(cacheKey)
|
|
168
|
+
|
|
169
|
+
if (cachedResult) {
|
|
170
|
+
this.stats.cacheHits++
|
|
171
|
+
cachedResult.hitCount++
|
|
172
|
+
this.stats.totalTimeSaved += cachedResult.executionTime
|
|
173
|
+
this.updateHitRate()
|
|
174
|
+
|
|
175
|
+
return this.options.resultDeserializer
|
|
176
|
+
? this.options.resultDeserializer(cachedResult.value)
|
|
177
|
+
: cachedResult.value
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Cache miss - execute function
|
|
181
|
+
this.stats.cacheMisses++
|
|
182
|
+
const startTime = performance.now()
|
|
183
|
+
|
|
184
|
+
const result = await fn(...args)
|
|
185
|
+
const executionTime = performance.now() - startTime
|
|
186
|
+
|
|
187
|
+
this.stats.averageExecutionTime
|
|
188
|
+
= (this.stats.averageExecutionTime * (this.stats.totalRequests - 1) + executionTime)
|
|
189
|
+
/ this.stats.totalRequests
|
|
190
|
+
|
|
191
|
+
// Check if result should be memoized
|
|
192
|
+
if (!options.shouldMemoize || options.shouldMemoize(result, ...args)) {
|
|
193
|
+
const serializedResult = this.options.resultSerializer
|
|
194
|
+
? this.options.resultSerializer(result)
|
|
195
|
+
: result
|
|
196
|
+
|
|
197
|
+
const memoizedResult: MemoizedResult = {
|
|
198
|
+
value: serializedResult,
|
|
199
|
+
timestamp: Date.now(),
|
|
200
|
+
requestFingerprint: JSON.stringify(args),
|
|
201
|
+
executionTime,
|
|
202
|
+
hitCount: 0,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.cache.set(cacheKey, memoizedResult, options.ttl)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this.updateHitRate()
|
|
209
|
+
return result
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Invalidate cached results by pattern
|
|
215
|
+
*/
|
|
216
|
+
invalidate(pattern: string | RegExp): number {
|
|
217
|
+
let invalidatedCount = 0
|
|
218
|
+
const keys = this.cache.keys()
|
|
219
|
+
|
|
220
|
+
for (const key of keys) {
|
|
221
|
+
if (typeof pattern === 'string') {
|
|
222
|
+
if (key.includes(pattern)) {
|
|
223
|
+
this.cache.delete(key)
|
|
224
|
+
invalidatedCount++
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (pattern instanceof RegExp) {
|
|
228
|
+
if (pattern.test(key)) {
|
|
229
|
+
this.cache.delete(key)
|
|
230
|
+
invalidatedCount++
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return invalidatedCount
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Invalidate cached results for specific user
|
|
240
|
+
*/
|
|
241
|
+
invalidateForUser(userId: string): number {
|
|
242
|
+
return this.invalidate(`user:${userId}`)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Invalidate cached results by tag
|
|
247
|
+
*/
|
|
248
|
+
invalidateByTag(tag: string): number {
|
|
249
|
+
return this.invalidate(`tag:${tag}`)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get most frequently used cached results
|
|
254
|
+
*/
|
|
255
|
+
getPopularResults(limit: number = 10): Array<{
|
|
256
|
+
key: string
|
|
257
|
+
hitCount: number
|
|
258
|
+
executionTime: number
|
|
259
|
+
timeSaved: number
|
|
260
|
+
}> {
|
|
261
|
+
const results: Array<{
|
|
262
|
+
key: string
|
|
263
|
+
hitCount: number
|
|
264
|
+
executionTime: number
|
|
265
|
+
timeSaved: number
|
|
266
|
+
}> = []
|
|
267
|
+
|
|
268
|
+
for (const key of this.cache.keys()) {
|
|
269
|
+
const result = this.cache.get(key)
|
|
270
|
+
if (result) {
|
|
271
|
+
results.push({
|
|
272
|
+
key,
|
|
273
|
+
hitCount: result.hitCount,
|
|
274
|
+
executionTime: result.executionTime,
|
|
275
|
+
timeSaved: result.hitCount * result.executionTime,
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return results
|
|
281
|
+
.sort((a, b) => b.timeSaved - a.timeSaved)
|
|
282
|
+
.slice(0, limit)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get memoization statistics
|
|
287
|
+
*/
|
|
288
|
+
getStats(): MemoizationStats {
|
|
289
|
+
return {
|
|
290
|
+
...this.stats,
|
|
291
|
+
memoryUsage: this.cache.getStats().memoryUsage,
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Clear cache and reset statistics
|
|
297
|
+
*/
|
|
298
|
+
clear(): void {
|
|
299
|
+
this.cache.clear()
|
|
300
|
+
this.stats = {
|
|
301
|
+
totalRequests: 0,
|
|
302
|
+
cacheHits: 0,
|
|
303
|
+
cacheMisses: 0,
|
|
304
|
+
hitRate: 0,
|
|
305
|
+
averageExecutionTime: 0,
|
|
306
|
+
totalTimeSaved: 0,
|
|
307
|
+
memoryUsage: 0,
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Prune expired entries
|
|
313
|
+
*/
|
|
314
|
+
prune(): number {
|
|
315
|
+
return this.cache.prune()
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Generate cache key for middleware
|
|
320
|
+
*/
|
|
321
|
+
private generateCacheKey(
|
|
322
|
+
req: EnhancedRequest,
|
|
323
|
+
middlewareName: string,
|
|
324
|
+
customKeyGenerator?: (req: EnhancedRequest) => string,
|
|
325
|
+
): string {
|
|
326
|
+
if (customKeyGenerator) {
|
|
327
|
+
return `${middlewareName}:${customKeyGenerator(req)}`
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (this.options.keyGenerator) {
|
|
331
|
+
return `${middlewareName}:${this.options.keyGenerator(req)}`
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Default key generation
|
|
335
|
+
const url = new URL(req.url)
|
|
336
|
+
const keyParts = [
|
|
337
|
+
middlewareName,
|
|
338
|
+
req.method,
|
|
339
|
+
url.pathname,
|
|
340
|
+
url.search,
|
|
341
|
+
req.user?.id || 'anonymous',
|
|
342
|
+
]
|
|
343
|
+
|
|
344
|
+
return keyParts.join(':')
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Generate request fingerprint for cache validation
|
|
349
|
+
*/
|
|
350
|
+
private generateRequestFingerprint(req: EnhancedRequest): string {
|
|
351
|
+
const fingerprint = {
|
|
352
|
+
method: req.method,
|
|
353
|
+
url: req.url,
|
|
354
|
+
userAgent: req.headers.get('user-agent'),
|
|
355
|
+
userId: req.user?.id,
|
|
356
|
+
timestamp: Math.floor(Date.now() / 60000), // Round to minute
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return JSON.stringify(fingerprint)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Update hit rate statistics
|
|
364
|
+
*/
|
|
365
|
+
private updateHitRate(): void {
|
|
366
|
+
const totalRequests = this.stats.cacheHits + this.stats.cacheMisses
|
|
367
|
+
this.stats.hitRate = totalRequests > 0 ? this.stats.cacheHits / totalRequests : 0
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Common memoization patterns and utilities
|
|
373
|
+
*/
|
|
374
|
+
export class MemoizationPatterns {
|
|
375
|
+
/**
|
|
376
|
+
* Memoize database queries
|
|
377
|
+
*/
|
|
378
|
+
static createDatabaseMemoizer(options: MemoizationOptions = { maxSize: 1000 }): MiddlewareMemoizer {
|
|
379
|
+
return new MiddlewareMemoizer({
|
|
380
|
+
...options,
|
|
381
|
+
keyGenerator: (req) => {
|
|
382
|
+
// Include user context and query parameters in key
|
|
383
|
+
const url = new URL(req.url)
|
|
384
|
+
return `db:${req.user?.id || 'anon'}:${url.pathname}:${url.search}`
|
|
385
|
+
},
|
|
386
|
+
shouldMemoize: (req, result) => {
|
|
387
|
+
// Only memoize successful results
|
|
388
|
+
return result && !result.error
|
|
389
|
+
},
|
|
390
|
+
ttl: 5 * 60 * 1000, // 5 minutes
|
|
391
|
+
})
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Memoize API calls
|
|
396
|
+
*/
|
|
397
|
+
static createApiMemoizer(options: MemoizationOptions = { maxSize: 500 }): MiddlewareMemoizer {
|
|
398
|
+
return new MiddlewareMemoizer({
|
|
399
|
+
...options,
|
|
400
|
+
keyGenerator: (req) => {
|
|
401
|
+
const url = new URL(req.url)
|
|
402
|
+
return `api:${req.method}:${url.pathname}:${url.search}`
|
|
403
|
+
},
|
|
404
|
+
shouldMemoize: (req, result) => {
|
|
405
|
+
// Memoize GET requests and successful responses
|
|
406
|
+
return req.method === 'GET' && result && result.status < 400
|
|
407
|
+
},
|
|
408
|
+
ttl: 2 * 60 * 1000, // 2 minutes
|
|
409
|
+
})
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Memoize authentication checks
|
|
414
|
+
*/
|
|
415
|
+
static createAuthMemoizer(options: MemoizationOptions = { maxSize: 2000 }): MiddlewareMemoizer {
|
|
416
|
+
return new MiddlewareMemoizer({
|
|
417
|
+
...options,
|
|
418
|
+
keyGenerator: (req) => {
|
|
419
|
+
const token = req.headers.get('authorization')?.replace('Bearer ', '')
|
|
420
|
+
return `auth:${token || 'anonymous'}`
|
|
421
|
+
},
|
|
422
|
+
shouldMemoize: (req, result) => {
|
|
423
|
+
// Memoize successful auth results
|
|
424
|
+
return result && result.user
|
|
425
|
+
},
|
|
426
|
+
ttl: 10 * 60 * 1000, // 10 minutes
|
|
427
|
+
})
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Memoize rate limiting checks
|
|
432
|
+
*/
|
|
433
|
+
static createRateLimitMemoizer(options: MemoizationOptions = { maxSize: 5000 }): MiddlewareMemoizer {
|
|
434
|
+
return new MiddlewareMemoizer({
|
|
435
|
+
...options,
|
|
436
|
+
keyGenerator: (req) => {
|
|
437
|
+
const clientId = req.ip || req.headers.get('x-forwarded-for') || 'unknown'
|
|
438
|
+
return `ratelimit:${clientId}`
|
|
439
|
+
},
|
|
440
|
+
shouldMemoize: (_req, _result) => {
|
|
441
|
+
// Always memoize rate limit results
|
|
442
|
+
return true
|
|
443
|
+
},
|
|
444
|
+
ttl: 60 * 1000, // 1 minute
|
|
445
|
+
})
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Memoize expensive computations
|
|
450
|
+
*/
|
|
451
|
+
static createComputationMemoizer(options: MemoizationOptions = { maxSize: 200 }): MiddlewareMemoizer {
|
|
452
|
+
return new MiddlewareMemoizer({
|
|
453
|
+
...options,
|
|
454
|
+
keyGenerator: (req) => {
|
|
455
|
+
// Include request body hash for POST requests
|
|
456
|
+
const url = new URL(req.url)
|
|
457
|
+
let key = `compute:${req.method}:${url.pathname}:${url.search}`
|
|
458
|
+
|
|
459
|
+
if (req.jsonBody) {
|
|
460
|
+
key += `:${JSON.stringify(req.jsonBody)}`
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return key
|
|
464
|
+
},
|
|
465
|
+
shouldMemoize: (req, result) => {
|
|
466
|
+
// Memoize successful computations
|
|
467
|
+
return result && !result.error
|
|
468
|
+
},
|
|
469
|
+
ttl: 30 * 60 * 1000, // 30 minutes
|
|
470
|
+
})
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Decorator for memoizing class methods
|
|
476
|
+
*/
|
|
477
|
+
export function Memoize(options: {
|
|
478
|
+
memoizer: MiddlewareMemoizer
|
|
479
|
+
keyGenerator?: (...args: any[]) => string
|
|
480
|
+
ttl?: number
|
|
481
|
+
} = { memoizer: new MiddlewareMemoizer({ maxSize: 100 }) }): MethodDecorator {
|
|
482
|
+
return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor {
|
|
483
|
+
const originalMethod = descriptor.value
|
|
484
|
+
|
|
485
|
+
descriptor.value = function (...args: any[]) {
|
|
486
|
+
const memoizedMethod = options.memoizer.memoizeFunction(
|
|
487
|
+
originalMethod.bind(this),
|
|
488
|
+
{
|
|
489
|
+
name: `${target.constructor.name}.${String(propertyKey)}`,
|
|
490
|
+
keyGenerator: options.keyGenerator,
|
|
491
|
+
ttl: options.ttl,
|
|
492
|
+
},
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
return memoizedMethod(...args)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return descriptor
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Factory functions for creating memoizers
|
|
504
|
+
*/
|
|
505
|
+
export const createMemoizer = {
|
|
506
|
+
/**
|
|
507
|
+
* Create memoizer for development
|
|
508
|
+
*/
|
|
509
|
+
development: (): MiddlewareMemoizer =>
|
|
510
|
+
new MiddlewareMemoizer({
|
|
511
|
+
maxSize: 100,
|
|
512
|
+
ttl: 60 * 1000, // 1 minute
|
|
513
|
+
updateAgeOnGet: true,
|
|
514
|
+
}),
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Create memoizer for production
|
|
518
|
+
*/
|
|
519
|
+
production: (): MiddlewareMemoizer =>
|
|
520
|
+
new MiddlewareMemoizer({
|
|
521
|
+
maxSize: 10000,
|
|
522
|
+
ttl: 15 * 60 * 1000, // 15 minutes
|
|
523
|
+
updateAgeOnGet: true,
|
|
524
|
+
}),
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Create custom memoizer
|
|
528
|
+
*/
|
|
529
|
+
custom: (options: MemoizationOptions): MiddlewareMemoizer =>
|
|
530
|
+
new MiddlewareMemoizer(options),
|
|
531
|
+
}
|