@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,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PatternMatcher Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { PatternMatcher } from '../PatternMatcher';
|
|
6
|
+
|
|
7
|
+
describe('PatternMatcher', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
PatternMatcher.clearCache();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('convertPatternToRegex', () => {
|
|
13
|
+
test('should handle simple patterns', () => {
|
|
14
|
+
const regex = PatternMatcher.convertPatternToRegex('user:*');
|
|
15
|
+
expect(regex.test('user:1')).toBe(true);
|
|
16
|
+
expect(regex.test('user:123')).toBe(true);
|
|
17
|
+
expect(regex.test('user:abc')).toBe(true);
|
|
18
|
+
expect(regex.test('admin:1')).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('should handle multiple wildcards', () => {
|
|
22
|
+
const regex = PatternMatcher.convertPatternToRegex('*:*:*');
|
|
23
|
+
expect(regex.test('user:1:profile')).toBe(true);
|
|
24
|
+
expect(regex.test('post:2:comments')).toBe(true);
|
|
25
|
+
expect(regex.test('user:1')).toBe(false);
|
|
26
|
+
expect(regex.test('user')).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('should handle exact matches', () => {
|
|
30
|
+
const regex = PatternMatcher.convertPatternToRegex('exact-key');
|
|
31
|
+
expect(regex.test('exact-key')).toBe(true);
|
|
32
|
+
expect(regex.test('exact-key-123')).toBe(false);
|
|
33
|
+
expect(regex.test('exact')).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('should handle patterns with special characters', () => {
|
|
37
|
+
const regex = PatternMatcher.convertPatternToRegex('user.*profile');
|
|
38
|
+
expect(regex.test('user.123.profile')).toBe(true);
|
|
39
|
+
expect(regex.test('user.abc.profile')).toBe(true);
|
|
40
|
+
expect(regex.test('user.profile')).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('should escape regex special characters', () => {
|
|
44
|
+
const regex = PatternMatcher.convertPatternToRegex('user.+?^${}()|[]\\');
|
|
45
|
+
expect(regex.test('user.+?^${}()|[]\\')).toBe(true);
|
|
46
|
+
expect(regex.test('user-something')).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('should handle empty pattern', () => {
|
|
50
|
+
const regex = PatternMatcher.convertPatternToRegex('');
|
|
51
|
+
expect(regex.test('')).toBe(true);
|
|
52
|
+
expect(regex.test('anything')).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('should handle pattern with only wildcards', () => {
|
|
56
|
+
const regex = PatternMatcher.convertPatternToRegex('*');
|
|
57
|
+
expect(regex.test('anything')).toBe(true);
|
|
58
|
+
expect(regex.test('')).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('should handle complex patterns', () => {
|
|
62
|
+
const regex = PatternMatcher.convertPatternToRegex('cache:*:data:*');
|
|
63
|
+
expect(regex.test('cache:user:data:123')).toBe(true);
|
|
64
|
+
expect(regex.test('cache:post:data:456')).toBe(true);
|
|
65
|
+
expect(regex.test('cache:user:meta:123')).toBe(false);
|
|
66
|
+
expect(regex.test('user:data:123')).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('should handle patterns with dots and dashes', () => {
|
|
70
|
+
const regex = PatternMatcher.convertPatternToRegex('module.*-service.*');
|
|
71
|
+
expect(regex.test('module.auth-service.v1')).toBe(true);
|
|
72
|
+
expect(regex.test('module.user-service.v2')).toBe(true);
|
|
73
|
+
expect(regex.test('module.auth')).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('matchesPattern', () => {
|
|
78
|
+
test('should return true for matching patterns', () => {
|
|
79
|
+
expect(PatternMatcher.matchesPattern('user:1', 'user:*')).toBe(true);
|
|
80
|
+
expect(PatternMatcher.matchesPattern('post:123:comments', '*:*:*')).toBe(true);
|
|
81
|
+
expect(PatternMatcher.matchesPattern('exact', 'exact')).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('should return false for non-matching patterns', () => {
|
|
85
|
+
expect(PatternMatcher.matchesPattern('admin:1', 'user:*')).toBe(false);
|
|
86
|
+
expect(PatternMatcher.matchesPattern('user:1', '*:*:*')).toBe(false);
|
|
87
|
+
expect(PatternMatcher.matchesPattern('exact', 'different')).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('should handle case-sensitive matching', () => {
|
|
91
|
+
expect(PatternMatcher.matchesPattern('User:1', 'user:*')).toBe(false);
|
|
92
|
+
expect(PatternMatcher.matchesPattern('user:1', 'User:*')).toBe(false);
|
|
93
|
+
expect(PatternMatcher.matchesPattern('USER:1', 'USER:*')).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('should handle empty strings', () => {
|
|
97
|
+
expect(PatternMatcher.matchesPattern('', '')).toBe(true);
|
|
98
|
+
expect(PatternMatcher.matchesPattern('', '*')).toBe(true);
|
|
99
|
+
expect(PatternMatcher.matchesPattern('test', '')).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('Regex Caching', () => {
|
|
104
|
+
test('should cache converted regex patterns', () => {
|
|
105
|
+
const pattern = 'user:*';
|
|
106
|
+
|
|
107
|
+
const regex1 = PatternMatcher.convertPatternToRegex(pattern);
|
|
108
|
+
const regex2 = PatternMatcher.convertPatternToRegex(pattern);
|
|
109
|
+
|
|
110
|
+
expect(regex1).toBe(regex2); // Same reference
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('should create different regex for different patterns', () => {
|
|
114
|
+
const regex1 = PatternMatcher.convertPatternToRegex('user:*');
|
|
115
|
+
const regex2 = PatternMatcher.convertPatternToRegex('post:*');
|
|
116
|
+
|
|
117
|
+
expect(regex1).not.toBe(regex2); // Different references
|
|
118
|
+
expect(regex1.test('user:1')).toBe(true);
|
|
119
|
+
expect(regex1.test('post:1')).toBe(false);
|
|
120
|
+
expect(regex2.test('post:1')).toBe(true);
|
|
121
|
+
expect(regex2.test('user:1')).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('should clear cache', () => {
|
|
125
|
+
const pattern = 'user:*';
|
|
126
|
+
|
|
127
|
+
const regex1 = PatternMatcher.convertPatternToRegex(pattern);
|
|
128
|
+
PatternMatcher.clearCache();
|
|
129
|
+
const regex2 = PatternMatcher.convertPatternToRegex(pattern);
|
|
130
|
+
|
|
131
|
+
expect(regex1).not.toBe(regex2); // Different references after clear
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('should maintain cache performance', () => {
|
|
135
|
+
const pattern = 'very:complex:pattern:*:with:many:parts:*';
|
|
136
|
+
|
|
137
|
+
// First call - should create new regex
|
|
138
|
+
const start1 = performance.now();
|
|
139
|
+
const regex1 = PatternMatcher.convertPatternToRegex(pattern);
|
|
140
|
+
const end1 = performance.now();
|
|
141
|
+
|
|
142
|
+
// Second call - should use cached regex
|
|
143
|
+
const start2 = performance.now();
|
|
144
|
+
const regex2 = PatternMatcher.convertPatternToRegex(pattern);
|
|
145
|
+
const end2 = performance.now();
|
|
146
|
+
|
|
147
|
+
expect(regex1).toBe(regex2);
|
|
148
|
+
// Second call should be faster (though this might not always be true in tests)
|
|
149
|
+
expect(end2 - start2).toBeLessThanOrEqual(end1 - start1);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('Edge Cases', () => {
|
|
154
|
+
test('should handle very long patterns', () => {
|
|
155
|
+
const longPattern = 'a'.repeat(1000) + '*';
|
|
156
|
+
const longKey = 'a'.repeat(1000) + 'suffix';
|
|
157
|
+
|
|
158
|
+
expect(PatternMatcher.matchesPattern(longKey, longPattern)).toBe(true);
|
|
159
|
+
expect(PatternMatcher.matchesPattern('a'.repeat(999), longPattern)).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('should handle patterns with only special characters', () => {
|
|
163
|
+
const regex = PatternMatcher.convertPatternToRegex('.+?^${}()|[]\\');
|
|
164
|
+
expect(regex.test('.+?^${}()|[]\\')).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('should handle Unicode characters', () => {
|
|
168
|
+
expect(PatternMatcher.matchesPattern('üser:1', 'üser:*')).toBe(true);
|
|
169
|
+
expect(PatternMatcher.matchesPattern('用户:1', '用户:*')).toBe(true);
|
|
170
|
+
expect(PatternMatcher.matchesPattern('🚀:launch', '🚀:*')).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('should handle null and undefined inputs gracefully', () => {
|
|
174
|
+
expect(() => {
|
|
175
|
+
PatternMatcher.convertPatternToRegex(null as any);
|
|
176
|
+
}).toThrow();
|
|
177
|
+
|
|
178
|
+
expect(() => {
|
|
179
|
+
PatternMatcher.convertPatternToRegex(undefined as any);
|
|
180
|
+
}).toThrow();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('should handle non-string inputs', () => {
|
|
184
|
+
expect(() => {
|
|
185
|
+
PatternMatcher.convertPatternToRegex(123 as any);
|
|
186
|
+
}).toThrow();
|
|
187
|
+
|
|
188
|
+
expect(() => {
|
|
189
|
+
PatternMatcher.convertPatternToRegex({} as any);
|
|
190
|
+
}).toThrow();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('Performance Considerations', () => {
|
|
195
|
+
test('should handle large number of pattern matches efficiently', () => {
|
|
196
|
+
const pattern = 'cache:*:data:*';
|
|
197
|
+
const keys = Array.from({ length: 1000 }, (_, i) => `cache:${i}:data:${i * 2}`);
|
|
198
|
+
|
|
199
|
+
const start = performance.now();
|
|
200
|
+
|
|
201
|
+
keys.forEach(key => {
|
|
202
|
+
PatternMatcher.matchesPattern(key, pattern);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const end = performance.now();
|
|
206
|
+
|
|
207
|
+
// Should complete within reasonable time (adjust threshold as needed)
|
|
208
|
+
expect(end - start).toBeLessThan(100); // 100ms
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('should reuse cached regex for many matches', () => {
|
|
212
|
+
const pattern = 'user:*';
|
|
213
|
+
const keys = Array.from({ length: 1000 }, (_, i) => `user:${i}`);
|
|
214
|
+
|
|
215
|
+
// Pre-cache the regex
|
|
216
|
+
PatternMatcher.convertPatternToRegex(pattern);
|
|
217
|
+
|
|
218
|
+
const start = performance.now();
|
|
219
|
+
|
|
220
|
+
keys.forEach(key => {
|
|
221
|
+
PatternMatcher.matchesPattern(key, pattern);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const end = performance.now();
|
|
225
|
+
|
|
226
|
+
// Should be faster with cached regex
|
|
227
|
+
expect(end - start).toBeLessThan(50); // 50ms
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe('Real-world Scenarios', () => {
|
|
232
|
+
test('should handle common cache key patterns', () => {
|
|
233
|
+
const scenarios = [
|
|
234
|
+
{ key: 'user:123:profile', pattern: 'user:*:profile', expected: true },
|
|
235
|
+
{ key: 'user:123:settings', pattern: 'user:*:profile', expected: false },
|
|
236
|
+
{ key: 'post:456:comments:789', pattern: 'post:*:comments:*', expected: true },
|
|
237
|
+
{ key: 'post:456:likes', pattern: 'post:*:comments:*', expected: false },
|
|
238
|
+
{ key: 'session:abc123', pattern: 'session:*', expected: true },
|
|
239
|
+
{ key: 'cache:api:user:123', pattern: 'cache:api:*', expected: true },
|
|
240
|
+
{ key: 'cache:db:user:123', pattern: 'cache:api:*', expected: false },
|
|
241
|
+
];
|
|
242
|
+
|
|
243
|
+
scenarios.forEach(({ key, pattern, expected }) => {
|
|
244
|
+
expect(PatternMatcher.matchesPattern(key, pattern)).toBe(expected);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('should handle API endpoint patterns', () => {
|
|
249
|
+
const apiPatterns = [
|
|
250
|
+
{ endpoint: '/api/v1/users/123', pattern: '/api/v1/users/*', expected: true },
|
|
251
|
+
{ endpoint: '/api/v1/posts/456/comments', pattern: '/api/v1/posts/*/comments', expected: true },
|
|
252
|
+
{ endpoint: '/api/v2/users/123', pattern: '/api/v1/users/*', expected: false },
|
|
253
|
+
{ endpoint: '/api/v1/users', pattern: '/api/v1/users/*', expected: false },
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
apiPatterns.forEach(({ endpoint, pattern, expected }) => {
|
|
257
|
+
expect(PatternMatcher.matchesPattern(endpoint, pattern)).toBe(expected);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FIFO (First In First Out) Eviction Strategy
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { EvictionStrategy } from './EvictionStrategy';
|
|
6
|
+
import type { CacheEntry } from '../types/Cache';
|
|
7
|
+
|
|
8
|
+
export class FIFOStrategy<T> implements EvictionStrategy<T> {
|
|
9
|
+
findKeyToEvict(entries: Map<string, CacheEntry<T>>): string | undefined {
|
|
10
|
+
return entries.keys().next().value;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LFU (Least Frequently Used) Eviction Strategy
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { EvictionStrategy } from './EvictionStrategy';
|
|
6
|
+
import type { CacheEntry } from '../types/Cache';
|
|
7
|
+
|
|
8
|
+
export class LFUStrategy<T> implements EvictionStrategy<T> {
|
|
9
|
+
findKeyToEvict(entries: Map<string, CacheEntry<T>>): string | undefined {
|
|
10
|
+
let least: string | undefined;
|
|
11
|
+
let leastCount = Infinity;
|
|
12
|
+
|
|
13
|
+
for (const [key, entry] of entries.entries()) {
|
|
14
|
+
if (entry.accessCount < leastCount) {
|
|
15
|
+
leastCount = entry.accessCount;
|
|
16
|
+
least = key;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return least;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LRU (Least Recently Used) Eviction Strategy
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { EvictionStrategy } from './EvictionStrategy';
|
|
6
|
+
import type { CacheEntry } from '../types/Cache';
|
|
7
|
+
|
|
8
|
+
export class LRUStrategy<T> implements EvictionStrategy<T> {
|
|
9
|
+
findKeyToEvict(entries: Map<string, CacheEntry<T>>): string | undefined {
|
|
10
|
+
let oldest: string | undefined;
|
|
11
|
+
let oldestTime = Infinity;
|
|
12
|
+
|
|
13
|
+
for (const [key, entry] of entries.entries()) {
|
|
14
|
+
if (entry.lastAccess < oldestTime) {
|
|
15
|
+
oldestTime = entry.lastAccess;
|
|
16
|
+
oldest = key;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return oldest;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Cache Eviction Strategies
|
|
2
|
+
|
|
3
|
+
Algorithms for determining which cache entries to evict when cache is full.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Eviction strategies determine which cache entries to remove when the cache reaches its maximum size. Located at `src/cache/domain/strategies/`.
|
|
8
|
+
|
|
9
|
+
## Strategies
|
|
10
|
+
|
|
11
|
+
### Strategy Selection
|
|
12
|
+
- Use LRU (Least Recently Used) for most use cases - best general performance
|
|
13
|
+
- Use LFU (Least Frequently Used) for popularity-based caching - keeps frequently accessed items
|
|
14
|
+
- Use FIFO (First In First Out) for simple queue-based caching - lowest overhead
|
|
15
|
+
- Use TTL (Time To Live) for time-sensitive data - required for expiration
|
|
16
|
+
|
|
17
|
+
### Performance Considerations
|
|
18
|
+
- LRU provides O(1) eviction time with proper data structures
|
|
19
|
+
- LFU provides O(n) eviction time but better for read-heavy workloads
|
|
20
|
+
- FIFO provides O(1) eviction time with minimal overhead
|
|
21
|
+
- TTL requires periodic cleanup but essential for freshness
|
|
22
|
+
|
|
23
|
+
### Access Pattern Analysis
|
|
24
|
+
- Consider data access patterns when choosing strategy
|
|
25
|
+
- Use LRU for temporal locality (recently accessed likely to be accessed again)
|
|
26
|
+
- Use LFU for frequency-based patterns (popular items should stay)
|
|
27
|
+
- Use FIFO for sequential access patterns
|
|
28
|
+
- Use TTL when data freshness is critical
|
|
29
|
+
|
|
30
|
+
### Implementation Strategy
|
|
31
|
+
- Implement EvictionStrategy interface for custom strategies
|
|
32
|
+
- Track necessary metadata (access count, last access, timestamp)
|
|
33
|
+
- Provide findKeyToEvict method for selection
|
|
34
|
+
- Update metadata on cache operations
|
|
35
|
+
|
|
36
|
+
## Restrictions
|
|
37
|
+
|
|
38
|
+
### Strategy Usage
|
|
39
|
+
- DO NOT use LFU for write-heavy workloads (expensive)
|
|
40
|
+
- DO NOT use FIFO when access patterns matter
|
|
41
|
+
- DO NOT use TTL when data staleness is acceptable
|
|
42
|
+
- DO NOT change strategies after cache creation
|
|
43
|
+
|
|
44
|
+
### Performance
|
|
45
|
+
- DO NOT implement O(n²) eviction algorithms
|
|
46
|
+
- DO NOT scan entire cache for eviction on every operation
|
|
47
|
+
- DO NOT track unnecessary metadata for chosen strategy
|
|
48
|
+
- DO NOT use complex algorithms for simple use cases
|
|
49
|
+
|
|
50
|
+
### Custom Strategies
|
|
51
|
+
- DO NOT implement custom strategies without thorough testing
|
|
52
|
+
- DO NOT ignore edge cases (empty cache, single entry)
|
|
53
|
+
- DO NOT create strategies without clear performance benefits
|
|
54
|
+
- DO NOT mix multiple eviction strategies in same cache
|
|
55
|
+
|
|
56
|
+
## Rules
|
|
57
|
+
|
|
58
|
+
### EvictionStrategy Interface
|
|
59
|
+
- MUST implement findKeyToEvict(store: Map): string | undefined
|
|
60
|
+
- MUST return undefined when store is empty
|
|
61
|
+
- MUST return single key for eviction
|
|
62
|
+
- MUST handle all cache states correctly
|
|
63
|
+
|
|
64
|
+
### LRU Strategy
|
|
65
|
+
- MUST track lastAccess timestamp for each entry
|
|
66
|
+
- MUST update lastAccess on every get operation
|
|
67
|
+
- MUST select entry with oldest lastAccess time
|
|
68
|
+
- MUST provide O(1) or O(log n) time complexity
|
|
69
|
+
|
|
70
|
+
### LFU Strategy
|
|
71
|
+
- MUST track accessCount for each entry
|
|
72
|
+
- MUST increment accessCount on every get operation
|
|
73
|
+
- MUST select entry with lowest accessCount
|
|
74
|
+
- MUST handle ties consistently (use LRU as tiebreaker)
|
|
75
|
+
|
|
76
|
+
### FIFO Strategy
|
|
77
|
+
- MUST track insertion order for each entry
|
|
78
|
+
- MUST select oldest entry (first inserted)
|
|
79
|
+
- MUST NOT consider access patterns
|
|
80
|
+
- MUST provide O(1) time complexity
|
|
81
|
+
|
|
82
|
+
### TTL Strategy
|
|
83
|
+
- MUST check timestamp + ttl against current time
|
|
84
|
+
- MUST select expired entries first
|
|
85
|
+
- MUST fall back to secondary strategy if no expired entries
|
|
86
|
+
- MUST handle zero TTL entries (immediate eviction)
|
|
87
|
+
|
|
88
|
+
### Metadata Updates
|
|
89
|
+
- MUST update eviction metadata on every cache operation
|
|
90
|
+
- MUST update metadata on cache set operations
|
|
91
|
+
- MUST update metadata on cache get operations
|
|
92
|
+
- MUST reset metadata appropriately on cache updates
|
|
93
|
+
|
|
94
|
+
### Edge Cases
|
|
95
|
+
- MUST handle empty cache (return undefined)
|
|
96
|
+
- MUST handle single entry cache
|
|
97
|
+
- MUST handle all entries with same metadata
|
|
98
|
+
- MUST handle concurrent access safely
|
|
99
|
+
|
|
100
|
+
### Strategy Selection
|
|
101
|
+
- MUST use LRU as default strategy
|
|
102
|
+
- MUST allow strategy override in configuration
|
|
103
|
+
- MUST validate strategy choice
|
|
104
|
+
- MUST document strategy behavior
|
|
105
|
+
|
|
106
|
+
### Testing Requirements
|
|
107
|
+
- MUST test eviction with full cache
|
|
108
|
+
- MUST test eviction with empty cache
|
|
109
|
+
- MUST test metadata tracking accuracy
|
|
110
|
+
- MUST test edge cases
|
|
111
|
+
- MUST measure time complexity
|
|
112
|
+
|
|
113
|
+
### Performance Rules
|
|
114
|
+
- MUST not exceed O(n) for eviction selection
|
|
115
|
+
- MUST not allocate memory during eviction
|
|
116
|
+
- MUST not use blocking operations
|
|
117
|
+
- MUST minimize CPU overhead
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTL (Time To Live) Eviction Strategy
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { EvictionStrategy } from './EvictionStrategy';
|
|
6
|
+
import type { CacheEntry } from '../types/Cache';
|
|
7
|
+
|
|
8
|
+
export class TTLStrategy<T> implements EvictionStrategy<T> {
|
|
9
|
+
findKeyToEvict(entries: Map<string, CacheEntry<T>>): string | undefined {
|
|
10
|
+
let nearest: string | undefined;
|
|
11
|
+
let nearestExpiry = Infinity;
|
|
12
|
+
|
|
13
|
+
for (const [key, entry] of entries.entries()) {
|
|
14
|
+
const expiry = entry.timestamp + entry.ttl;
|
|
15
|
+
if (expiry < nearestExpiry) {
|
|
16
|
+
nearestExpiry = expiry;
|
|
17
|
+
nearest = key;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return nearest;
|
|
22
|
+
}
|
|
23
|
+
}
|