@stacksjs/bun-router 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +770 -0
- package/README.md +584 -261
- package/package.json +43 -47
- package/bin/cli.ts +0 -9
- package/dist/auth.d.ts +0 -134
- package/dist/cache/lru-cache.d.ts +0 -139
- package/dist/cache/middleware-memoization.d.ts +0 -151
- package/dist/cache/route-cache-warmer.d.ts +0 -161
- package/dist/cache/sqlite-cache.d.ts +0 -209
- package/dist/cache/streaming-cache.d.ts +0 -132
- package/dist/chunk-1mvtv6jw.js +0 -13012
- package/dist/cli/colors.d.ts +0 -15
- package/dist/cli/index.d.ts +0 -10
- package/dist/cli/middleware.d.ts +0 -31
- package/dist/cli/openapi.d.ts +0 -17
- package/dist/cli/router.d.ts +0 -15
- package/dist/cli/routes.d.ts +0 -29
- package/dist/cli/utils.d.ts +0 -74
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -3195
- package/dist/config.d.ts +0 -7
- package/dist/container/container.d.ts +0 -273
- package/dist/container/contextual-binding.d.ts +0 -240
- package/dist/container/decorators.d.ts +0 -141
- package/dist/container/service-provider.d.ts +0 -285
- package/dist/development/hot-reload.d.ts +0 -187
- package/dist/development/index.d.ts +0 -167
- package/dist/development/performance-profiler.d.ts +0 -217
- package/dist/development/route-debugger.d.ts +0 -154
- package/dist/development/route-inspector.d.ts +0 -211
- package/dist/development/typescript-utilities.d.ts +0 -209
- package/dist/docs.d.ts +0 -11
- package/dist/errors/circuit-breaker.d.ts +0 -195
- package/dist/errors/error-handler.d.ts +0 -88
- package/dist/errors/error-reporting.d.ts +0 -144
- package/dist/errors/exceptions.d.ts +0 -184
- package/dist/errors/graceful-degradation.d.ts +0 -154
- package/dist/errors/index.d.ts +0 -9
- package/dist/errors/router-errors.d.ts +0 -110
- package/dist/file-serving/static-files.d.ts +0 -143
- package/dist/index.d.ts +0 -14
- package/dist/index.js +0 -13933
- package/dist/middleware/auth.d.ts +0 -63
- package/dist/middleware/content_security_policy.d.ts +0 -53
- package/dist/middleware/cors.d.ts +0 -5
- package/dist/middleware/csrf.d.ts +0 -7
- package/dist/middleware/ddos_protection.d.ts +0 -39
- package/dist/middleware/file_security.d.ts +0 -26
- package/dist/middleware/file_upload.d.ts +0 -38
- package/dist/middleware/helmet.d.ts +0 -54
- package/dist/middleware/index.d.ts +0 -44
- package/dist/middleware/input_validation.d.ts +0 -45
- package/dist/middleware/json_body.d.ts +0 -4
- package/dist/middleware/performance_alerting.d.ts +0 -87
- package/dist/middleware/performance_dashboard.d.ts +0 -87
- package/dist/middleware/performance_monitor.d.ts +0 -209
- package/dist/middleware/pipeline.d.ts +0 -131
- package/dist/middleware/rate_limit.d.ts +0 -36
- package/dist/middleware/request_id.d.ts +0 -4
- package/dist/middleware/request_signing.d.ts +0 -153
- package/dist/middleware/request_tracer.d.ts +0 -72
- package/dist/middleware/response_cache.d.ts +0 -97
- package/dist/middleware/security.d.ts +0 -69
- package/dist/middleware/security_suite.d.ts +0 -46
- package/dist/middleware/session.d.ts +0 -23
- package/dist/model-binding/index.d.ts +0 -2
- package/dist/model-binding/model-middleware.d.ts +0 -118
- package/dist/model-binding/model-registry.d.ts +0 -164
- package/dist/model-binding.d.ts +0 -185
- package/dist/model-resolver-factory.d.ts +0 -31
- package/dist/observability/correlation.d.ts +0 -187
- package/dist/observability/health-checks.d.ts +0 -186
- package/dist/observability/index.d.ts +0 -60
- package/dist/observability/integration.d.ts +0 -147
- package/dist/observability/metrics.d.ts +0 -184
- package/dist/observability/tracing.d.ts +0 -187
- package/dist/optimization/bun-utilities.d.ts +0 -224
- package/dist/query-builder-integration.d.ts +0 -36
- package/dist/request/context.d.ts +0 -34
- package/dist/request/enhanced-request.d.ts +0 -213
- package/dist/request/macros.d.ts +0 -300
- package/dist/response/macros.d.ts +0 -245
- package/dist/response/response-factory.d.ts +0 -133
- package/dist/router/api-routes.d.ts +0 -5
- package/dist/router/file-based-routing.d.ts +0 -118
- package/dist/router/file-streaming.d.ts +0 -5
- package/dist/router/fluent-router.d.ts +0 -315
- package/dist/router/fluent-routing.d.ts +0 -271
- package/dist/router/group-organization.d.ts +0 -5
- package/dist/router/handler-resolver.d.ts +0 -24
- package/dist/router/http-methods.d.ts +0 -10
- package/dist/router/index.d.ts +0 -114
- package/dist/router/middleware-groups.d.ts +0 -94
- package/dist/router/middleware-integration.d.ts +0 -112
- package/dist/router/middleware.d.ts +0 -5
- package/dist/router/model-binding.d.ts +0 -5
- package/dist/router/optimized-route-matching.d.ts +0 -6
- package/dist/router/route-building.d.ts +0 -5
- package/dist/router/route-compiler.d.ts +0 -153
- package/dist/router/route-matching.d.ts +0 -5
- package/dist/router/route-trie.d.ts +0 -114
- package/dist/router/router.d.ts +0 -294
- package/dist/router/server.d.ts +0 -5
- package/dist/router/validation-integration.d.ts +0 -170
- package/dist/router/view-rendering.d.ts +0 -5
- package/dist/router/websocket.d.ts +0 -5
- package/dist/routing/route-caching.d.ts +0 -129
- package/dist/routing/route-throttling.d.ts +0 -150
- package/dist/routing/subdomain-routing.d.ts +0 -206
- package/dist/session/database-store.d.ts +0 -51
- package/dist/session/file-store.d.ts +0 -22
- package/dist/session/index.d.ts +0 -106
- package/dist/session/memory-store.d.ts +0 -21
- package/dist/session/redis-store.d.ts +0 -31
- package/dist/streaming/index.d.ts +0 -3
- package/dist/streaming/sse-handler.d.ts +0 -138
- package/dist/streaming/stream-handler.d.ts +0 -114
- package/dist/testing/auth-testing.d.ts +0 -156
- package/dist/testing/file-upload-testing.d.ts +0 -190
- package/dist/testing/index.d.ts +0 -10
- package/dist/testing/middleware-testing.d.ts +0 -138
- package/dist/testing/model-binding-testing.d.ts +0 -186
- package/dist/testing/performance-testing.d.ts +0 -235
- package/dist/testing/test-client.d.ts +0 -117
- package/dist/testing/test-request.d.ts +0 -85
- package/dist/testing/test-response.d.ts +0 -90
- package/dist/testing/types.d.ts +0 -207
- package/dist/testing/websocket-testing.d.ts +0 -227
- package/dist/types/controller-types.d.ts +0 -208
- package/dist/types/core.d.ts +0 -543
- package/dist/types/middleware-types.d.ts +0 -227
- package/dist/types/request-response-augmentation.d.ts +0 -261
- package/dist/types/route-inference.d.ts +0 -168
- package/dist/types.d.ts +0 -1782
- package/dist/url.d.ts +0 -56
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/query-preservation.d.ts +0 -48
- package/dist/utils.d.ts +0 -69
- package/dist/validation/validator.d.ts +0 -140
- package/dist/websocket/clustering.d.ts +0 -185
- package/src/auth.ts +0 -469
- package/src/cache/lru-cache.ts +0 -457
- package/src/cache/middleware-memoization.ts +0 -531
- package/src/cache/route-cache-warmer.ts +0 -486
- package/src/cache/sqlite-cache.ts +0 -783
- package/src/cache/streaming-cache.ts +0 -572
- package/src/cli/colors.ts +0 -29
- package/src/cli/index.ts +0 -287
- package/src/cli/middleware.ts +0 -291
- package/src/cli/openapi.ts +0 -407
- package/src/cli/router.ts +0 -188
- package/src/cli/routes.ts +0 -265
- package/src/cli/utils.ts +0 -531
- package/src/cli.ts +0 -5
- package/src/config.ts +0 -322
- package/src/container/container.ts +0 -740
- package/src/container/contextual-binding.ts +0 -603
- package/src/container/decorators.ts +0 -359
- package/src/container/service-provider.ts +0 -596
- package/src/development/hot-reload.ts +0 -673
- package/src/development/index.ts +0 -499
- package/src/development/performance-profiler.ts +0 -717
- package/src/development/route-debugger.ts +0 -527
- package/src/development/route-inspector.ts +0 -749
- package/src/development/typescript-utilities.ts +0 -682
- package/src/docs.ts +0 -397
- package/src/errors/circuit-breaker.ts +0 -732
- package/src/errors/error-handler.ts +0 -569
- package/src/errors/error-reporting.ts +0 -672
- package/src/errors/exceptions.ts +0 -536
- package/src/errors/graceful-degradation.ts +0 -621
- package/src/errors/index.ts +0 -21
- package/src/errors/router-errors.ts +0 -632
- package/src/file-serving/static-files.ts +0 -581
- package/src/index.ts +0 -14
- package/src/middleware/auth.ts +0 -219
- package/src/middleware/content_security_policy.ts +0 -215
- package/src/middleware/cors.ts +0 -74
- package/src/middleware/csrf.ts +0 -108
- package/src/middleware/ddos_protection.ts +0 -255
- package/src/middleware/file_security.ts +0 -191
- package/src/middleware/file_upload.ts +0 -264
- package/src/middleware/helmet.ts +0 -268
- package/src/middleware/index.ts +0 -117
- package/src/middleware/input_validation.ts +0 -449
- package/src/middleware/json_body.ts +0 -37
- package/src/middleware/performance_alerting.ts +0 -538
- package/src/middleware/performance_dashboard.ts +0 -661
- package/src/middleware/performance_monitor.ts +0 -943
- package/src/middleware/pipeline.ts +0 -489
- package/src/middleware/rate_limit.ts +0 -245
- package/src/middleware/request_id.ts +0 -36
- package/src/middleware/request_signing.ts +0 -636
- package/src/middleware/request_tracer.ts +0 -636
- package/src/middleware/response_cache.ts +0 -743
- package/src/middleware/security.ts +0 -482
- package/src/middleware/security_suite.ts +0 -257
- package/src/middleware/session.ts +0 -91
- package/src/model-binding/index.ts +0 -18
- package/src/model-binding/model-middleware.ts +0 -425
- package/src/model-binding/model-registry.ts +0 -550
- package/src/model-binding.ts +0 -370
- package/src/model-resolver-factory.ts +0 -106
- package/src/observability/correlation.ts +0 -691
- package/src/observability/health-checks.ts +0 -729
- package/src/observability/index.ts +0 -184
- package/src/observability/integration.ts +0 -548
- package/src/observability/metrics.ts +0 -753
- package/src/observability/tracing.ts +0 -638
- package/src/optimization/bun-utilities.ts +0 -778
- package/src/query-builder-integration.ts +0 -137
- package/src/request/context.ts +0 -62
- package/src/request/enhanced-request.ts +0 -857
- package/src/request/macros.ts +0 -688
- package/src/response/macros.ts +0 -612
- package/src/response/response-factory.ts +0 -596
- package/src/router/api-routes.ts +0 -243
- package/src/router/file-based-routing.ts +0 -670
- package/src/router/file-streaming.ts +0 -372
- package/src/router/fluent-router.ts +0 -927
- package/src/router/fluent-routing.ts +0 -797
- package/src/router/group-organization.ts +0 -213
- package/src/router/handler-resolver.ts +0 -291
- package/src/router/http-methods.ts +0 -377
- package/src/router/index.ts +0 -187
- package/src/router/middleware-groups.ts +0 -222
- package/src/router/middleware-integration.ts +0 -399
- package/src/router/middleware.ts +0 -231
- package/src/router/model-binding.ts +0 -215
- package/src/router/optimized-route-matching.ts +0 -253
- package/src/router/route-building.ts +0 -212
- package/src/router/route-compiler.ts +0 -702
- package/src/router/route-matching.ts +0 -349
- package/src/router/route-trie.ts +0 -464
- package/src/router/router.ts +0 -1581
- package/src/router/server.ts +0 -462
- package/src/router/validation-integration.ts +0 -443
- package/src/router/view-rendering.ts +0 -233
- package/src/router/websocket.ts +0 -100
- package/src/routing/route-caching.ts +0 -402
- package/src/routing/route-throttling.ts +0 -469
- package/src/routing/subdomain-routing.ts +0 -492
- package/src/session/database-store.ts +0 -109
- package/src/session/file-store.ts +0 -148
- package/src/session/index.ts +0 -244
- package/src/session/memory-store.ts +0 -88
- package/src/session/redis-store.ts +0 -93
- package/src/streaming/index.ts +0 -17
- package/src/streaming/sse-handler.ts +0 -482
- package/src/streaming/stream-handler.ts +0 -552
- package/src/testing/auth-testing.ts +0 -446
- package/src/testing/file-upload-testing.ts +0 -543
- package/src/testing/index.ts +0 -10
- package/src/testing/middleware-testing.ts +0 -322
- package/src/testing/model-binding-testing.ts +0 -645
- package/src/testing/performance-testing.ts +0 -738
- package/src/testing/test-client.ts +0 -310
- package/src/testing/test-request.ts +0 -315
- package/src/testing/test-response.ts +0 -332
- package/src/testing/types.ts +0 -224
- package/src/testing/websocket-testing.ts +0 -590
- package/src/types/controller-types.ts +0 -385
- package/src/types/core.ts +0 -683
- package/src/types/middleware-types.ts +0 -419
- package/src/types/request-response-augmentation.ts +0 -489
- package/src/types/route-inference.ts +0 -357
- package/src/types.ts +0 -2055
- package/src/url.ts +0 -126
- package/src/utils/index.ts +0 -1
- package/src/utils/query-preservation.ts +0 -201
- package/src/utils.ts +0 -327
- package/src/validation/validator.ts +0 -685
- package/src/websocket/clustering.ts +0 -762
|
@@ -1,783 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SQLite Embedded Caching
|
|
3
|
-
*
|
|
4
|
-
* High-performance embedded caching using Bun's SQLite integration
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Database } from 'bun:sqlite'
|
|
8
|
-
import { Buffer } from 'node:buffer'
|
|
9
|
-
|
|
10
|
-
export interface SQLiteCacheConfig {
|
|
11
|
-
filename?: string
|
|
12
|
-
memory?: boolean
|
|
13
|
-
wal?: boolean
|
|
14
|
-
synchronous?: 'OFF' | 'NORMAL' | 'FULL'
|
|
15
|
-
journalMode?: 'DELETE' | 'TRUNCATE' | 'PERSIST' | 'MEMORY' | 'WAL' | 'OFF'
|
|
16
|
-
cacheSize?: number
|
|
17
|
-
pageSize?: number
|
|
18
|
-
maxSize?: number
|
|
19
|
-
defaultTTL?: number
|
|
20
|
-
cleanupInterval?: number
|
|
21
|
-
compression?: boolean
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface CacheEntry {
|
|
25
|
-
key: string
|
|
26
|
-
value: string
|
|
27
|
-
expires: number
|
|
28
|
-
created: number
|
|
29
|
-
accessed: number
|
|
30
|
-
hits: number
|
|
31
|
-
size: number
|
|
32
|
-
compressed: boolean
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface CacheStats {
|
|
36
|
-
totalEntries: number
|
|
37
|
-
totalSize: number
|
|
38
|
-
hitRate: number
|
|
39
|
-
missRate: number
|
|
40
|
-
evictions: number
|
|
41
|
-
compressionRatio: number
|
|
42
|
-
averageAccessTime: number
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* High-performance SQLite-based cache
|
|
47
|
-
*/
|
|
48
|
-
export class SQLiteCache {
|
|
49
|
-
private db!: Database
|
|
50
|
-
private config: Required<SQLiteCacheConfig>
|
|
51
|
-
private stats: CacheStats
|
|
52
|
-
private cleanupTimer?: ReturnType<typeof setInterval>
|
|
53
|
-
private preparedStatements!: {
|
|
54
|
-
get: any
|
|
55
|
-
set: any
|
|
56
|
-
delete: any
|
|
57
|
-
exists: any
|
|
58
|
-
clear: any
|
|
59
|
-
cleanup: any
|
|
60
|
-
stats: any
|
|
61
|
-
touch: any
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
constructor(config: SQLiteCacheConfig = {}) {
|
|
65
|
-
this.config = {
|
|
66
|
-
filename: config.filename || ':memory:',
|
|
67
|
-
memory: config.memory ?? true,
|
|
68
|
-
wal: config.wal ?? true,
|
|
69
|
-
synchronous: config.synchronous || 'NORMAL',
|
|
70
|
-
journalMode: config.journalMode || 'WAL',
|
|
71
|
-
cacheSize: config.cacheSize || 10000,
|
|
72
|
-
pageSize: config.pageSize || 4096,
|
|
73
|
-
maxSize: config.maxSize || 100 * 1024 * 1024, // 100MB
|
|
74
|
-
defaultTTL: config.defaultTTL || 3600, // 1 hour
|
|
75
|
-
cleanupInterval: config.cleanupInterval || 300, // 5 minutes
|
|
76
|
-
compression: config.compression ?? true,
|
|
77
|
-
...config,
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
this.stats = {
|
|
81
|
-
totalEntries: 0,
|
|
82
|
-
totalSize: 0,
|
|
83
|
-
hitRate: 0,
|
|
84
|
-
missRate: 0,
|
|
85
|
-
evictions: 0,
|
|
86
|
-
compressionRatio: 0,
|
|
87
|
-
averageAccessTime: 0,
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
this.initializeDatabase()
|
|
91
|
-
this.prepareStatements()
|
|
92
|
-
this.startCleanupTimer()
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Get value from cache
|
|
97
|
-
*/
|
|
98
|
-
get<T = any>(key: string): T | null {
|
|
99
|
-
const startTime = performance.now()
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
const result = this.preparedStatements.get.get(key, Date.now()) as CacheEntry | null
|
|
103
|
-
|
|
104
|
-
if (!result) {
|
|
105
|
-
this.updateStats('miss', performance.now() - startTime)
|
|
106
|
-
return null
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Update access statistics
|
|
110
|
-
this.preparedStatements.touch.run(key, Date.now())
|
|
111
|
-
|
|
112
|
-
let value = result.value
|
|
113
|
-
if (result.compressed && this.config.compression) {
|
|
114
|
-
value = this.decompress(value)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
this.updateStats('hit', performance.now() - startTime)
|
|
118
|
-
return JSON.parse(value)
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
console.error('Cache get error:', error)
|
|
122
|
-
this.updateStats('miss', performance.now() - startTime)
|
|
123
|
-
return null
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Set value in cache
|
|
129
|
-
*/
|
|
130
|
-
set<T = any>(key: string, value: T, ttl?: number): boolean {
|
|
131
|
-
const startTime = performance.now()
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
const serialized = JSON.stringify(value)
|
|
135
|
-
const expires = Date.now() + ((ttl || this.config.defaultTTL) * 1000)
|
|
136
|
-
const created = Date.now()
|
|
137
|
-
|
|
138
|
-
let finalValue = serialized
|
|
139
|
-
let compressed = false
|
|
140
|
-
let size = serialized.length
|
|
141
|
-
|
|
142
|
-
// Compress if enabled and value is large enough
|
|
143
|
-
if (this.config.compression && serialized.length > 1024) {
|
|
144
|
-
const compressedValue = this.compress(serialized)
|
|
145
|
-
if (compressedValue && compressedValue.length < serialized.length) {
|
|
146
|
-
finalValue = compressedValue
|
|
147
|
-
compressed = true
|
|
148
|
-
size = compressedValue.length
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Check if we need to evict entries
|
|
153
|
-
this.evictIfNeeded(size)
|
|
154
|
-
|
|
155
|
-
this.preparedStatements.set.run(
|
|
156
|
-
key,
|
|
157
|
-
finalValue,
|
|
158
|
-
expires,
|
|
159
|
-
created,
|
|
160
|
-
created, // accessed
|
|
161
|
-
0, // hits
|
|
162
|
-
size,
|
|
163
|
-
compressed ? 1 : 0,
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
this.updateCacheStats()
|
|
167
|
-
this.updateStats('set', performance.now() - startTime)
|
|
168
|
-
return true
|
|
169
|
-
}
|
|
170
|
-
catch (error) {
|
|
171
|
-
console.error('Cache set error:', error)
|
|
172
|
-
return false
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Delete value from cache
|
|
178
|
-
*/
|
|
179
|
-
delete(key: string): boolean {
|
|
180
|
-
try {
|
|
181
|
-
const result = this.preparedStatements.delete.run(key)
|
|
182
|
-
this.updateCacheStats()
|
|
183
|
-
return result.changes > 0
|
|
184
|
-
}
|
|
185
|
-
catch (error) {
|
|
186
|
-
console.error('Cache delete error:', error)
|
|
187
|
-
return false
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Check if key exists in cache
|
|
193
|
-
*/
|
|
194
|
-
has(key: string): boolean {
|
|
195
|
-
try {
|
|
196
|
-
const result = this.preparedStatements.exists.get(key, Date.now())
|
|
197
|
-
return result !== null
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
console.error('Cache exists error:', error)
|
|
201
|
-
return false
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Clear all cache entries
|
|
207
|
-
*/
|
|
208
|
-
clear(): boolean {
|
|
209
|
-
try {
|
|
210
|
-
this.preparedStatements.clear.run()
|
|
211
|
-
this.updateCacheStats()
|
|
212
|
-
return true
|
|
213
|
-
}
|
|
214
|
-
catch (error) {
|
|
215
|
-
console.error('Cache clear error:', error)
|
|
216
|
-
return false
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Get cache statistics
|
|
222
|
-
*/
|
|
223
|
-
getStats(): CacheStats {
|
|
224
|
-
this.updateCacheStats()
|
|
225
|
-
return { ...this.stats }
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Get all keys matching pattern
|
|
230
|
-
*/
|
|
231
|
-
keys(pattern?: string): string[] {
|
|
232
|
-
try {
|
|
233
|
-
const query = pattern
|
|
234
|
-
? this.db.query('SELECT key FROM cache WHERE key LIKE ? AND expires > ?')
|
|
235
|
-
: this.db.query('SELECT key FROM cache WHERE expires > ?')
|
|
236
|
-
|
|
237
|
-
const params = pattern ? [pattern, Date.now()] : [Date.now()]
|
|
238
|
-
const results = query.all(...params) as Array<{ key: string }>
|
|
239
|
-
|
|
240
|
-
return results.map(row => row.key)
|
|
241
|
-
}
|
|
242
|
-
catch (error) {
|
|
243
|
-
console.error('Cache keys error:', error)
|
|
244
|
-
return []
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Get multiple values at once
|
|
250
|
-
*/
|
|
251
|
-
mget<T = any>(keys: string[]): Record<string, T | null> {
|
|
252
|
-
const result: Record<string, T | null> = {}
|
|
253
|
-
|
|
254
|
-
for (const key of keys) {
|
|
255
|
-
result[key] = this.get<T>(key)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return result
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Set multiple values at once
|
|
263
|
-
*/
|
|
264
|
-
mset<T = any>(entries: Record<string, T>, ttl?: number): boolean {
|
|
265
|
-
const transaction = this.db.transaction(() => {
|
|
266
|
-
for (const [key, value] of Object.entries(entries)) {
|
|
267
|
-
this.set(key, value, ttl)
|
|
268
|
-
}
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
try {
|
|
272
|
-
transaction()
|
|
273
|
-
return true
|
|
274
|
-
}
|
|
275
|
-
catch (error) {
|
|
276
|
-
console.error('Cache mset error:', error)
|
|
277
|
-
return false
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Increment numeric value
|
|
283
|
-
*/
|
|
284
|
-
increment(key: string, delta: number = 1, ttl?: number): number | null {
|
|
285
|
-
const current = this.get<number>(key)
|
|
286
|
-
const newValue = (current || 0) + delta
|
|
287
|
-
|
|
288
|
-
if (this.set(key, newValue, ttl)) {
|
|
289
|
-
return newValue
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return null
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Decrement numeric value
|
|
297
|
-
*/
|
|
298
|
-
decrement(key: string, delta: number = 1, ttl?: number): number | null {
|
|
299
|
-
return this.increment(key, -delta, ttl)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Set value only if key doesn't exist
|
|
304
|
-
*/
|
|
305
|
-
setnx<T = any>(key: string, value: T, ttl?: number): boolean {
|
|
306
|
-
if (this.has(key)) {
|
|
307
|
-
return false
|
|
308
|
-
}
|
|
309
|
-
return this.set(key, value, ttl)
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Get and delete value atomically
|
|
314
|
-
*/
|
|
315
|
-
getdel<T = any>(key: string): T | null {
|
|
316
|
-
const value = this.get<T>(key)
|
|
317
|
-
if (value !== null) {
|
|
318
|
-
this.delete(key)
|
|
319
|
-
}
|
|
320
|
-
return value
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Update TTL for existing key
|
|
325
|
-
*/
|
|
326
|
-
expire(key: string, ttl: number): boolean {
|
|
327
|
-
try {
|
|
328
|
-
const expires = Date.now() + (ttl * 1000)
|
|
329
|
-
const result = this.db.query('UPDATE cache SET expires = ? WHERE key = ? AND expires > ?')
|
|
330
|
-
.run(expires, key, Date.now())
|
|
331
|
-
|
|
332
|
-
return result.changes > 0
|
|
333
|
-
}
|
|
334
|
-
catch (error) {
|
|
335
|
-
console.error('Cache expire error:', error)
|
|
336
|
-
return false
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Get TTL for key
|
|
342
|
-
*/
|
|
343
|
-
ttl(key: string): number {
|
|
344
|
-
try {
|
|
345
|
-
const result = this.db.query('SELECT expires FROM cache WHERE key = ? AND expires > ?')
|
|
346
|
-
.get(key, Date.now()) as { expires: number } | null
|
|
347
|
-
|
|
348
|
-
if (!result)
|
|
349
|
-
return -1
|
|
350
|
-
|
|
351
|
-
return Math.floor((result.expires - Date.now()) / 1000)
|
|
352
|
-
}
|
|
353
|
-
catch (error) {
|
|
354
|
-
console.error('Cache TTL error:', error)
|
|
355
|
-
return -1
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Cleanup expired entries
|
|
361
|
-
*/
|
|
362
|
-
cleanup(): number {
|
|
363
|
-
try {
|
|
364
|
-
const result = this.preparedStatements.cleanup.run(Date.now())
|
|
365
|
-
this.updateCacheStats()
|
|
366
|
-
return result.changes
|
|
367
|
-
}
|
|
368
|
-
catch (error) {
|
|
369
|
-
console.error('Cache cleanup error:', error)
|
|
370
|
-
return 0
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Close database connection
|
|
376
|
-
*/
|
|
377
|
-
close(): void {
|
|
378
|
-
if (this.cleanupTimer) {
|
|
379
|
-
clearInterval(this.cleanupTimer)
|
|
380
|
-
}
|
|
381
|
-
this.db.close()
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Initialize SQLite database
|
|
386
|
-
*/
|
|
387
|
-
private initializeDatabase(): void {
|
|
388
|
-
this.db = new Database(this.config.filename)
|
|
389
|
-
|
|
390
|
-
// Configure database
|
|
391
|
-
this.db.exec(`PRAGMA synchronous = ${this.config.synchronous}`)
|
|
392
|
-
this.db.exec(`PRAGMA journal_mode = ${this.config.journalMode}`)
|
|
393
|
-
this.db.exec(`PRAGMA cache_size = ${this.config.cacheSize}`)
|
|
394
|
-
this.db.exec(`PRAGMA page_size = ${this.config.pageSize}`)
|
|
395
|
-
this.db.exec('PRAGMA temp_store = MEMORY')
|
|
396
|
-
this.db.exec('PRAGMA mmap_size = 268435456') // 256MB
|
|
397
|
-
|
|
398
|
-
// Create cache table
|
|
399
|
-
this.db.exec(`
|
|
400
|
-
CREATE TABLE IF NOT EXISTS cache (
|
|
401
|
-
key TEXT PRIMARY KEY,
|
|
402
|
-
value TEXT NOT NULL,
|
|
403
|
-
expires INTEGER NOT NULL,
|
|
404
|
-
created INTEGER NOT NULL,
|
|
405
|
-
accessed INTEGER NOT NULL,
|
|
406
|
-
hits INTEGER DEFAULT 0,
|
|
407
|
-
size INTEGER NOT NULL,
|
|
408
|
-
compressed INTEGER DEFAULT 0
|
|
409
|
-
)
|
|
410
|
-
`)
|
|
411
|
-
|
|
412
|
-
// Create indexes
|
|
413
|
-
this.db.exec('CREATE INDEX IF NOT EXISTS idx_expires ON cache(expires)')
|
|
414
|
-
this.db.exec('CREATE INDEX IF NOT EXISTS idx_accessed ON cache(accessed)')
|
|
415
|
-
this.db.exec('CREATE INDEX IF NOT EXISTS idx_size ON cache(size)')
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Prepare SQL statements
|
|
420
|
-
*/
|
|
421
|
-
private prepareStatements(): void {
|
|
422
|
-
this.preparedStatements = {
|
|
423
|
-
get: this.db.query(`
|
|
424
|
-
SELECT key, value, expires, created, accessed, hits, size, compressed
|
|
425
|
-
FROM cache
|
|
426
|
-
WHERE key = ? AND expires > ?
|
|
427
|
-
`),
|
|
428
|
-
|
|
429
|
-
set: this.db.query(`
|
|
430
|
-
INSERT OR REPLACE INTO cache
|
|
431
|
-
(key, value, expires, created, accessed, hits, size, compressed)
|
|
432
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
433
|
-
`),
|
|
434
|
-
|
|
435
|
-
delete: this.db.query('DELETE FROM cache WHERE key = ?'),
|
|
436
|
-
|
|
437
|
-
exists: this.db.query('SELECT 1 FROM cache WHERE key = ? AND expires > ?'),
|
|
438
|
-
|
|
439
|
-
clear: this.db.query('DELETE FROM cache'),
|
|
440
|
-
|
|
441
|
-
cleanup: this.db.query('DELETE FROM cache WHERE expires <= ?'),
|
|
442
|
-
|
|
443
|
-
stats: this.db.query(`
|
|
444
|
-
SELECT
|
|
445
|
-
COUNT(*) as total_entries,
|
|
446
|
-
SUM(size) as total_size,
|
|
447
|
-
AVG(hits) as avg_hits,
|
|
448
|
-
SUM(CASE WHEN compressed = 1 THEN size ELSE 0 END) as compressed_size
|
|
449
|
-
FROM cache
|
|
450
|
-
WHERE expires > ?
|
|
451
|
-
`),
|
|
452
|
-
|
|
453
|
-
touch: this.db.query(`
|
|
454
|
-
UPDATE cache
|
|
455
|
-
SET accessed = ?, hits = hits + 1
|
|
456
|
-
WHERE key = ?
|
|
457
|
-
`),
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* Start cleanup timer
|
|
463
|
-
*/
|
|
464
|
-
private startCleanupTimer(): void {
|
|
465
|
-
this.cleanupTimer = setInterval(() => {
|
|
466
|
-
const cleaned = this.cleanup()
|
|
467
|
-
if (cleaned > 0) {
|
|
468
|
-
console.warn(`SQLite cache cleanup: removed ${cleaned} expired entries`)
|
|
469
|
-
}
|
|
470
|
-
}, this.config.cleanupInterval * 1000)
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Evict entries if cache is too large
|
|
475
|
-
*/
|
|
476
|
-
private evictIfNeeded(newEntrySize: number): void {
|
|
477
|
-
const currentStats = this.preparedStatements.stats.get(Date.now()) as {
|
|
478
|
-
total_entries: number
|
|
479
|
-
total_size: number
|
|
480
|
-
} | null
|
|
481
|
-
|
|
482
|
-
if (!currentStats)
|
|
483
|
-
return
|
|
484
|
-
|
|
485
|
-
const projectedSize = currentStats.total_size + newEntrySize
|
|
486
|
-
|
|
487
|
-
if (projectedSize > this.config.maxSize) {
|
|
488
|
-
// Evict least recently accessed entries
|
|
489
|
-
const toEvict = Math.ceil(currentStats.total_entries * 0.1) // Evict 10%
|
|
490
|
-
|
|
491
|
-
const evictQuery = this.db.query(`
|
|
492
|
-
DELETE FROM cache
|
|
493
|
-
WHERE key IN (
|
|
494
|
-
SELECT key FROM cache
|
|
495
|
-
WHERE expires > ?
|
|
496
|
-
ORDER BY accessed ASC
|
|
497
|
-
LIMIT ?
|
|
498
|
-
)
|
|
499
|
-
`)
|
|
500
|
-
|
|
501
|
-
const result = evictQuery.run(Date.now(), toEvict)
|
|
502
|
-
this.stats.evictions += result.changes
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Update cache statistics
|
|
508
|
-
*/
|
|
509
|
-
private updateCacheStats(): void {
|
|
510
|
-
try {
|
|
511
|
-
const result = this.preparedStatements.stats.get(Date.now()) as {
|
|
512
|
-
total_entries: number
|
|
513
|
-
total_size: number
|
|
514
|
-
avg_hits: number
|
|
515
|
-
compressed_size: number
|
|
516
|
-
} | null
|
|
517
|
-
|
|
518
|
-
if (result) {
|
|
519
|
-
this.stats.totalEntries = result.total_entries || 0
|
|
520
|
-
this.stats.totalSize = result.total_size || 0
|
|
521
|
-
this.stats.compressionRatio = result.total_size > 0
|
|
522
|
-
? (result.compressed_size / result.total_size) * 100
|
|
523
|
-
: 0
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
catch (error) {
|
|
527
|
-
console.error('Error updating cache stats:', error)
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* Update operation statistics
|
|
533
|
-
*/
|
|
534
|
-
private updateStats(operation: 'hit' | 'miss' | 'set', responseTime: number): void {
|
|
535
|
-
if (operation === 'hit') {
|
|
536
|
-
this.stats.hitRate = (this.stats.hitRate + 1) / 2
|
|
537
|
-
}
|
|
538
|
-
else if (operation === 'miss') {
|
|
539
|
-
this.stats.missRate = (this.stats.missRate + 1) / 2
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
this.stats.averageAccessTime = (this.stats.averageAccessTime + responseTime) / 2
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/**
|
|
546
|
-
* Compress string value
|
|
547
|
-
*/
|
|
548
|
-
private compress(value: string): string | null {
|
|
549
|
-
try {
|
|
550
|
-
const compressed = Bun.gzipSync(value)
|
|
551
|
-
return Buffer.from(compressed).toString('base64')
|
|
552
|
-
}
|
|
553
|
-
catch (error) {
|
|
554
|
-
console.error('Compression error:', error)
|
|
555
|
-
return null
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Decompress string value
|
|
561
|
-
*/
|
|
562
|
-
private decompress(value: string): string {
|
|
563
|
-
try {
|
|
564
|
-
const buffer = Buffer.from(value, 'base64')
|
|
565
|
-
const decompressed = Bun.gunzipSync(buffer)
|
|
566
|
-
return new TextDecoder().decode(decompressed)
|
|
567
|
-
}
|
|
568
|
-
catch (error) {
|
|
569
|
-
console.error('Decompression error:', error)
|
|
570
|
-
return value
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
* SQLite cache factory
|
|
577
|
-
*/
|
|
578
|
-
export class SQLiteCacheFactory {
|
|
579
|
-
/**
|
|
580
|
-
* Create in-memory cache
|
|
581
|
-
*/
|
|
582
|
-
static createMemoryCache(config?: Partial<SQLiteCacheConfig>): SQLiteCache {
|
|
583
|
-
return new SQLiteCache({
|
|
584
|
-
filename: ':memory:',
|
|
585
|
-
memory: true,
|
|
586
|
-
wal: false,
|
|
587
|
-
synchronous: 'OFF',
|
|
588
|
-
...config,
|
|
589
|
-
})
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* Create persistent cache
|
|
594
|
-
*/
|
|
595
|
-
static createPersistentCache(filename: string, config?: Partial<SQLiteCacheConfig>): SQLiteCache {
|
|
596
|
-
return new SQLiteCache({
|
|
597
|
-
filename,
|
|
598
|
-
memory: false,
|
|
599
|
-
wal: true,
|
|
600
|
-
synchronous: 'NORMAL',
|
|
601
|
-
...config,
|
|
602
|
-
})
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
/**
|
|
606
|
-
* Create high-performance cache
|
|
607
|
-
*/
|
|
608
|
-
static createHighPerformanceCache(config?: Partial<SQLiteCacheConfig>): SQLiteCache {
|
|
609
|
-
return new SQLiteCache({
|
|
610
|
-
filename: ':memory:',
|
|
611
|
-
memory: true,
|
|
612
|
-
wal: false,
|
|
613
|
-
synchronous: 'OFF',
|
|
614
|
-
cacheSize: 50000,
|
|
615
|
-
pageSize: 8192,
|
|
616
|
-
maxSize: 500 * 1024 * 1024, // 500MB
|
|
617
|
-
compression: true,
|
|
618
|
-
cleanupInterval: 60,
|
|
619
|
-
...config,
|
|
620
|
-
})
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
/**
|
|
624
|
-
* Create development cache with debugging
|
|
625
|
-
*/
|
|
626
|
-
static createDevelopmentCache(config?: Partial<SQLiteCacheConfig>): SQLiteCache {
|
|
627
|
-
return new SQLiteCache({
|
|
628
|
-
filename: 'dev-cache.db',
|
|
629
|
-
memory: false,
|
|
630
|
-
wal: true,
|
|
631
|
-
synchronous: 'FULL',
|
|
632
|
-
maxSize: 50 * 1024 * 1024, // 50MB
|
|
633
|
-
compression: false,
|
|
634
|
-
cleanupInterval: 30,
|
|
635
|
-
...config,
|
|
636
|
-
})
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
/**
|
|
641
|
-
* Cache middleware factory
|
|
642
|
-
*/
|
|
643
|
-
export function createSQLiteCacheMiddleware(cache: SQLiteCache, options: {
|
|
644
|
-
keyGenerator?: (request: Request) => string
|
|
645
|
-
ttl?: number
|
|
646
|
-
skipMethods?: string[]
|
|
647
|
-
skipPaths?: string[]
|
|
648
|
-
skipHeaders?: string[]
|
|
649
|
-
} = {}) {
|
|
650
|
-
const {
|
|
651
|
-
keyGenerator = req => `${req.method}:${new URL(req.url).pathname}`,
|
|
652
|
-
ttl = 300, // 5 minutes
|
|
653
|
-
skipMethods = ['POST', 'PUT', 'PATCH', 'DELETE'],
|
|
654
|
-
skipPaths = [],
|
|
655
|
-
skipHeaders = ['authorization', 'cookie'],
|
|
656
|
-
} = options
|
|
657
|
-
|
|
658
|
-
return async (request: Request, next: () => Promise<Response>): Promise<Response> => {
|
|
659
|
-
// Skip caching for certain methods
|
|
660
|
-
if (skipMethods.includes(request.method)) {
|
|
661
|
-
return await next()
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// Skip caching for certain paths
|
|
665
|
-
const url = new URL(request.url)
|
|
666
|
-
if (skipPaths.some(path => url.pathname.startsWith(path))) {
|
|
667
|
-
return await next()
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// Skip caching if certain headers are present
|
|
671
|
-
if (skipHeaders.some(header => request.headers.has(header))) {
|
|
672
|
-
return await next()
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
const cacheKey = keyGenerator(request)
|
|
676
|
-
|
|
677
|
-
// Try to get from cache
|
|
678
|
-
const cached = cache.get<{
|
|
679
|
-
status: number
|
|
680
|
-
headers: Record<string, string>
|
|
681
|
-
body: string
|
|
682
|
-
}>(cacheKey)
|
|
683
|
-
|
|
684
|
-
if (cached) {
|
|
685
|
-
return new Response(cached.body, {
|
|
686
|
-
status: cached.status,
|
|
687
|
-
headers: {
|
|
688
|
-
...cached.headers,
|
|
689
|
-
'X-Cache': 'HIT',
|
|
690
|
-
},
|
|
691
|
-
})
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// Get response and cache it
|
|
695
|
-
const response = await next()
|
|
696
|
-
|
|
697
|
-
// Only cache successful responses
|
|
698
|
-
if (response.status >= 200 && response.status < 300) {
|
|
699
|
-
const body = await response.text()
|
|
700
|
-
const headers = Object.fromEntries(response.headers.entries())
|
|
701
|
-
|
|
702
|
-
cache.set(cacheKey, {
|
|
703
|
-
status: response.status,
|
|
704
|
-
headers,
|
|
705
|
-
body,
|
|
706
|
-
}, ttl)
|
|
707
|
-
|
|
708
|
-
return new Response(body, {
|
|
709
|
-
status: response.status,
|
|
710
|
-
headers: {
|
|
711
|
-
...response.headers,
|
|
712
|
-
'X-Cache': 'MISS',
|
|
713
|
-
},
|
|
714
|
-
})
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
return response
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
/**
|
|
722
|
-
* SQLite cache helpers
|
|
723
|
-
*/
|
|
724
|
-
export const SQLiteCacheHelpers = {
|
|
725
|
-
/**
|
|
726
|
-
* Create session cache
|
|
727
|
-
*/
|
|
728
|
-
createSessionCache: (cache: SQLiteCache): {
|
|
729
|
-
get: (sessionId: string) => any
|
|
730
|
-
set: (sessionId: string, data: any, ttl?: number) => void
|
|
731
|
-
delete: (sessionId: string) => boolean
|
|
732
|
-
touch: (sessionId: string, ttl?: number) => boolean
|
|
733
|
-
} => ({
|
|
734
|
-
get: (sessionId: string): any => cache.get(`session:${sessionId}`),
|
|
735
|
-
set: (sessionId: string, data: any, ttl = 3600): void => { cache.set(`session:${sessionId}`, data, ttl) },
|
|
736
|
-
delete: (sessionId: string): boolean => cache.delete(`session:${sessionId}`),
|
|
737
|
-
touch: (sessionId: string, ttl = 3600): boolean => cache.expire(`session:${sessionId}`, ttl),
|
|
738
|
-
}),
|
|
739
|
-
|
|
740
|
-
/**
|
|
741
|
-
* Create rate limiting cache
|
|
742
|
-
*/
|
|
743
|
-
createRateLimitCache: (cache: SQLiteCache): {
|
|
744
|
-
increment: (key: string, window?: number) => number
|
|
745
|
-
get: (key: string) => number
|
|
746
|
-
reset: (key: string) => boolean
|
|
747
|
-
} => ({
|
|
748
|
-
increment: (key: string, window = 60): number => {
|
|
749
|
-
const current = cache.get<number>(`rate:${key}`) || 0
|
|
750
|
-
const newValue = current + 1
|
|
751
|
-
cache.set(`rate:${key}`, newValue, window)
|
|
752
|
-
return newValue
|
|
753
|
-
},
|
|
754
|
-
|
|
755
|
-
get: (key: string): number => cache.get<number>(`rate:${key}`) || 0,
|
|
756
|
-
|
|
757
|
-
reset: (key: string): boolean => cache.delete(`rate:${key}`),
|
|
758
|
-
}),
|
|
759
|
-
|
|
760
|
-
/**
|
|
761
|
-
* Create query result cache
|
|
762
|
-
*/
|
|
763
|
-
createQueryCache: (cache: SQLiteCache): {
|
|
764
|
-
get: (query: string, params: any[]) => any
|
|
765
|
-
set: (query: string, params: any[], result: any, ttl?: number) => void
|
|
766
|
-
invalidate: (pattern: string) => void
|
|
767
|
-
} => ({
|
|
768
|
-
get: (query: string, params: any[]): any => {
|
|
769
|
-
const key = `query:${Bun.hash(query + JSON.stringify(params))}`
|
|
770
|
-
return cache.get(key)
|
|
771
|
-
},
|
|
772
|
-
|
|
773
|
-
set: (query: string, params: any[], result: any, ttl = 300): void => {
|
|
774
|
-
const key = `query:${Bun.hash(query + JSON.stringify(params))}`
|
|
775
|
-
cache.set(key, result, ttl)
|
|
776
|
-
},
|
|
777
|
-
|
|
778
|
-
invalidate: (pattern: string): void => {
|
|
779
|
-
const keys = cache.keys(`query:*${pattern}*`)
|
|
780
|
-
keys.forEach(key => cache.delete(key))
|
|
781
|
-
},
|
|
782
|
-
}),
|
|
783
|
-
}
|