bunsane 0.1.4 → 0.2.0

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.
Files changed (257) hide show
  1. package/.claude/settings.local.json +47 -0
  2. package/.claude/skills/update-memory.md +74 -0
  3. package/.prettierrc +4 -0
  4. package/.serena/memories/architectural-decision-no-dependency-injection.md +76 -0
  5. package/.serena/memories/architecture.md +154 -0
  6. package/.serena/memories/cache-interface-refactoring-2026-01-24.md +165 -0
  7. package/.serena/memories/code_style_and_conventions.md +76 -0
  8. package/.serena/memories/project_overview.md +43 -0
  9. package/.serena/memories/schema-dsl-plan.md +107 -0
  10. package/.serena/memories/suggested_commands.md +80 -0
  11. package/.serena/memories/typescript-compilation-status.md +54 -0
  12. package/.serena/project.yml +114 -0
  13. package/TODO.md +1 -7
  14. package/bun.lock +150 -4
  15. package/bunfig.toml +10 -0
  16. package/config/cache.config.ts +77 -0
  17. package/config/upload.config.ts +4 -5
  18. package/core/App.ts +870 -123
  19. package/core/ArcheType.ts +2268 -377
  20. package/core/BatchLoader.ts +181 -71
  21. package/core/Config.ts +153 -0
  22. package/core/Decorators.ts +4 -1
  23. package/core/Entity.ts +621 -92
  24. package/core/EntityHookManager.ts +1 -1
  25. package/core/EntityInterface.ts +3 -1
  26. package/core/EntityManager.ts +1 -13
  27. package/core/ErrorHandler.ts +8 -2
  28. package/core/Logger.ts +9 -0
  29. package/core/Middleware.ts +34 -0
  30. package/core/RequestContext.ts +5 -1
  31. package/core/RequestLoaders.ts +227 -93
  32. package/core/SchedulerManager.ts +193 -52
  33. package/core/cache/CacheAnalytics.ts +399 -0
  34. package/core/cache/CacheFactory.ts +145 -0
  35. package/core/cache/CacheManager.ts +520 -0
  36. package/core/cache/CacheProvider.ts +34 -0
  37. package/core/cache/CacheWarmer.ts +157 -0
  38. package/core/cache/CompressionUtils.ts +110 -0
  39. package/core/cache/MemoryCache.ts +251 -0
  40. package/core/cache/MultiLevelCache.ts +180 -0
  41. package/core/cache/NoOpCache.ts +53 -0
  42. package/core/cache/RedisCache.ts +464 -0
  43. package/core/cache/TTLStrategy.ts +254 -0
  44. package/core/cache/index.ts +6 -0
  45. package/core/components/BaseComponent.ts +120 -0
  46. package/core/{ComponentRegistry.ts → components/ComponentRegistry.ts} +148 -54
  47. package/core/components/Decorators.ts +88 -0
  48. package/core/components/Interfaces.ts +7 -0
  49. package/core/components/index.ts +5 -0
  50. package/core/decorators/EntityHooks.ts +0 -3
  51. package/core/decorators/IndexedField.ts +26 -0
  52. package/core/decorators/ScheduledTask.ts +0 -47
  53. package/core/events/EntityLifecycleEvents.ts +1 -1
  54. package/core/health.ts +112 -0
  55. package/core/metadata/definitions/ArcheType.ts +14 -0
  56. package/core/metadata/definitions/Component.ts +9 -0
  57. package/core/metadata/definitions/gqlObject.ts +1 -1
  58. package/core/metadata/index.ts +42 -1
  59. package/core/metadata/metadata-storage.ts +28 -2
  60. package/core/middleware/AccessLog.ts +59 -0
  61. package/core/middleware/RequestId.ts +38 -0
  62. package/core/middleware/SecurityHeaders.ts +62 -0
  63. package/core/middleware/index.ts +3 -0
  64. package/core/scheduler/DistributedLock.ts +266 -0
  65. package/core/scheduler/index.ts +15 -0
  66. package/core/validateEnv.ts +92 -0
  67. package/database/DatabaseHelper.ts +416 -40
  68. package/database/IndexingStrategy.ts +342 -0
  69. package/database/PreparedStatementCache.ts +226 -0
  70. package/database/index.ts +32 -7
  71. package/database/sqlHelpers.ts +14 -2
  72. package/endpoints/archetypes.ts +362 -0
  73. package/endpoints/components.ts +58 -0
  74. package/endpoints/entity.ts +80 -0
  75. package/endpoints/index.ts +27 -0
  76. package/endpoints/query.ts +93 -0
  77. package/endpoints/stats.ts +76 -0
  78. package/endpoints/tables.ts +212 -0
  79. package/endpoints/types.ts +155 -0
  80. package/gql/ArchetypeOperations.ts +32 -86
  81. package/gql/Generator.ts +27 -315
  82. package/gql/GeneratorV2.ts +37 -0
  83. package/gql/builders/InputTypeBuilder.ts +99 -0
  84. package/gql/builders/ResolverBuilder.ts +234 -0
  85. package/gql/builders/TypeDefBuilder.ts +105 -0
  86. package/gql/builders/index.ts +3 -0
  87. package/gql/decorators/Upload.ts +1 -1
  88. package/gql/depthLimit.ts +85 -0
  89. package/gql/graph/GraphNode.ts +224 -0
  90. package/gql/graph/SchemaGraph.ts +278 -0
  91. package/gql/helpers.ts +8 -2
  92. package/gql/index.ts +56 -4
  93. package/gql/middleware.ts +79 -0
  94. package/gql/orchestration/GraphQLSchemaOrchestrator.ts +241 -0
  95. package/gql/orchestration/index.ts +1 -0
  96. package/gql/scanner/ServiceScanner.ts +347 -0
  97. package/gql/schema/index.ts +458 -0
  98. package/gql/strategies/TypeGenerationStrategy.ts +329 -0
  99. package/gql/types.ts +1 -0
  100. package/gql/utils/TypeSignature.ts +220 -0
  101. package/gql/utils/index.ts +1 -0
  102. package/gql/visitors/ArchetypePreprocessorVisitor.ts +80 -0
  103. package/gql/visitors/DeduplicationVisitor.ts +82 -0
  104. package/gql/visitors/GraphVisitor.ts +78 -0
  105. package/gql/visitors/ResolverGeneratorVisitor.ts +122 -0
  106. package/gql/visitors/SchemaGeneratorVisitor.ts +851 -0
  107. package/gql/visitors/TypeCollectorVisitor.ts +79 -0
  108. package/gql/visitors/VisitorComposer.ts +96 -0
  109. package/gql/visitors/index.ts +7 -0
  110. package/package.json +59 -37
  111. package/plugins/index.ts +2 -2
  112. package/query/CTENode.ts +97 -0
  113. package/query/ComponentInclusionNode.ts +689 -0
  114. package/query/FilterBuilder.ts +127 -0
  115. package/query/FilterBuilderRegistry.ts +202 -0
  116. package/query/OrNode.ts +517 -0
  117. package/query/OrQuery.ts +42 -0
  118. package/query/Query.ts +1022 -0
  119. package/query/QueryContext.ts +170 -0
  120. package/query/QueryDAG.ts +122 -0
  121. package/query/QueryNode.ts +65 -0
  122. package/query/SourceNode.ts +53 -0
  123. package/query/builders/FullTextSearchBuilder.ts +236 -0
  124. package/query/index.ts +21 -0
  125. package/scheduler/index.ts +40 -8
  126. package/service/Service.ts +2 -1
  127. package/service/ServiceRegistry.ts +6 -5
  128. package/{core/storage → storage}/LocalStorageProvider.ts +2 -2
  129. package/storage/S3StorageProvider.ts +316 -0
  130. package/{core/storage → storage}/StorageProvider.ts +7 -3
  131. package/studio/bun.lock +482 -0
  132. package/studio/index.html +13 -0
  133. package/studio/package.json +39 -0
  134. package/studio/postcss.config.js +6 -0
  135. package/studio/src/components/DataTable.tsx +211 -0
  136. package/studio/src/components/Layout.tsx +13 -0
  137. package/studio/src/components/PageContainer.tsx +9 -0
  138. package/studio/src/components/PageHeader.tsx +13 -0
  139. package/studio/src/components/SearchBar.tsx +57 -0
  140. package/studio/src/components/Sidebar.tsx +294 -0
  141. package/studio/src/components/ui/button.tsx +56 -0
  142. package/studio/src/components/ui/checkbox.tsx +26 -0
  143. package/studio/src/components/ui/input.tsx +25 -0
  144. package/studio/src/hooks/useDataTable.ts +131 -0
  145. package/studio/src/index.css +36 -0
  146. package/studio/src/lib/api.ts +186 -0
  147. package/studio/src/lib/utils.ts +13 -0
  148. package/studio/src/main.tsx +17 -0
  149. package/studio/src/pages/ArcheType.tsx +239 -0
  150. package/studio/src/pages/Components.tsx +124 -0
  151. package/studio/src/pages/EntityInspector.tsx +302 -0
  152. package/studio/src/pages/QueryRunner.tsx +246 -0
  153. package/studio/src/pages/Table.tsx +94 -0
  154. package/studio/src/pages/Welcome.tsx +241 -0
  155. package/studio/src/routes.tsx +45 -0
  156. package/studio/src/store/archeTypeSettings.ts +30 -0
  157. package/studio/src/store/studio.ts +65 -0
  158. package/studio/src/utils/columnHelpers.tsx +114 -0
  159. package/studio/studio-instructions.md +81 -0
  160. package/studio/tailwind.config.js +77 -0
  161. package/studio/tsconfig.json +24 -0
  162. package/studio/utils.ts +54 -0
  163. package/studio/vite.config.js +19 -0
  164. package/swagger/generator.ts +1 -1
  165. package/tests/e2e/http.test.ts +126 -0
  166. package/tests/fixtures/archetypes/TestUserArchetype.ts +21 -0
  167. package/tests/fixtures/components/TestOrder.ts +23 -0
  168. package/tests/fixtures/components/TestProduct.ts +23 -0
  169. package/tests/fixtures/components/TestUser.ts +20 -0
  170. package/tests/fixtures/components/index.ts +6 -0
  171. package/tests/graphql/SchemaGeneration.test.ts +90 -0
  172. package/tests/graphql/builders/ResolverBuilder.test.ts +223 -0
  173. package/tests/graphql/builders/TypeDefBuilder.test.ts +153 -0
  174. package/tests/integration/archetype/ArcheType.persistence.test.ts +241 -0
  175. package/tests/integration/cache/CacheInvalidation.test.ts +259 -0
  176. package/tests/integration/entity/Entity.persistence.test.ts +333 -0
  177. package/tests/integration/query/Query.exec.test.ts +523 -0
  178. package/tests/pglite-setup.ts +61 -0
  179. package/tests/setup.ts +164 -0
  180. package/tests/stress/BenchmarkRunner.ts +203 -0
  181. package/tests/stress/DataSeeder.ts +190 -0
  182. package/tests/stress/StressTestReporter.ts +229 -0
  183. package/tests/stress/cursor-perf-test.ts +171 -0
  184. package/tests/stress/fixtures/StressTestComponents.ts +58 -0
  185. package/tests/stress/index.ts +7 -0
  186. package/tests/stress/scenarios/query-benchmarks.test.ts +285 -0
  187. package/tests/unit/BatchLoader.test.ts +82 -0
  188. package/tests/unit/archetype/ArcheType.test.ts +107 -0
  189. package/tests/unit/cache/CacheManager.test.ts +347 -0
  190. package/tests/unit/cache/MemoryCache.test.ts +260 -0
  191. package/tests/unit/cache/RedisCache.test.ts +411 -0
  192. package/tests/unit/entity/Entity.components.test.ts +244 -0
  193. package/tests/unit/entity/Entity.test.ts +345 -0
  194. package/tests/unit/gql/depthLimit.test.ts +203 -0
  195. package/tests/unit/gql/operationMiddleware.test.ts +293 -0
  196. package/tests/unit/health/Health.test.ts +129 -0
  197. package/tests/unit/middleware/AccessLog.test.ts +37 -0
  198. package/tests/unit/middleware/Middleware.test.ts +98 -0
  199. package/tests/unit/middleware/RequestId.test.ts +54 -0
  200. package/tests/unit/middleware/SecurityHeaders.test.ts +66 -0
  201. package/tests/unit/query/FilterBuilder.test.ts +111 -0
  202. package/tests/unit/query/Query.test.ts +308 -0
  203. package/tests/unit/scheduler/DistributedLock.test.ts +274 -0
  204. package/tests/unit/schema/schema-integration.test.ts +426 -0
  205. package/tests/unit/schema/schema.test.ts +580 -0
  206. package/tests/unit/storage/S3StorageProvider.test.ts +571 -0
  207. package/tests/unit/upload/RestUpload.test.ts +267 -0
  208. package/tests/unit/validateEnv.test.ts +82 -0
  209. package/tests/utils/entity-tracker.ts +57 -0
  210. package/tests/utils/index.ts +13 -0
  211. package/tests/utils/test-context.ts +149 -0
  212. package/tsconfig.json +5 -1
  213. package/types/archetype.types.ts +6 -0
  214. package/types/hooks.types.ts +1 -1
  215. package/types/query.types.ts +110 -0
  216. package/types/scheduler.types.ts +68 -7
  217. package/types/upload.types.ts +1 -0
  218. package/{core → upload}/FileValidator.ts +10 -1
  219. package/upload/RestUpload.ts +130 -0
  220. package/{core/components → upload}/UploadComponent.ts +11 -11
  221. package/{core → upload}/UploadManager.ts +3 -3
  222. package/upload/index.ts +23 -7
  223. package/utils/UploadHelper.ts +27 -6
  224. package/utils/cronParser.ts +16 -6
  225. package/.github/workflows/deploy-docs.yml +0 -57
  226. package/core/Components.ts +0 -202
  227. package/core/EntityCache.ts +0 -15
  228. package/core/Query.ts +0 -880
  229. package/docs/README.md +0 -149
  230. package/docs/_coverpage.md +0 -36
  231. package/docs/_sidebar.md +0 -23
  232. package/docs/api/core.md +0 -568
  233. package/docs/api/hooks.md +0 -554
  234. package/docs/api/index.md +0 -222
  235. package/docs/api/query.md +0 -678
  236. package/docs/api/service.md +0 -744
  237. package/docs/core-concepts/archetypes.md +0 -512
  238. package/docs/core-concepts/components.md +0 -498
  239. package/docs/core-concepts/entity.md +0 -314
  240. package/docs/core-concepts/hooks.md +0 -683
  241. package/docs/core-concepts/query.md +0 -588
  242. package/docs/core-concepts/services.md +0 -647
  243. package/docs/examples/code-examples.md +0 -425
  244. package/docs/getting-started.md +0 -337
  245. package/docs/index.html +0 -97
  246. package/tests/bench/insert.bench.ts +0 -60
  247. package/tests/bench/relations.bench.ts +0 -270
  248. package/tests/bench/sorting.bench.ts +0 -416
  249. package/tests/component-hooks-simple.test.ts +0 -117
  250. package/tests/component-hooks.test.ts +0 -1461
  251. package/tests/component.test.ts +0 -339
  252. package/tests/errorHandling.test.ts +0 -155
  253. package/tests/hooks.test.ts +0 -667
  254. package/tests/query-sorting.test.ts +0 -101
  255. package/tests/query.test.ts +0 -81
  256. package/tests/relations.test.ts +0 -170
  257. package/tests/scheduler.test.ts +0 -724
@@ -0,0 +1,399 @@
1
+ import type { CacheProvider, CacheStats } from './CacheProvider.js';
2
+
3
+ /**
4
+ * CacheReport provides comprehensive analytics about cache performance
5
+ * and recommendations for optimization.
6
+ */
7
+ export interface CacheReport {
8
+ /** Overall cache hit rate (0.0 to 1.0) */
9
+ hitRate: number;
10
+
11
+ /** Total number of cache requests */
12
+ totalRequests: number;
13
+
14
+ /** Number of cache hits */
15
+ hits: number;
16
+
17
+ /** Number of cache misses */
18
+ misses: number;
19
+
20
+ /** Average response time in milliseconds */
21
+ averageLatency: number;
22
+
23
+ /** Peak memory usage in bytes */
24
+ memoryUsage?: number;
25
+
26
+ /** Cache efficiency score (0.0 to 1.0) */
27
+ efficiency: number;
28
+
29
+ /** List of optimization recommendations */
30
+ recommendations: string[];
31
+
32
+ /** Detailed breakdown by operation type */
33
+ breakdown: {
34
+ get: { hits: number; misses: number; hitRate: number };
35
+ set: { operations: number; averageLatency: number };
36
+ delete: { operations: number; averageLatency: number };
37
+ };
38
+
39
+ /** Time period covered by this report */
40
+ timeRange: {
41
+ start: Date;
42
+ end: Date;
43
+ };
44
+ }
45
+
46
+ /**
47
+ * CacheAnalytics tracks cache performance metrics and provides
48
+ * optimization recommendations based on usage patterns.
49
+ *
50
+ * Features:
51
+ * - Hit/miss rate tracking
52
+ * - Latency monitoring
53
+ * - Memory usage analysis
54
+ * - Automated recommendations
55
+ * - Performance trend analysis
56
+ */
57
+ export class CacheAnalytics {
58
+ private metrics: {
59
+ hits: number;
60
+ misses: number;
61
+ totalRequests: number;
62
+ latencies: number[];
63
+ operationLatencies: Map<string, number[]>;
64
+ memoryUsage: number[];
65
+ startTime: Date;
66
+ };
67
+
68
+ private readonly maxLatencySamples = 1000;
69
+ private readonly maxMemorySamples = 100;
70
+
71
+ constructor() {
72
+ this.metrics = {
73
+ hits: 0,
74
+ misses: 0,
75
+ totalRequests: 0,
76
+ latencies: [],
77
+ operationLatencies: new Map(),
78
+ memoryUsage: [],
79
+ startTime: new Date()
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Records a cache hit
85
+ */
86
+ recordHit(operation: string = 'get', latency?: number): void {
87
+ this.metrics.hits++;
88
+ this.metrics.totalRequests++;
89
+ this.recordLatency(operation, latency);
90
+ }
91
+
92
+ /**
93
+ * Records a cache miss
94
+ */
95
+ recordMiss(operation: string = 'get', latency?: number): void {
96
+ this.metrics.misses++;
97
+ this.metrics.totalRequests++;
98
+ this.recordLatency(operation, latency);
99
+ }
100
+
101
+ /**
102
+ * Records operation latency
103
+ */
104
+ recordLatency(operation: string, latency?: number): void {
105
+ if (latency !== undefined) {
106
+ this.metrics.latencies.push(latency);
107
+
108
+ // Keep only recent samples
109
+ if (this.metrics.latencies.length > this.maxLatencySamples) {
110
+ this.metrics.latencies.shift();
111
+ }
112
+
113
+ // Record per-operation latency
114
+ if (!this.metrics.operationLatencies.has(operation)) {
115
+ this.metrics.operationLatencies.set(operation, []);
116
+ }
117
+ const opLatencies = this.metrics.operationLatencies.get(operation)!;
118
+ opLatencies.push(latency);
119
+
120
+ // Keep only recent samples per operation
121
+ if (opLatencies.length > this.maxLatencySamples / 10) {
122
+ opLatencies.shift();
123
+ }
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Records memory usage
129
+ */
130
+ recordMemoryUsage(bytes: number): void {
131
+ this.metrics.memoryUsage.push(bytes);
132
+
133
+ // Keep only recent samples
134
+ if (this.metrics.memoryUsage.length > this.maxMemorySamples) {
135
+ this.metrics.memoryUsage.shift();
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Generates a comprehensive cache performance report
141
+ */
142
+ getReport(): CacheReport {
143
+ const hitRate = this.metrics.totalRequests > 0 ? this.metrics.hits / this.metrics.totalRequests : 0;
144
+ const averageLatency = this.metrics.latencies.length > 0
145
+ ? this.metrics.latencies.reduce((a, b) => a + b, 0) / this.metrics.latencies.length
146
+ : 0;
147
+
148
+ const memoryUsage = this.metrics.memoryUsage.length > 0
149
+ ? Math.max(...this.metrics.memoryUsage)
150
+ : undefined;
151
+
152
+ const efficiency = this.calculateEfficiency(hitRate, averageLatency);
153
+
154
+ const recommendations = this.generateRecommendations(hitRate, averageLatency, memoryUsage);
155
+
156
+ const breakdown = this.generateBreakdown();
157
+
158
+ return {
159
+ hitRate,
160
+ totalRequests: this.metrics.totalRequests,
161
+ hits: this.metrics.hits,
162
+ misses: this.metrics.misses,
163
+ averageLatency,
164
+ memoryUsage,
165
+ efficiency,
166
+ recommendations,
167
+ breakdown,
168
+ timeRange: {
169
+ start: this.metrics.startTime,
170
+ end: new Date()
171
+ }
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Calculates cache efficiency score
177
+ */
178
+ private calculateEfficiency(hitRate: number, averageLatency: number): number {
179
+ // Efficiency is based on hit rate and latency
180
+ // Higher hit rate and lower latency = higher efficiency
181
+ const latencyScore = Math.max(0, 1 - (averageLatency / 100)); // Assume 100ms is poor
182
+ const hitRateScore = hitRate;
183
+
184
+ return (latencyScore + hitRateScore) / 2;
185
+ }
186
+
187
+ /**
188
+ * Generates optimization recommendations
189
+ */
190
+ private generateRecommendations(hitRate: number, averageLatency: number, memoryUsage?: number): string[] {
191
+ const recommendations: string[] = [];
192
+
193
+ if (hitRate < 0.5) {
194
+ recommendations.push('Cache hit rate is below 50%. Consider increasing TTL or preloading frequently accessed data.');
195
+ }
196
+
197
+ if (hitRate > 0.9) {
198
+ recommendations.push('Excellent hit rate! Consider increasing TTL to reduce database load further.');
199
+ }
200
+
201
+ if (averageLatency > 50) {
202
+ recommendations.push('High latency detected. Consider using faster storage or optimizing cache key generation.');
203
+ }
204
+
205
+ if (averageLatency < 1) {
206
+ recommendations.push('Very low latency achieved. Cache performance is optimal.');
207
+ }
208
+
209
+ if (memoryUsage && memoryUsage > 100 * 1024 * 1024) { // 100MB
210
+ recommendations.push('High memory usage detected. Consider implementing compression or reducing TTL.');
211
+ }
212
+
213
+ if (this.metrics.totalRequests < 100) {
214
+ recommendations.push('Low request volume. Monitor performance as traffic increases.');
215
+ }
216
+
217
+ return recommendations;
218
+ }
219
+
220
+ /**
221
+ * Generates detailed breakdown by operation type
222
+ */
223
+ private generateBreakdown() {
224
+ const getLatencies = this.metrics.operationLatencies.get('get') || [];
225
+ const setLatencies = this.metrics.operationLatencies.get('set') || [];
226
+ const deleteLatencies = this.metrics.operationLatencies.get('delete') || [];
227
+
228
+ const getHits = this.metrics.hits; // Assuming all hits are from get operations
229
+ const getMisses = this.metrics.misses; // Assuming all misses are from get operations
230
+ const getHitRate = (getHits + getMisses) > 0 ? getHits / (getHits + getMisses) : 0;
231
+
232
+ return {
233
+ get: {
234
+ hits: getHits,
235
+ misses: getMisses,
236
+ hitRate: getHitRate
237
+ },
238
+ set: {
239
+ operations: setLatencies.length,
240
+ averageLatency: setLatencies.length > 0 ? setLatencies.reduce((a, b) => a + b, 0) / setLatencies.length : 0
241
+ },
242
+ delete: {
243
+ operations: deleteLatencies.length,
244
+ averageLatency: deleteLatencies.length > 0 ? deleteLatencies.reduce((a, b) => a + b, 0) / deleteLatencies.length : 0
245
+ }
246
+ };
247
+ }
248
+
249
+ /**
250
+ * Resets all metrics
251
+ */
252
+ reset(): void {
253
+ this.metrics = {
254
+ hits: 0,
255
+ misses: 0,
256
+ totalRequests: 0,
257
+ latencies: [],
258
+ operationLatencies: new Map(),
259
+ memoryUsage: [],
260
+ startTime: new Date()
261
+ };
262
+ }
263
+
264
+ /**
265
+ * Gets current metrics summary
266
+ */
267
+ getSummary(): {
268
+ hitRate: number;
269
+ totalRequests: number;
270
+ averageLatency: number;
271
+ efficiency: number;
272
+ } {
273
+ const report = this.getReport();
274
+ return {
275
+ hitRate: report.hitRate,
276
+ totalRequests: report.totalRequests,
277
+ averageLatency: report.averageLatency,
278
+ efficiency: report.efficiency
279
+ };
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Enhanced cache provider with analytics integration
285
+ */
286
+ export class AnalyticsCacheProvider implements CacheProvider {
287
+ private cache: CacheProvider;
288
+ private analytics: CacheAnalytics;
289
+
290
+ constructor(cache: CacheProvider, analytics?: CacheAnalytics) {
291
+ this.cache = cache;
292
+ this.analytics = analytics || new CacheAnalytics();
293
+ }
294
+
295
+ async get(key: string): Promise<any | null> {
296
+ const startTime = Date.now();
297
+ const result = await this.cache.get(key);
298
+ const latency = Date.now() - startTime;
299
+
300
+ if (result !== null) {
301
+ this.analytics.recordHit('get', latency);
302
+ } else {
303
+ this.analytics.recordMiss('get', latency);
304
+ }
305
+
306
+ return result;
307
+ }
308
+
309
+ async set(key: string, value: any, ttl?: number): Promise<void> {
310
+ const startTime = Date.now();
311
+ await this.cache.set(key, value, ttl);
312
+ const latency = Date.now() - startTime;
313
+
314
+ this.analytics.recordLatency('set', latency);
315
+ }
316
+
317
+ async delete(key: string | string[]): Promise<void> {
318
+ const startTime = Date.now();
319
+ await this.cache.delete(key);
320
+ const latency = Date.now() - startTime;
321
+
322
+ this.analytics.recordLatency('delete', latency);
323
+ }
324
+
325
+ async clear(): Promise<void> {
326
+ await this.cache.clear();
327
+ this.analytics.reset();
328
+ }
329
+
330
+ async getMany<T>(keys: string[]): Promise<(T | null)[]> {
331
+ const startTime = Date.now();
332
+ const results = await this.cache.getMany<T>(keys);
333
+ const latency = Date.now() - startTime;
334
+
335
+ // Count hits and misses
336
+ let hits = 0;
337
+ let misses = 0;
338
+
339
+ for (const result of results) {
340
+ if (result !== null) {
341
+ hits++;
342
+ } else {
343
+ misses++;
344
+ }
345
+ }
346
+
347
+ // Record metrics (approximate - we don't know individual latencies)
348
+ for (let i = 0; i < hits; i++) {
349
+ this.analytics.recordHit('get', latency / keys.length);
350
+ }
351
+ for (let i = 0; i < misses; i++) {
352
+ this.analytics.recordMiss('get', latency / keys.length);
353
+ }
354
+
355
+ return results;
356
+ }
357
+
358
+ async setMany<T>(entries: Array<{key: string, value: T, ttl?: number}>): Promise<void> {
359
+ const startTime = Date.now();
360
+ await this.cache.setMany(entries);
361
+ const latency = Date.now() - startTime;
362
+
363
+ this.analytics.recordLatency('set', latency);
364
+ }
365
+
366
+ async deleteMany(keys: string[]): Promise<void> {
367
+ const startTime = Date.now();
368
+ await this.cache.deleteMany(keys);
369
+ const latency = Date.now() - startTime;
370
+
371
+ this.analytics.recordLatency('delete', latency);
372
+ }
373
+
374
+ async invalidatePattern(pattern: string): Promise<void> {
375
+ await this.cache.invalidatePattern(pattern);
376
+ }
377
+
378
+ async ping(): Promise<boolean> {
379
+ return await this.cache.ping();
380
+ }
381
+
382
+ async getStats(): Promise<CacheStats> {
383
+ return await this.cache.getStats();
384
+ }
385
+
386
+ /**
387
+ * Gets the analytics instance
388
+ */
389
+ getAnalytics(): CacheAnalytics {
390
+ return this.analytics;
391
+ }
392
+
393
+ /**
394
+ * Gets current performance report
395
+ */
396
+ getReport(): CacheReport {
397
+ return this.analytics.getReport();
398
+ }
399
+ }
@@ -0,0 +1,145 @@
1
+ import { type CacheProvider } from './CacheProvider';
2
+ import { MemoryCache, type MemoryCacheConfig } from './MemoryCache';
3
+ import { NoOpCache } from './NoOpCache';
4
+ import { RedisCache, type RedisCacheConfig } from './RedisCache';
5
+ import { MultiLevelCache } from './MultiLevelCache';
6
+ import { type CacheConfig } from '../../config/cache.config';
7
+ import { logger } from '../Logger';
8
+
9
+ /**
10
+ * Factory for creating cache provider instances based on configuration
11
+ */
12
+ export class CacheFactory {
13
+ /**
14
+ * Create a cache provider instance based on the configuration
15
+ */
16
+ public static create(config: CacheConfig): CacheProvider {
17
+ if (!config.enabled) {
18
+ logger.debug('Cache disabled, using NoOpCache');
19
+ return new NoOpCache();
20
+ }
21
+
22
+ switch (config.provider) {
23
+ case 'memory':
24
+ return this.createMemoryCache(config);
25
+ case 'redis':
26
+ return this.createRedisCache(config);
27
+ case 'multilevel':
28
+ return this.createMultiLevelCache(config);
29
+ case 'noop':
30
+ return new NoOpCache();
31
+ default:
32
+ logger.warn(`Unknown cache provider '${config.provider}', falling back to MemoryCache`);
33
+ return this.createMemoryCache(config);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Create a MemoryCache instance with the given configuration
39
+ */
40
+ private static createMemoryCache(config: CacheConfig): MemoryCache {
41
+ const memoryConfig: MemoryCacheConfig = {
42
+ maxSize: config.query?.maxSize ?? 10000,
43
+ maxMemory: config.maxMemory,
44
+ defaultTTL: config.defaultTTL,
45
+ cleanupInterval: 60000 // 1 minute cleanup interval
46
+ };
47
+
48
+ logger.debug({ msg: 'Creating MemoryCache', config: memoryConfig });
49
+ return new MemoryCache(memoryConfig);
50
+ }
51
+
52
+ /**
53
+ * Create a RedisCache instance with the given configuration
54
+ */
55
+ private static createRedisCache(config: CacheConfig): RedisCache {
56
+ if (!config.redis) {
57
+ throw new Error('Redis configuration is required for Redis cache provider');
58
+ }
59
+
60
+ const redisConfig: RedisCacheConfig = {
61
+ host: config.redis.host,
62
+ port: config.redis.port,
63
+ password: config.redis.password,
64
+ db: config.redis.db,
65
+ keyPrefix: config.redis.keyPrefix,
66
+ retryStrategy: config.redis.retryStrategy,
67
+ maxRetriesPerRequest: 3,
68
+ lazyConnect: false,
69
+ enableReadyCheck: true
70
+ };
71
+
72
+ const { password: _pw, ...safeConfig } = redisConfig;
73
+ logger.debug({ msg: 'Creating RedisCache', config: safeConfig });
74
+ return new RedisCache(redisConfig);
75
+ }
76
+
77
+ /**
78
+ * Create a MultiLevelCache instance with the given configuration
79
+ */
80
+ private static createMultiLevelCache(config: CacheConfig): MultiLevelCache {
81
+ // Create L1 (Memory) cache
82
+ const l1Cache = this.createMemoryCache(config);
83
+
84
+ // Create L2 (Redis) cache if Redis config is available
85
+ let l2Cache: RedisCache | null = null;
86
+ if (config.redis) {
87
+ l2Cache = this.createRedisCache(config);
88
+ } else {
89
+ logger.warn('MultiLevel cache requested but no Redis config provided, using Memory-only cache');
90
+ }
91
+
92
+ logger.debug({ msg: 'Creating MultiLevelCache', hasL2: !!l2Cache });
93
+ return new MultiLevelCache(l1Cache, l2Cache, config);
94
+ }
95
+
96
+ /**
97
+ * Validate cache configuration
98
+ */
99
+ public static validateConfig(config: CacheConfig): { valid: boolean; errors: string[] } {
100
+ const errors: string[] = [];
101
+
102
+ if (config.defaultTTL < 0) {
103
+ errors.push('defaultTTL must be non-negative');
104
+ }
105
+
106
+ if (config.maxMemory && config.maxMemory < 0) {
107
+ errors.push('maxMemory must be non-negative');
108
+ }
109
+
110
+ if (config.entity?.ttl && config.entity.ttl < 0) {
111
+ errors.push('entity.ttl must be non-negative');
112
+ }
113
+
114
+ if (config.component?.ttl && config.component.ttl < 0) {
115
+ errors.push('component.ttl must be non-negative');
116
+ }
117
+
118
+ if (config.query?.ttl && config.query.ttl < 0) {
119
+ errors.push('query.ttl must be non-negative');
120
+ }
121
+
122
+ if (config.query?.maxSize && config.query.maxSize < 0) {
123
+ errors.push('query.maxSize must be non-negative');
124
+ }
125
+
126
+ if (config.redis) {
127
+ if (config.redis.port < 1 || config.redis.port > 65535) {
128
+ errors.push('redis.port must be between 1 and 65535');
129
+ }
130
+
131
+ if (config.redis.db && (config.redis.db < 0 || config.redis.db > 15)) {
132
+ errors.push('redis.db must be between 0 and 15');
133
+ }
134
+
135
+ if (config.provider === 'redis' && !config.redis.host) {
136
+ errors.push('redis.host is required when using redis provider');
137
+ }
138
+ }
139
+
140
+ return {
141
+ valid: errors.length === 0,
142
+ errors
143
+ };
144
+ }
145
+ }