@umituz/react-native-storage 2.5.0 → 2.6.1

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 (28) hide show
  1. package/package.json +7 -3
  2. package/src/cache/__tests__/PerformanceAndMemory.test.ts +386 -0
  3. package/src/cache/__tests__/setup.ts +19 -0
  4. package/src/cache/domain/Cache.ts +146 -0
  5. package/src/cache/domain/CacheManager.ts +48 -0
  6. package/src/cache/domain/CacheStatsTracker.ts +49 -0
  7. package/src/cache/domain/ErrorHandler.ts +42 -0
  8. package/src/cache/domain/PatternMatcher.ts +30 -0
  9. package/src/cache/domain/__tests__/Cache.test.ts +292 -0
  10. package/src/cache/domain/__tests__/CacheManager.test.ts +276 -0
  11. package/src/cache/domain/__tests__/ErrorHandler.test.ts +303 -0
  12. package/src/cache/domain/__tests__/PatternMatcher.test.ts +261 -0
  13. package/src/cache/domain/strategies/EvictionStrategy.ts +9 -0
  14. package/src/cache/domain/strategies/FIFOStrategy.ts +12 -0
  15. package/src/cache/domain/strategies/LFUStrategy.ts +22 -0
  16. package/src/cache/domain/strategies/LRUStrategy.ts +22 -0
  17. package/src/cache/domain/strategies/TTLStrategy.ts +23 -0
  18. package/src/cache/domain/strategies/__tests__/EvictionStrategies.test.ts +293 -0
  19. package/src/cache/domain/types/Cache.ts +28 -0
  20. package/src/cache/index.ts +28 -0
  21. package/src/cache/infrastructure/TTLCache.ts +103 -0
  22. package/src/cache/infrastructure/__tests__/TTLCache.test.ts +303 -0
  23. package/src/cache/presentation/__tests__/ReactHooks.test.ts +512 -0
  24. package/src/cache/presentation/useCache.ts +76 -0
  25. package/src/cache/presentation/useCachedValue.ts +88 -0
  26. package/src/cache/types.d.ts +3 -0
  27. package/src/index.ts +28 -0
  28. package/src/types/global.d.ts +2 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@umituz/react-native-storage",
3
- "version": "2.5.0",
4
- "description": "Zustand state management with AsyncStorage persistence and type-safe storage operations for React Native",
3
+ "version": "2.6.1",
4
+ "description": "Unified storage solution with AsyncStorage persistence, Zustand state management, and in-memory caching for React Native",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
7
7
  "scripts": {
@@ -17,7 +17,11 @@
17
17
  "async-storage",
18
18
  "zustand",
19
19
  "persistence",
20
- "secure-storage"
20
+ "secure-storage",
21
+ "cache",
22
+ "in-memory",
23
+ "ttl",
24
+ "lru"
21
25
  ],
22
26
  "author": "Ümit UZ <umit@umituz.com>",
23
27
  "license": "MIT",
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Performance and Memory Leak Tests
3
+ */
4
+
5
+ import { Cache } from '../Cache';
6
+ import { TTLCache } from '../TTLCache';
7
+ import { cacheManager } from '../CacheManager';
8
+ import { PatternMatcher } from '../PatternMatcher';
9
+ import { renderHook, act } from '@testing-library/react';
10
+ import { useCache } from '../../presentation/useCache';
11
+
12
+ describe('Performance and Memory Leak Tests', () => {
13
+ describe('Cache Performance', () => {
14
+ test('should handle large number of entries efficiently', () => {
15
+ const cache = new Cache<string>({ maxSize: 10000 });
16
+ const startTime = performance.now();
17
+
18
+ // Add 10,000 entries
19
+ for (let i = 0; i < 10000; i++) {
20
+ cache.set(`key${i}`, `value${i}`);
21
+ }
22
+
23
+ const insertTime = performance.now() - startTime;
24
+
25
+ // Test retrieval performance
26
+ const retrieveStart = performance.now();
27
+ for (let i = 0; i < 10000; i++) {
28
+ cache.get(`key${i}`);
29
+ }
30
+ const retrieveTime = performance.now() - retrieveStart;
31
+
32
+ expect(cache.getStats().size).toBe(10000);
33
+ expect(insertTime).toBeLessThan(1000); // 1 second for 10k inserts
34
+ expect(retrieveTime).toBeLessThan(500); // 0.5 second for 10k retrievals
35
+ });
36
+
37
+ test('should handle rapid eviction without performance degradation', () => {
38
+ const cache = new Cache<string>({ maxSize: 100 });
39
+ const startTime = performance.now();
40
+
41
+ // Add and rapidly evict entries
42
+ for (let i = 0; i < 1000; i++) {
43
+ cache.set(`key${i}`, `value${i}`);
44
+ }
45
+
46
+ const endTime = performance.now();
47
+ const duration = endTime - startTime;
48
+
49
+ expect(cache.getStats().size).toBe(100); // Should maintain max size
50
+ expect(cache.getStats().evictions).toBeGreaterThan(800); // Many evictions
51
+ expect(duration).toBeLessThan(1000); // Should complete quickly
52
+ });
53
+
54
+ test('should handle pattern invalidation efficiently', () => {
55
+ const cache = new Cache<string>();
56
+
57
+ // Add entries with different patterns
58
+ for (let i = 0; i < 1000; i++) {
59
+ cache.set(`user:${i}:profile`, `profile${i}`);
60
+ cache.set(`user:${i}:settings`, `settings${i}`);
61
+ cache.set(`post:${i}`, `post${i}`);
62
+ }
63
+
64
+ const startTime = performance.now();
65
+ const invalidatedCount = cache.invalidatePattern('user:*:profile');
66
+ const endTime = performance.now();
67
+
68
+ const duration = endTime - startTime;
69
+
70
+ expect(invalidatedCount).toBe(1000);
71
+ expect(duration).toBeLessThan(100); // Should be very fast
72
+ expect(cache.getStats().size).toBe(2000); // posts + settings remain
73
+ });
74
+ });
75
+
76
+ describe('Memory Management', () => {
77
+ test('should not memory leak with cache destruction', () => {
78
+ const caches: TTLCache<string>[] = [];
79
+
80
+ // Create many caches
81
+ for (let i = 0; i < 100; i++) {
82
+ const cache = new TTLCache<string>({ cleanupIntervalMs: 100 });
83
+ cache.set(`key${i}`, `value${i}`);
84
+ caches.push(cache);
85
+ }
86
+
87
+ // Destroy all caches
88
+ const startTime = performance.now();
89
+ caches.forEach(cache => cache.destroy());
90
+ const endTime = performance.now();
91
+
92
+ const duration = endTime - startTime;
93
+
94
+ expect(duration).toBeLessThan(1000); // Should destroy quickly
95
+
96
+ // Operations on destroyed caches should be safe
97
+ caches.forEach(cache => {
98
+ expect(() => cache.set('test', 'value')).not.toThrow();
99
+ expect(cache.get('test')).toBeUndefined();
100
+ });
101
+ });
102
+
103
+ test('should handle cache manager memory efficiently', () => {
104
+ const cacheNames: string[] = [];
105
+
106
+ // Create many caches through manager
107
+ for (let i = 0; i < 1000; i++) {
108
+ const name = `cache-${i}`;
109
+ cacheNames.push(name);
110
+ const cache = cacheManager.getCache<string>(name);
111
+ cache.set(`key${i}`, `value${i}`);
112
+ }
113
+
114
+ expect(cacheManager.getCacheNames()).toHaveLength(1000);
115
+
116
+ // Delete all caches
117
+ const startTime = performance.now();
118
+ cacheNames.forEach(name => cacheManager.deleteCache(name));
119
+ const endTime = performance.now();
120
+
121
+ const duration = endTime - startTime;
122
+
123
+ expect(duration).toBeLessThan(1000);
124
+ expect(cacheManager.getCacheNames()).toHaveLength(0);
125
+ });
126
+
127
+ test('should cleanup pattern matcher cache', () => {
128
+ // Create many unique patterns to fill cache
129
+ for (let i = 0; i < 1000; i++) {
130
+ PatternMatcher.convertPatternToRegex(`pattern-${i}-*`);
131
+ }
132
+
133
+ // Clear cache and verify memory is freed
134
+ PatternMatcher.clearCache();
135
+
136
+ // Should still work after clear
137
+ expect(PatternMatcher.matchesPattern('test-key', 'test-*')).toBe(true);
138
+ });
139
+ });
140
+
141
+ describe('React Hooks Performance', () => {
142
+ test('should handle many hook instances without memory leaks', () => {
143
+ const hooks: Array<ReturnType<typeof useCache<string>>> = [];
144
+
145
+ // Create many hook instances
146
+ for (let i = 0; i < 100; i++) {
147
+ const { result } = renderHook(() => useCache<string>(`test-cache-${i}`));
148
+ hooks.push(result.current);
149
+ }
150
+
151
+ // Perform operations on all hooks
152
+ const startTime = performance.now();
153
+ hooks.forEach((hook, index) => {
154
+ act(() => {
155
+ hook.set(`key${index}`, `value${index}`);
156
+ });
157
+ });
158
+ const endTime = performance.now();
159
+
160
+ const duration = endTime - startTime;
161
+
162
+ expect(duration).toBeLessThan(2000); // Should complete within 2 seconds
163
+
164
+ // Verify all operations worked
165
+ hooks.forEach((hook, index) => {
166
+ expect(hook.get(`key${index}`)).toBe(`value${index}`);
167
+ });
168
+
169
+ // Cleanup all hooks
170
+ hooks.forEach(() => {
171
+ // Hooks will be automatically cleaned up when unmounted
172
+ });
173
+ });
174
+
175
+ test('should handle rapid hook re-renders efficiently', () => {
176
+ const { result, rerender } = renderHook(() => useCache<string>('rapid-cache'));
177
+
178
+ const startTime = performance.now();
179
+
180
+ // Perform many rapid operations
181
+ for (let i = 0; i < 1000; i++) {
182
+ act(() => {
183
+ result.current.set(`key${i}`, `value${i}`);
184
+ });
185
+ }
186
+
187
+ const endTime = performance.now();
188
+ const duration = endTime - startTime;
189
+
190
+ expect(duration).toBeLessThan(3000); // Should complete within 3 seconds
191
+ expect(result.current.getStats().size).toBeLessThanOrEqual(100); // Limited by eviction
192
+ });
193
+ });
194
+
195
+ describe('Stress Tests', () => {
196
+ test('should handle concurrent operations safely', async () => {
197
+ const cache = new Cache<string>({ maxSize: 1000 });
198
+ const promises: Promise<void>[] = [];
199
+
200
+ // Create concurrent operations
201
+ for (let i = 0; i < 100; i++) {
202
+ promises.push(
203
+ new Promise<void>((resolve) => {
204
+ setTimeout(() => {
205
+ for (let j = 0; j < 10; j++) {
206
+ const key = `concurrent-${i}-${j}`;
207
+ const value = `value-${i}-${j}`;
208
+ cache.set(key, value);
209
+ cache.get(key);
210
+ }
211
+ resolve();
212
+ }, Math.random() * 100);
213
+ })
214
+ );
215
+ }
216
+
217
+ // Wait for all operations to complete
218
+ await Promise.all(promises);
219
+
220
+ // Verify cache is in consistent state
221
+ const stats = cache.getStats();
222
+ expect(stats.size).toBeLessThanOrEqual(1000);
223
+ expect(stats.hits + stats.misses).toBeGreaterThan(0);
224
+ });
225
+
226
+ test('should handle TTL cache under stress', async () => {
227
+ jest.useFakeTimers();
228
+
229
+ const cache = new TTLCache<string>({
230
+ maxSize: 500,
231
+ defaultTTL: 100,
232
+ cleanupIntervalMs: 50
233
+ });
234
+
235
+ // Add many entries with short TTL
236
+ for (let i = 0; i < 1000; i++) {
237
+ cache.set(`stress-key${i}`, `stress-value${i}`);
238
+ }
239
+
240
+ // Advance time to trigger multiple cleanup cycles
241
+ for (let i = 0; i < 10; i++) {
242
+ jest.advanceTimersByTime(50);
243
+ await new Promise(resolve => setTimeout(resolve, 0));
244
+ }
245
+
246
+ // Cache should handle stress without errors
247
+ expect(() => cache.get('any-key')).not.toThrow();
248
+
249
+ const stats = cache.getStats();
250
+ expect(stats.expirations).toBeGreaterThan(0);
251
+
252
+ cache.destroy();
253
+ jest.useRealTimers();
254
+ });
255
+
256
+ test('should handle pattern matching stress test', () => {
257
+ const patterns: string[] = [];
258
+ const keys: string[] = [];
259
+
260
+ // Generate many patterns and keys
261
+ for (let i = 0; i < 1000; i++) {
262
+ patterns.push(`pattern-${i}-*`);
263
+ keys.push(`pattern-${i}-value`);
264
+ }
265
+
266
+ const startTime = performance.now();
267
+
268
+ // Test all pattern matches
269
+ patterns.forEach((pattern, index) => {
270
+ PatternMatcher.matchesPattern(keys[index], pattern);
271
+ });
272
+
273
+ const endTime = performance.now();
274
+ const duration = endTime - startTime;
275
+
276
+ expect(duration).toBeLessThan(100); // Should be very fast with caching
277
+ });
278
+ });
279
+
280
+ describe('Memory Leak Detection', () => {
281
+ test('should not leak memory with repeated cache operations', () => {
282
+ const cache = new Cache<string>();
283
+ const initialMemory = process.memoryUsage().heapUsed;
284
+
285
+ // Perform many operations
286
+ for (let cycle = 0; cycle < 100; cycle++) {
287
+ // Add many entries
288
+ for (let i = 0; i < 100; i++) {
289
+ cache.set(`cycle-${cycle}-key-${i}`, `value-${i}`);
290
+ }
291
+
292
+ // Clear cache
293
+ cache.clear();
294
+
295
+ // Force garbage collection if available
296
+ if (global.gc) {
297
+ global.gc();
298
+ }
299
+ }
300
+
301
+ const finalMemory = process.memoryUsage().heapUsed;
302
+ const memoryIncrease = finalMemory - initialMemory;
303
+
304
+ // Memory increase should be minimal (allowing for some variance)
305
+ expect(memoryIncrease).toBeLessThan(10 * 1024 * 1024); // Less than 10MB
306
+ });
307
+
308
+ test('should not leak memory with repeated hook mount/unmount', () => {
309
+ const initialMemory = process.memoryUsage().heapUsed;
310
+
311
+ // Mount and unmount hooks repeatedly
312
+ for (let i = 0; i < 100; i++) {
313
+ const { unmount } = renderHook(() => useCache<string>(`test-cache-${i}`));
314
+
315
+ act(() => {
316
+ // Perform some operations
317
+ });
318
+
319
+ unmount();
320
+
321
+ // Force garbage collection if available
322
+ if (global.gc) {
323
+ global.gc();
324
+ }
325
+ }
326
+
327
+ const finalMemory = process.memoryUsage().heapUsed;
328
+ const memoryIncrease = finalMemory - initialMemory;
329
+
330
+ // Memory increase should be minimal
331
+ expect(memoryIncrease).toBeLessThan(5 * 1024 * 1024); // Less than 5MB
332
+ });
333
+ });
334
+
335
+ describe('Performance Regression Tests', () => {
336
+ test('should maintain performance with large cache sizes', () => {
337
+ const sizes = [100, 1000, 5000, 10000];
338
+
339
+ sizes.forEach(size => {
340
+ const cache = new Cache<string>({ maxSize: size });
341
+ const startTime = performance.now();
342
+
343
+ // Fill cache
344
+ for (let i = 0; i < size; i++) {
345
+ cache.set(`key${i}`, `value${i}`);
346
+ }
347
+
348
+ // Random access pattern
349
+ for (let i = 0; i < size; i++) {
350
+ const randomIndex = Math.floor(Math.random() * size);
351
+ cache.get(`key${randomIndex}`);
352
+ }
353
+
354
+ const endTime = performance.now();
355
+ const duration = endTime - startTime;
356
+
357
+ // Performance should scale reasonably
358
+ const opsPerMs = (size * 2) / duration; // inserts + gets
359
+ expect(opsPerMs).toBeGreaterThan(10); // At least 10 ops per ms
360
+ });
361
+ });
362
+
363
+ test('should maintain pattern matching performance', () => {
364
+ const patternComplexities = [
365
+ 'simple:*',
366
+ 'complex:*:pattern:*:here',
367
+ 'very:complex:pattern:*:with:many:parts:*:and:sections',
368
+ ];
369
+
370
+ patternComplexities.forEach(pattern => {
371
+ const startTime = performance.now();
372
+
373
+ // Test many matches
374
+ for (let i = 0; i < 1000; i++) {
375
+ PatternMatcher.matchesPattern(`test:${i}:value`, pattern);
376
+ }
377
+
378
+ const endTime = performance.now();
379
+ const duration = endTime - startTime;
380
+
381
+ // Even complex patterns should be fast
382
+ expect(duration).toBeLessThan(50); // Less than 50ms for 1000 matches
383
+ });
384
+ });
385
+ });
386
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Test Setup
3
+ */
4
+
5
+ // Mock console methods in test environment
6
+ (global as any).console = {
7
+ ...console,
8
+ log: jest.fn(),
9
+ warn: jest.fn(),
10
+ error: jest.fn(),
11
+ };
12
+
13
+ // Mock __DEV__ for testing
14
+ (global as any).__DEV__ = true;
15
+
16
+ // Mock timers globally
17
+ jest.useFakeTimers({
18
+ doNotFake: ['nextTick', 'setImmediate']
19
+ });
@@ -0,0 +1,146 @@
1
+ /**
2
+ * In-Memory Cache
3
+ */
4
+
5
+ import type { CacheEntry, CacheConfig, CacheStats, EvictionStrategy } from './types/Cache';
6
+ import { CacheStatsTracker } from './CacheStatsTracker';
7
+ import { PatternMatcher } from './PatternMatcher';
8
+ import { LRUStrategy } from './strategies/LRUStrategy';
9
+ import { LFUStrategy } from './strategies/LFUStrategy';
10
+ import { FIFOStrategy } from './strategies/FIFOStrategy';
11
+ import { TTLStrategy } from './strategies/TTLStrategy';
12
+
13
+ export class Cache<T = unknown> {
14
+ private store = new Map<string, CacheEntry<T>>();
15
+ private config: Required<CacheConfig>;
16
+ private statsTracker = new CacheStatsTracker();
17
+ private strategies = {
18
+ lru: new LRUStrategy<T>(),
19
+ lfu: new LFUStrategy<T>(),
20
+ fifo: new FIFOStrategy<T>(),
21
+ ttl: new TTLStrategy<T>(),
22
+ };
23
+
24
+ constructor(config: CacheConfig = {}) {
25
+ this.config = {
26
+ maxSize: config.maxSize || 100,
27
+ defaultTTL: config.defaultTTL || 5 * 60 * 1000,
28
+ onEvict: config.onEvict || (() => { }),
29
+ onExpire: config.onExpire || (() => { }),
30
+ };
31
+ }
32
+
33
+ set(key: string, value: T, ttl?: number): void {
34
+ if (this.store.size >= this.config.maxSize && !this.store.has(key)) {
35
+ this.evictOne('lru');
36
+ }
37
+
38
+ const entry: CacheEntry<T> = {
39
+ value,
40
+ timestamp: Date.now(),
41
+ ttl: ttl || this.config.defaultTTL,
42
+ accessCount: 0,
43
+ lastAccess: Date.now(),
44
+ };
45
+
46
+ this.store.set(key, entry);
47
+ this.statsTracker.updateSize(this.store.size);
48
+
49
+ if (typeof __DEV__ !== 'undefined' && __DEV__ && typeof console !== 'undefined' && console.log) {
50
+ console.log(`Cache: Set key "${key}" with TTL ${entry.ttl}ms`);
51
+ }
52
+ }
53
+
54
+ get(key: string): T | undefined {
55
+ const entry = this.store.get(key);
56
+
57
+ if (!entry) {
58
+ this.statsTracker.recordMiss();
59
+ return undefined;
60
+ }
61
+
62
+ if (this.isExpired(entry)) {
63
+ this.delete(key);
64
+ this.statsTracker.recordMiss();
65
+ this.statsTracker.recordExpiration();
66
+ this.config.onExpire(key, entry);
67
+ return undefined;
68
+ }
69
+
70
+ entry.accessCount++;
71
+ entry.lastAccess = Date.now();
72
+ this.statsTracker.recordHit();
73
+ return entry.value;
74
+ }
75
+
76
+ has(key: string): boolean {
77
+ const entry = this.store.get(key);
78
+ if (!entry) return false;
79
+ if (this.isExpired(entry)) {
80
+ this.delete(key);
81
+ return false;
82
+ }
83
+ return true;
84
+ }
85
+
86
+ delete(key: string): boolean {
87
+ const deleted = this.store.delete(key);
88
+ if (deleted) {
89
+ this.statsTracker.updateSize(this.store.size);
90
+ }
91
+ return deleted;
92
+ }
93
+
94
+ invalidatePattern(pattern: string): number {
95
+ const regex = PatternMatcher.convertPatternToRegex(pattern);
96
+ let invalidatedCount = 0;
97
+
98
+ for (const key of this.store.keys()) {
99
+ if (regex.test(key)) {
100
+ this.store.delete(key);
101
+ invalidatedCount++;
102
+ }
103
+ }
104
+
105
+ this.statsTracker.updateSize(this.store.size);
106
+ return invalidatedCount;
107
+ }
108
+
109
+ clear(): void {
110
+ this.store.clear();
111
+ this.statsTracker.reset();
112
+ }
113
+
114
+ getStats(): CacheStats {
115
+ return this.statsTracker.getStats();
116
+ }
117
+
118
+ keys(): string[] {
119
+ return Array.from(this.store.keys());
120
+ }
121
+
122
+ private isExpired(entry: CacheEntry<T>): boolean {
123
+ return Date.now() - entry.timestamp > entry.ttl;
124
+ }
125
+
126
+ private evictOne(strategy: EvictionStrategy): void {
127
+ const evictionStrategy = this.strategies[strategy];
128
+ if (!evictionStrategy) return;
129
+
130
+ const keyToEvict = evictionStrategy.findKeyToEvict(this.store);
131
+ if (keyToEvict) {
132
+ const entry = this.store.get(keyToEvict);
133
+ this.store.delete(keyToEvict);
134
+ this.statsTracker.recordEviction();
135
+ this.statsTracker.updateSize(this.store.size);
136
+
137
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
138
+ console.log(`Cache: Evicted key "${keyToEvict}" using ${strategy} strategy`);
139
+ }
140
+
141
+ if (entry) {
142
+ this.config.onEvict(keyToEvict, entry);
143
+ }
144
+ }
145
+ }
146
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Cache Manager
3
+ * Manages multiple cache instances
4
+ */
5
+
6
+ import { Cache } from './Cache';
7
+ import type { CacheConfig } from './types/Cache';
8
+
9
+ export class CacheManager {
10
+ private static instance: CacheManager;
11
+ private caches = new Map<string, Cache<any>>();
12
+
13
+ private constructor() {}
14
+
15
+ static getInstance(): CacheManager {
16
+ if (!CacheManager.instance) {
17
+ CacheManager.instance = new CacheManager();
18
+ }
19
+ return CacheManager.instance;
20
+ }
21
+
22
+ getCache<T>(name: string, config?: CacheConfig): Cache<T> {
23
+ if (!this.caches.has(name)) {
24
+ this.caches.set(name, new Cache<T>(config));
25
+ }
26
+ return this.caches.get(name)!;
27
+ }
28
+
29
+ deleteCache(name: string): boolean {
30
+ const cache = this.caches.get(name);
31
+ if (cache) {
32
+ cache.clear();
33
+ return this.caches.delete(name);
34
+ }
35
+ return false;
36
+ }
37
+
38
+ clearAll(): void {
39
+ this.caches.forEach((cache) => cache.clear());
40
+ this.caches.clear();
41
+ }
42
+
43
+ getCacheNames(): string[] {
44
+ return Array.from(this.caches.keys());
45
+ }
46
+ }
47
+
48
+ export const cacheManager = CacheManager.getInstance();
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Cache Statistics Tracker
3
+ */
4
+
5
+ import type { CacheStats } from './types/Cache';
6
+
7
+ export class CacheStatsTracker {
8
+ private stats: CacheStats = {
9
+ size: 0,
10
+ hits: 0,
11
+ misses: 0,
12
+ evictions: 0,
13
+ expirations: 0,
14
+ };
15
+
16
+ recordHit(): void {
17
+ this.stats.hits++;
18
+ }
19
+
20
+ recordMiss(): void {
21
+ this.stats.misses++;
22
+ }
23
+
24
+ recordEviction(): void {
25
+ this.stats.evictions++;
26
+ }
27
+
28
+ recordExpiration(): void {
29
+ this.stats.expirations++;
30
+ }
31
+
32
+ updateSize(size: number): void {
33
+ this.stats.size = size;
34
+ }
35
+
36
+ getStats(): CacheStats {
37
+ return { ...this.stats };
38
+ }
39
+
40
+ reset(): void {
41
+ this.stats = {
42
+ size: 0,
43
+ hits: 0,
44
+ misses: 0,
45
+ evictions: 0,
46
+ expirations: 0,
47
+ };
48
+ }
49
+ }