@umituz/react-native-design-system 2.8.7 → 2.8.8
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/package.json +5 -6
- package/src/device/infrastructure/repositories/LegacyDeviceIdRepository.ts +1 -1
- package/src/device/infrastructure/services/DeviceFeatureService.ts +1 -1
- package/src/exception/infrastructure/services/ExceptionLogger.ts +1 -1
- package/src/exception/infrastructure/storage/ExceptionStore.ts +1 -1
- package/src/exports/filesystem.ts +1 -0
- package/src/exports/storage.ts +1 -0
- package/src/filesystem/domain/constants/FileConstants.ts +20 -0
- package/src/filesystem/domain/entities/File.ts +20 -0
- package/src/filesystem/domain/types/FileTypes.ts +43 -0
- package/src/filesystem/domain/utils/FileUtils.ts +86 -0
- package/src/filesystem/index.ts +23 -0
- package/src/filesystem/infrastructure/services/FileSystemService.ts +45 -0
- package/src/filesystem/infrastructure/services/cache.service.ts +48 -0
- package/src/filesystem/infrastructure/services/directory.service.ts +66 -0
- package/src/filesystem/infrastructure/services/download.constants.ts +6 -0
- package/src/filesystem/infrastructure/services/download.service.ts +74 -0
- package/src/filesystem/infrastructure/services/download.types.ts +7 -0
- package/src/filesystem/infrastructure/services/encoding.service.ts +25 -0
- package/src/filesystem/infrastructure/services/file-info.service.ts +52 -0
- package/src/filesystem/infrastructure/services/file-manager.service.ts +81 -0
- package/src/filesystem/infrastructure/services/file-path.service.ts +22 -0
- package/src/filesystem/infrastructure/services/file-reader.service.ts +52 -0
- package/src/filesystem/infrastructure/services/file-writer.service.ts +32 -0
- package/src/filesystem/infrastructure/utils/blob.utils.ts +20 -0
- package/src/image/infrastructure/services/ImageStorageService.ts +1 -1
- package/src/index.ts +9 -0
- package/src/molecules/alerts/AlertStore.ts +1 -1
- package/src/molecules/calendar/infrastructure/storage/EventActions.ts +1 -1
- package/src/molecules/calendar/infrastructure/stores/storageAdapter.ts +1 -1
- package/src/offline/infrastructure/storage/OfflineStore.ts +1 -1
- package/src/onboarding/infrastructure/storage/OnboardingStore.ts +2 -2
- package/src/onboarding/infrastructure/storage/__tests__/OnboardingStore.test.ts +1 -1
- package/src/onboarding/infrastructure/storage/actions/answerActions.ts +1 -1
- package/src/onboarding/infrastructure/storage/actions/storageHelpers.ts +1 -1
- package/src/storage/README.md +185 -0
- package/src/storage/__tests__/integration.test.ts +391 -0
- package/src/storage/__tests__/mocks/asyncStorage.mock.ts +52 -0
- package/src/storage/__tests__/performance.test.tsx +352 -0
- package/src/storage/__tests__/setup.ts +63 -0
- package/src/storage/application/README.md +158 -0
- package/src/storage/application/ports/IStorageRepository.ts +61 -0
- package/src/storage/application/ports/README.md +127 -0
- package/src/storage/cache/README.md +154 -0
- package/src/storage/cache/__tests__/PerformanceAndMemory.test.ts +387 -0
- package/src/storage/cache/__tests__/setup.ts +19 -0
- package/src/storage/cache/domain/Cache.ts +146 -0
- package/src/storage/cache/domain/CacheManager.md +83 -0
- package/src/storage/cache/domain/CacheManager.ts +48 -0
- package/src/storage/cache/domain/CacheStatsTracker.md +169 -0
- package/src/storage/cache/domain/CacheStatsTracker.ts +49 -0
- package/src/storage/cache/domain/CachedValue.md +97 -0
- package/src/storage/cache/domain/ErrorHandler.md +99 -0
- package/src/storage/cache/domain/ErrorHandler.ts +42 -0
- package/src/storage/cache/domain/PatternMatcher.md +122 -0
- package/src/storage/cache/domain/PatternMatcher.ts +30 -0
- package/src/storage/cache/domain/README.md +118 -0
- package/src/storage/cache/domain/__tests__/Cache.test.ts +293 -0
- package/src/storage/cache/domain/__tests__/CacheManager.test.ts +276 -0
- package/src/storage/cache/domain/__tests__/ErrorHandler.test.ts +303 -0
- package/src/storage/cache/domain/__tests__/PatternMatcher.test.ts +261 -0
- package/src/storage/cache/domain/strategies/EvictionStrategy.ts +9 -0
- package/src/storage/cache/domain/strategies/FIFOStrategy.ts +12 -0
- package/src/storage/cache/domain/strategies/LFUStrategy.ts +22 -0
- package/src/storage/cache/domain/strategies/LRUStrategy.ts +22 -0
- package/src/storage/cache/domain/strategies/README.md +117 -0
- package/src/storage/cache/domain/strategies/TTLStrategy.ts +23 -0
- package/src/storage/cache/domain/strategies/__tests__/EvictionStrategies.test.ts +293 -0
- package/src/storage/cache/domain/types/Cache.ts +28 -0
- package/src/storage/cache/domain/types/README.md +107 -0
- package/src/storage/cache/index.ts +28 -0
- package/src/storage/cache/infrastructure/README.md +126 -0
- package/src/storage/cache/infrastructure/TTLCache.ts +103 -0
- package/src/storage/cache/infrastructure/__tests__/TTLCache.test.ts +303 -0
- package/src/storage/cache/presentation/README.md +123 -0
- package/src/storage/cache/presentation/__tests__/ReactHooks.test.ts +514 -0
- package/src/storage/cache/presentation/useCache.ts +76 -0
- package/src/storage/cache/presentation/useCachedValue.ts +88 -0
- package/src/storage/cache/types.d.ts +3 -0
- package/src/storage/domain/README.md +128 -0
- package/src/storage/domain/constants/CacheDefaults.ts +64 -0
- package/src/storage/domain/constants/README.md +105 -0
- package/src/storage/domain/entities/CachedValue.ts +86 -0
- package/src/storage/domain/entities/README.md +109 -0
- package/src/storage/domain/entities/StorageResult.ts +75 -0
- package/src/storage/domain/entities/__tests__/CachedValue.test.ts +149 -0
- package/src/storage/domain/entities/__tests__/StorageResult.test.ts +122 -0
- package/src/storage/domain/errors/README.md +126 -0
- package/src/storage/domain/errors/StorageError.ts +81 -0
- package/src/storage/domain/errors/__tests__/StorageError.test.ts +127 -0
- package/src/storage/domain/factories/README.md +138 -0
- package/src/storage/domain/factories/StoreFactory.ts +59 -0
- package/src/storage/domain/types/README.md +522 -0
- package/src/storage/domain/types/Store.ts +44 -0
- package/src/storage/domain/utils/CacheKeyGenerator.ts +66 -0
- package/src/storage/domain/utils/README.md +127 -0
- package/src/storage/domain/utils/__tests__/devUtils.test.ts +97 -0
- package/src/storage/domain/utils/devUtils.ts +37 -0
- package/src/storage/domain/value-objects/README.md +120 -0
- package/src/storage/domain/value-objects/StorageKey.ts +60 -0
- package/src/storage/index.ts +175 -0
- package/src/storage/infrastructure/README.md +165 -0
- package/src/storage/infrastructure/adapters/README.md +175 -0
- package/src/storage/infrastructure/adapters/StorageService.md +103 -0
- package/src/storage/infrastructure/adapters/StorageService.ts +49 -0
- package/src/storage/infrastructure/repositories/AsyncStorageRepository.ts +98 -0
- package/src/storage/infrastructure/repositories/BaseStorageOperations.ts +100 -0
- package/src/storage/infrastructure/repositories/BatchStorageOperations.ts +42 -0
- package/src/storage/infrastructure/repositories/README.md +121 -0
- package/src/storage/infrastructure/repositories/StringStorageOperations.ts +44 -0
- package/src/storage/infrastructure/repositories/__tests__/AsyncStorageRepository.test.ts +170 -0
- package/src/storage/infrastructure/repositories/__tests__/BaseStorageOperations.test.ts +201 -0
- package/src/storage/presentation/README.md +181 -0
- package/src/storage/presentation/hooks/CacheStorageOperations.ts +94 -0
- package/src/storage/presentation/hooks/README.md +128 -0
- package/src/storage/presentation/hooks/__tests__/usePersistentCache.test.ts +405 -0
- package/src/storage/presentation/hooks/__tests__/useStorage.test.ts +247 -0
- package/src/storage/presentation/hooks/__tests__/useStorageState.test.ts +293 -0
- package/src/storage/presentation/hooks/useCacheState.ts +53 -0
- package/src/storage/presentation/hooks/usePersistentCache.ts +154 -0
- package/src/storage/presentation/hooks/useStorage.ts +102 -0
- package/src/storage/presentation/hooks/useStorageState.ts +71 -0
- package/src/storage/presentation/hooks/useStore.ts +15 -0
- package/src/storage/types/README.md +103 -0
- package/src/theme/infrastructure/globalThemeStore.ts +1 -1
- package/src/theme/infrastructure/storage/ThemeStorage.ts +1 -1
- package/src/theme/infrastructure/stores/themeStore.ts +1 -1
- package/src/utilities/sharing/infrastructure/services/SharingService.ts +1 -1
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eviction Strategies Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { LRUStrategy } from '../LRUStrategy';
|
|
6
|
+
import { LFUStrategy } from '../LFUStrategy';
|
|
7
|
+
import { FIFOStrategy } from '../FIFOStrategy';
|
|
8
|
+
import { TTLStrategy } from '../TTLStrategy';
|
|
9
|
+
import type { CacheEntry } from '../../types/Cache';
|
|
10
|
+
|
|
11
|
+
describe('Eviction Strategies', () => {
|
|
12
|
+
const createMockEntries = (): Map<string, CacheEntry<string>> => {
|
|
13
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
|
|
16
|
+
entries.set('key1', {
|
|
17
|
+
value: 'value1',
|
|
18
|
+
timestamp: now - 1000,
|
|
19
|
+
ttl: 5000,
|
|
20
|
+
accessCount: 5,
|
|
21
|
+
lastAccess: now - 500,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
entries.set('key2', {
|
|
25
|
+
value: 'value2',
|
|
26
|
+
timestamp: now - 2000,
|
|
27
|
+
ttl: 5000,
|
|
28
|
+
accessCount: 3,
|
|
29
|
+
lastAccess: now - 100,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
entries.set('key3', {
|
|
33
|
+
value: 'value3',
|
|
34
|
+
timestamp: now - 3000,
|
|
35
|
+
ttl: 5000,
|
|
36
|
+
accessCount: 10,
|
|
37
|
+
lastAccess: now - 1500,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return entries;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
describe('LRUStrategy', () => {
|
|
44
|
+
let strategy: LRUStrategy<string>;
|
|
45
|
+
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
strategy = new LRUStrategy<string>();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('should evict least recently used key', () => {
|
|
51
|
+
const entries = createMockEntries();
|
|
52
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
53
|
+
|
|
54
|
+
// key3 has the oldest lastAccess time
|
|
55
|
+
expect(keyToEvict).toBe('key3');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('should return undefined for empty entries', () => {
|
|
59
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
60
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
61
|
+
|
|
62
|
+
expect(keyToEvict).toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('should handle single entry', () => {
|
|
66
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
67
|
+
entries.set('key1', {
|
|
68
|
+
value: 'value1',
|
|
69
|
+
timestamp: Date.now(),
|
|
70
|
+
ttl: 5000,
|
|
71
|
+
accessCount: 1,
|
|
72
|
+
lastAccess: Date.now(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
76
|
+
expect(keyToEvict).toBe('key1');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('LFUStrategy', () => {
|
|
81
|
+
let strategy: LFUStrategy<string>;
|
|
82
|
+
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
strategy = new LFUStrategy<string>();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('should evict least frequently used key', () => {
|
|
88
|
+
const entries = createMockEntries();
|
|
89
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
90
|
+
|
|
91
|
+
// key2 has the lowest access count (3)
|
|
92
|
+
expect(keyToEvict).toBe('key2');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should return undefined for empty entries', () => {
|
|
96
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
97
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
98
|
+
|
|
99
|
+
expect(keyToEvict).toBeUndefined();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('should handle ties by choosing first encountered', () => {
|
|
103
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
|
|
106
|
+
entries.set('key1', {
|
|
107
|
+
value: 'value1',
|
|
108
|
+
timestamp: now,
|
|
109
|
+
ttl: 5000,
|
|
110
|
+
accessCount: 2,
|
|
111
|
+
lastAccess: now,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
entries.set('key2', {
|
|
115
|
+
value: 'value2',
|
|
116
|
+
timestamp: now,
|
|
117
|
+
ttl: 5000,
|
|
118
|
+
accessCount: 2,
|
|
119
|
+
lastAccess: now,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
123
|
+
// Should return the first key with lowest count
|
|
124
|
+
expect(keyToEvict).toBe('key1');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('FIFOStrategy', () => {
|
|
129
|
+
let strategy: FIFOStrategy<string>;
|
|
130
|
+
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
strategy = new FIFOStrategy<string>();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('should evict first inserted key', () => {
|
|
136
|
+
const entries = createMockEntries();
|
|
137
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
138
|
+
|
|
139
|
+
// Map preserves insertion order, so first key should be evicted
|
|
140
|
+
expect(keyToEvict).toBe('key1');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('should return undefined for empty entries', () => {
|
|
144
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
145
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
146
|
+
|
|
147
|
+
expect(keyToEvict).toBeUndefined();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('should handle single entry', () => {
|
|
151
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
152
|
+
entries.set('onlyKey', {
|
|
153
|
+
value: 'value',
|
|
154
|
+
timestamp: Date.now(),
|
|
155
|
+
ttl: 5000,
|
|
156
|
+
accessCount: 1,
|
|
157
|
+
lastAccess: Date.now(),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
161
|
+
expect(keyToEvict).toBe('onlyKey');
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('TTLStrategy', () => {
|
|
166
|
+
let strategy: TTLStrategy<string>;
|
|
167
|
+
|
|
168
|
+
beforeEach(() => {
|
|
169
|
+
strategy = new TTLStrategy<string>();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('should evict key with nearest expiry', () => {
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
175
|
+
|
|
176
|
+
entries.set('key1', {
|
|
177
|
+
value: 'value1',
|
|
178
|
+
timestamp: now - 1000,
|
|
179
|
+
ttl: 2000, // Expires at now + 1000
|
|
180
|
+
accessCount: 1,
|
|
181
|
+
lastAccess: now,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
entries.set('key2', {
|
|
185
|
+
value: 'value2',
|
|
186
|
+
timestamp: now - 500,
|
|
187
|
+
ttl: 1000, // Expires at now + 500
|
|
188
|
+
accessCount: 1,
|
|
189
|
+
lastAccess: now,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
entries.set('key3', {
|
|
193
|
+
value: 'value3',
|
|
194
|
+
timestamp: now - 2000,
|
|
195
|
+
ttl: 3000, // Expires at now + 1000
|
|
196
|
+
accessCount: 1,
|
|
197
|
+
lastAccess: now,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
201
|
+
|
|
202
|
+
// key2 expires soonest (now + 500)
|
|
203
|
+
expect(keyToEvict).toBe('key2');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('should return undefined for empty entries', () => {
|
|
207
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
208
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
209
|
+
|
|
210
|
+
expect(keyToEvict).toBeUndefined();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('should handle already expired entries', () => {
|
|
214
|
+
const now = Date.now();
|
|
215
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
216
|
+
|
|
217
|
+
entries.set('key1', {
|
|
218
|
+
value: 'value1',
|
|
219
|
+
timestamp: now - 2000,
|
|
220
|
+
ttl: 1000, // Already expired
|
|
221
|
+
accessCount: 1,
|
|
222
|
+
lastAccess: now,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
entries.set('key2', {
|
|
226
|
+
value: 'value2',
|
|
227
|
+
timestamp: now - 1000,
|
|
228
|
+
ttl: 2000, // Expires at now + 1000
|
|
229
|
+
accessCount: 1,
|
|
230
|
+
lastAccess: now,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
234
|
+
|
|
235
|
+
// key1 is already expired, should be evicted first
|
|
236
|
+
expect(keyToEvict).toBe('key1');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test('should handle ties by choosing first encountered', () => {
|
|
240
|
+
const now = Date.now();
|
|
241
|
+
const entries = new Map<string, CacheEntry<string>>();
|
|
242
|
+
|
|
243
|
+
entries.set('key1', {
|
|
244
|
+
value: 'value1',
|
|
245
|
+
timestamp: now - 1000,
|
|
246
|
+
ttl: 2000, // Both expire at now + 1000
|
|
247
|
+
accessCount: 1,
|
|
248
|
+
lastAccess: now,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
entries.set('key2', {
|
|
252
|
+
value: 'value2',
|
|
253
|
+
timestamp: now - 1000,
|
|
254
|
+
ttl: 2000, // Both expire at now + 1000
|
|
255
|
+
accessCount: 1,
|
|
256
|
+
lastAccess: now,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const keyToEvict = strategy.findKeyToEvict(entries);
|
|
260
|
+
|
|
261
|
+
// Should return the first key with nearest expiry
|
|
262
|
+
expect(keyToEvict).toBe('key1');
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
describe('Strategy Integration', () => {
|
|
267
|
+
test('all strategies should handle different data types', () => {
|
|
268
|
+
const numberStrategy = new LRUStrategy<number>();
|
|
269
|
+
const objectStrategy = new LRUStrategy<{ id: string }>();
|
|
270
|
+
|
|
271
|
+
const numberEntries = new Map<string, CacheEntry<number>>();
|
|
272
|
+
numberEntries.set('num1', {
|
|
273
|
+
value: 42,
|
|
274
|
+
timestamp: Date.now(),
|
|
275
|
+
ttl: 5000,
|
|
276
|
+
accessCount: 1,
|
|
277
|
+
lastAccess: Date.now(),
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const objectEntries = new Map<string, CacheEntry<{ id: string }>>();
|
|
281
|
+
objectEntries.set('obj1', {
|
|
282
|
+
value: { id: 'test' },
|
|
283
|
+
timestamp: Date.now(),
|
|
284
|
+
ttl: 5000,
|
|
285
|
+
accessCount: 1,
|
|
286
|
+
lastAccess: Date.now(),
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
expect(numberStrategy.findKeyToEvict(numberEntries)).toBe('num1');
|
|
290
|
+
expect(objectStrategy.findKeyToEvict(objectEntries)).toBe('obj1');
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface CacheEntry<T> {
|
|
6
|
+
value: T;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
ttl: number;
|
|
9
|
+
accessCount: number;
|
|
10
|
+
lastAccess: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CacheConfig {
|
|
14
|
+
maxSize?: number;
|
|
15
|
+
defaultTTL?: number;
|
|
16
|
+
onEvict?: (key: string, entry: CacheEntry<unknown>) => void;
|
|
17
|
+
onExpire?: (key: string, entry: CacheEntry<unknown>) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CacheStats {
|
|
21
|
+
size: number;
|
|
22
|
+
hits: number;
|
|
23
|
+
misses: number;
|
|
24
|
+
evictions: number;
|
|
25
|
+
expirations: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type EvictionStrategy = 'lru' | 'lfu' | 'fifo' | 'ttl';
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Cache Domain Types
|
|
2
|
+
|
|
3
|
+
TypeScript interfaces and types for cache system.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
TypeScript type definitions for cache entries, configuration, statistics, and eviction strategies. Located at `src/cache/domain/types/`.
|
|
8
|
+
|
|
9
|
+
## Strategies
|
|
10
|
+
|
|
11
|
+
### Cache Entry Definition
|
|
12
|
+
- Define CacheEntry with value, timestamp, TTL, and access tracking
|
|
13
|
+
- Include metadata for eviction strategies
|
|
14
|
+
- Support generic type parameter for cached values
|
|
15
|
+
- Track access patterns for statistics
|
|
16
|
+
|
|
17
|
+
### Cache Configuration
|
|
18
|
+
- Provide optional configuration parameters
|
|
19
|
+
- Set sensible defaults (maxSize: 100, TTL: 5 minutes)
|
|
20
|
+
- Support lifecycle callbacks (onEvict, onExpire)
|
|
21
|
+
- Allow strategy selection
|
|
22
|
+
|
|
23
|
+
### Statistics Tracking
|
|
24
|
+
- Track hits, misses, evictions, expirations
|
|
25
|
+
- Calculate hit rate automatically
|
|
26
|
+
- Provide snapshot of cache state
|
|
27
|
+
- Support performance monitoring
|
|
28
|
+
|
|
29
|
+
### Type Safety
|
|
30
|
+
- Use generic type parameter for cached values
|
|
31
|
+
- Enforce type consistency across operations
|
|
32
|
+
- Provide type inference for cache creation
|
|
33
|
+
- Support nested type structures
|
|
34
|
+
|
|
35
|
+
## Restrictions
|
|
36
|
+
|
|
37
|
+
### Type Definitions
|
|
38
|
+
- DO NOT use optional properties for required fields
|
|
39
|
+
- DO NOT mix different data types in same cache without generic
|
|
40
|
+
- DO NOT create circular type dependencies
|
|
41
|
+
- DO NOT omit timestamp or TTL from entries
|
|
42
|
+
|
|
43
|
+
### Configuration
|
|
44
|
+
- DO NOT allow unlimited cache sizes (must have maxSize)
|
|
45
|
+
- DO NOT set zero or negative TTL as default
|
|
46
|
+
- DO NOT make callbacks required (use optional)
|
|
47
|
+
- DO NOT ignore validation in configuration
|
|
48
|
+
|
|
49
|
+
### Statistics
|
|
50
|
+
- DO NOT calculate statistics on every access (expensive)
|
|
51
|
+
- DO NOT track unnecessary metrics
|
|
52
|
+
- DO NOT use floating-point for hit rate (use decimal)
|
|
53
|
+
- DO NOT expose internal tracking fields
|
|
54
|
+
|
|
55
|
+
## Rules
|
|
56
|
+
|
|
57
|
+
### CacheEntry Interface
|
|
58
|
+
- MUST include value field of generic type T
|
|
59
|
+
- MUST include timestamp (creation time in milliseconds)
|
|
60
|
+
- MUST include ttl (time to live in milliseconds)
|
|
61
|
+
- MUST include accessCount (for LFU strategy)
|
|
62
|
+
- MUST include lastAccess (for LRU strategy)
|
|
63
|
+
|
|
64
|
+
### CacheConfig Interface
|
|
65
|
+
- MUST provide maxSize with default value of 100
|
|
66
|
+
- MUST provide defaultTTL with default value of 300000 (5 minutes)
|
|
67
|
+
- MUST provide optional onEvict callback
|
|
68
|
+
- MUST provide optional onExpire callback
|
|
69
|
+
- MUST validate configuration values
|
|
70
|
+
|
|
71
|
+
### CacheStats Interface
|
|
72
|
+
- MUST include size (current entry count)
|
|
73
|
+
- MUST include hits (cache hit count)
|
|
74
|
+
- MUST include misses (cache miss count)
|
|
75
|
+
- MUST include evictions (evicted entry count)
|
|
76
|
+
- MUST include expirations (expired entry count)
|
|
77
|
+
- MUST include hitRate (calculated as hits / (hits + misses))
|
|
78
|
+
|
|
79
|
+
### Eviction Strategy Type
|
|
80
|
+
- MUST be union of literal types ('lru' | 'lfu' | 'fifo' | 'ttl')
|
|
81
|
+
- MUST support custom strategies
|
|
82
|
+
- MUST be case-sensitive
|
|
83
|
+
- MUST be documented
|
|
84
|
+
|
|
85
|
+
### Generic Type Parameters
|
|
86
|
+
- MUST use T for cached value type
|
|
87
|
+
- MUST provide type inference
|
|
88
|
+
- MUST support nested types
|
|
89
|
+
- MUST enforce type safety
|
|
90
|
+
|
|
91
|
+
### Type Guards
|
|
92
|
+
- MUST provide isCacheEntry guard
|
|
93
|
+
- MUST validate all required fields
|
|
94
|
+
- MUST check types of all fields
|
|
95
|
+
- MUST handle edge cases
|
|
96
|
+
|
|
97
|
+
### Statistics Calculation
|
|
98
|
+
- MUST calculate hitRate as decimal (0-1)
|
|
99
|
+
- MUST update statistics on every operation
|
|
100
|
+
- MUST provide accurate counts
|
|
101
|
+
- MUST not expose internal calculation methods
|
|
102
|
+
|
|
103
|
+
### Export Rules
|
|
104
|
+
- MUST export all public types
|
|
105
|
+
- MUST use `type` keyword for type-only exports
|
|
106
|
+
- MUST maintain backward compatibility
|
|
107
|
+
- MUST document type changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @umituz/react-native-cache
|
|
3
|
+
* In-memory caching utilities for React Native
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { Cache } from './domain/Cache';
|
|
7
|
+
export { CacheManager, cacheManager } from './domain/CacheManager';
|
|
8
|
+
export { CacheStatsTracker } from './domain/CacheStatsTracker';
|
|
9
|
+
export { PatternMatcher } from './domain/PatternMatcher';
|
|
10
|
+
export { ErrorHandler, CacheError } from './domain/ErrorHandler';
|
|
11
|
+
export { TTLCache } from './infrastructure/TTLCache';
|
|
12
|
+
|
|
13
|
+
export type {
|
|
14
|
+
CacheEntry,
|
|
15
|
+
CacheConfig,
|
|
16
|
+
CacheStats,
|
|
17
|
+
EvictionStrategy,
|
|
18
|
+
} from './domain/types/Cache';
|
|
19
|
+
|
|
20
|
+
export type { EvictionStrategy as IEvictionStrategy } from './domain/strategies/EvictionStrategy';
|
|
21
|
+
|
|
22
|
+
export { LRUStrategy } from './domain/strategies/LRUStrategy';
|
|
23
|
+
export { LFUStrategy } from './domain/strategies/LFUStrategy';
|
|
24
|
+
export { FIFOStrategy } from './domain/strategies/FIFOStrategy';
|
|
25
|
+
export { TTLStrategy as TTLEvictionStrategy } from './domain/strategies/TTLStrategy';
|
|
26
|
+
|
|
27
|
+
export { useCache } from './presentation/useCache';
|
|
28
|
+
export { useCachedValue } from './presentation/useCachedValue';
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Cache Infrastructure
|
|
2
|
+
|
|
3
|
+
Implementation details and concrete implementations of cache domain logic.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Infrastructure layer for cache system, providing TTLCache implementation with actual storage and eviction logic. Located at `src/cache/infrastructure/`.
|
|
8
|
+
|
|
9
|
+
## Strategies
|
|
10
|
+
|
|
11
|
+
### Cache Implementation
|
|
12
|
+
- Use Map data structure for O(1) operations
|
|
13
|
+
- Implement TTL-based expiration checking
|
|
14
|
+
- Support eviction strategies from domain layer
|
|
15
|
+
- Provide thread-safe operations
|
|
16
|
+
|
|
17
|
+
### Storage Management
|
|
18
|
+
- Track cache size limits
|
|
19
|
+
- Implement automatic eviction when full
|
|
20
|
+
- Clean up expired entries periodically
|
|
21
|
+
- Support manual cache clearing
|
|
22
|
+
|
|
23
|
+
### Performance Optimization
|
|
24
|
+
- Minimize memory overhead per entry
|
|
25
|
+
- Use efficient data structures
|
|
26
|
+
- Implement lazy expiration checking
|
|
27
|
+
- Cache frequently accessed metadata
|
|
28
|
+
|
|
29
|
+
### Integration with Domain
|
|
30
|
+
- Implement interfaces defined in domain layer
|
|
31
|
+
- Use domain types for type safety
|
|
32
|
+
- Follow domain-level strategies and rules
|
|
33
|
+
- Export clean API to presentation layer
|
|
34
|
+
|
|
35
|
+
## Restrictions
|
|
36
|
+
|
|
37
|
+
### Implementation
|
|
38
|
+
- DO NOT expose internal storage directly
|
|
39
|
+
- DO NOT bypass eviction strategies
|
|
40
|
+
- DO NOT allow cache size to exceed limit
|
|
41
|
+
- DO NOT store non-serializable data
|
|
42
|
+
|
|
43
|
+
### Performance
|
|
44
|
+
- DO NOT perform expensive operations on every access
|
|
45
|
+
- DO NOT use blocking operations
|
|
46
|
+
- DO NOT allocate memory unnecessarily
|
|
47
|
+
- DO NOT iterate entire cache for single operations
|
|
48
|
+
|
|
49
|
+
### Expiration
|
|
50
|
+
- DO NOT ignore expired entries on access
|
|
51
|
+
- DO NOT auto-clean without explicit trigger
|
|
52
|
+
- DO NOT use inconsistent time sources
|
|
53
|
+
- DO NOT rely solely on TTL for memory management
|
|
54
|
+
|
|
55
|
+
## Rules
|
|
56
|
+
|
|
57
|
+
### TTLCache Implementation
|
|
58
|
+
- MUST implement Cache interface from domain
|
|
59
|
+
- MUST enforce maxSize limit
|
|
60
|
+
- MUST check expiration on get operations
|
|
61
|
+
- MUST trigger eviction when full
|
|
62
|
+
- MUST provide accurate statistics
|
|
63
|
+
|
|
64
|
+
### Entry Storage
|
|
65
|
+
- MUST use Map<string, CacheEntry<T>> for storage
|
|
66
|
+
- MUST include all required metadata
|
|
67
|
+
- MUST update timestamps on access
|
|
68
|
+
- MUST validate entry structure
|
|
69
|
+
|
|
70
|
+
### Expiration Handling
|
|
71
|
+
- MUST check (timestamp + ttl) < Date.now()
|
|
72
|
+
- MUST return undefined for expired entries
|
|
73
|
+
- MUST remove expired entries from storage
|
|
74
|
+
- MUST track expiration statistics
|
|
75
|
+
|
|
76
|
+
### Eviction Execution
|
|
77
|
+
- MUST call strategy.findKeyToEvict() when full
|
|
78
|
+
- MUST remove selected entry from storage
|
|
79
|
+
- MUST trigger onEvict callback if provided
|
|
80
|
+
- MUST update eviction statistics
|
|
81
|
+
|
|
82
|
+
### Statistics Tracking
|
|
83
|
+
- MUST increment hits on successful get
|
|
84
|
+
- MUST increment misses on failed get
|
|
85
|
+
- MUST increment evictions on eviction
|
|
86
|
+
- MUST increment expirations on expiration
|
|
87
|
+
- MUST calculate hitRate correctly
|
|
88
|
+
|
|
89
|
+
### Public Methods
|
|
90
|
+
- MUST implement set(key, value, ttl?)
|
|
91
|
+
- MUST implement get(key) -> T | undefined
|
|
92
|
+
- MUST implement has(key) -> boolean
|
|
93
|
+
- MUST implement delete(key) -> boolean
|
|
94
|
+
- MUST implement clear()
|
|
95
|
+
- MUST implement getStats()
|
|
96
|
+
|
|
97
|
+
### Configuration Validation
|
|
98
|
+
- MUST validate maxSize > 0
|
|
99
|
+
- MUST validate defaultTTL > 0
|
|
100
|
+
- MUST provide default values
|
|
101
|
+
- MUST reject invalid configuration
|
|
102
|
+
|
|
103
|
+
### Lifecycle Callbacks
|
|
104
|
+
- MUST call onEvict(entry) when entry evicted
|
|
105
|
+
- MUST call onExpire(entry) when entry expired
|
|
106
|
+
- MUST pass key and entry to callbacks
|
|
107
|
+
- MUST handle callback errors gracefully
|
|
108
|
+
|
|
109
|
+
### Type Safety
|
|
110
|
+
- MUST use generic type parameter T
|
|
111
|
+
- MUST enforce type consistency
|
|
112
|
+
- MUST provide type inference
|
|
113
|
+
- MUST validate input types where possible
|
|
114
|
+
|
|
115
|
+
### Thread Safety
|
|
116
|
+
- MUST handle concurrent access appropriately
|
|
117
|
+
- MUST not corrupt cache state
|
|
118
|
+
- MUST provide atomic operations
|
|
119
|
+
- MUST handle race conditions
|
|
120
|
+
|
|
121
|
+
### Testing Requirements
|
|
122
|
+
- MUST test all eviction strategies
|
|
123
|
+
- MUST test expiration logic
|
|
124
|
+
- MUST test statistics accuracy
|
|
125
|
+
- MUST test edge cases (empty, full, expired)
|
|
126
|
+
- MUST measure performance
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTL Cache
|
|
3
|
+
* Time-to-live cache with automatic cleanup
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Cache } from '../domain/Cache';
|
|
7
|
+
import type { CacheConfig } from '../domain/types/Cache';
|
|
8
|
+
|
|
9
|
+
export class TTLCache<T = unknown> extends Cache<T> {
|
|
10
|
+
private cleanupInterval: ReturnType<typeof setInterval> | null = null;
|
|
11
|
+
private isDestroyed = false;
|
|
12
|
+
private readonly cleanupIntervalMs: number;
|
|
13
|
+
|
|
14
|
+
constructor(config: CacheConfig & { cleanupIntervalMs?: number } = {}) {
|
|
15
|
+
super(config);
|
|
16
|
+
|
|
17
|
+
this.cleanupIntervalMs = config.cleanupIntervalMs || 60000;
|
|
18
|
+
this.startCleanup();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private startCleanup(): void {
|
|
22
|
+
if (this.isDestroyed) return;
|
|
23
|
+
|
|
24
|
+
this.cleanupInterval = setInterval(() => {
|
|
25
|
+
if (!this.isDestroyed) {
|
|
26
|
+
this.cleanup();
|
|
27
|
+
}
|
|
28
|
+
}, this.cleanupIntervalMs);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private cleanup(): void {
|
|
32
|
+
if (this.isDestroyed) return;
|
|
33
|
+
|
|
34
|
+
const keys = this.keys();
|
|
35
|
+
let cleanedCount = 0;
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
|
|
38
|
+
for (const key of keys) {
|
|
39
|
+
const entry = (this as any).store.get(key);
|
|
40
|
+
if (entry && (now - entry.timestamp) > entry.ttl) {
|
|
41
|
+
(this as any).store.delete(key);
|
|
42
|
+
cleanedCount++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (cleanedCount > 0) {
|
|
47
|
+
(this as any).statsTracker.updateSize((this as any).store.size);
|
|
48
|
+
(this as any).statsTracker.recordExpiration();
|
|
49
|
+
|
|
50
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
51
|
+
console.log(`TTLCache: Cleaned up ${cleanedCount} expired entries`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
destroy(): void {
|
|
57
|
+
if (this.isDestroyed) return;
|
|
58
|
+
|
|
59
|
+
this.isDestroyed = true;
|
|
60
|
+
|
|
61
|
+
if (this.cleanupInterval) {
|
|
62
|
+
clearInterval(this.cleanupInterval);
|
|
63
|
+
this.cleanupInterval = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.clear();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
override set(key: string, value: T, ttl?: number): void {
|
|
70
|
+
if (this.isDestroyed) {
|
|
71
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
72
|
+
console.warn('TTLCache: Attempted to set value on destroyed cache');
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
super.set(key, value, ttl);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
override get(key: string): T | undefined {
|
|
80
|
+
if (this.isDestroyed) {
|
|
81
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
82
|
+
console.warn('TTLCache: Attempted to get value from destroyed cache');
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
return super.get(key);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
override has(key: string): boolean {
|
|
90
|
+
if (this.isDestroyed) return false;
|
|
91
|
+
return super.has(key);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
override delete(key: string): boolean {
|
|
95
|
+
if (this.isDestroyed) return false;
|
|
96
|
+
return super.delete(key);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
override clear(): void {
|
|
100
|
+
if (this.isDestroyed) return;
|
|
101
|
+
super.clear();
|
|
102
|
+
}
|
|
103
|
+
}
|