@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.
Files changed (128) hide show
  1. package/package.json +5 -6
  2. package/src/device/infrastructure/repositories/LegacyDeviceIdRepository.ts +1 -1
  3. package/src/device/infrastructure/services/DeviceFeatureService.ts +1 -1
  4. package/src/exception/infrastructure/services/ExceptionLogger.ts +1 -1
  5. package/src/exception/infrastructure/storage/ExceptionStore.ts +1 -1
  6. package/src/exports/filesystem.ts +1 -0
  7. package/src/exports/storage.ts +1 -0
  8. package/src/filesystem/domain/constants/FileConstants.ts +20 -0
  9. package/src/filesystem/domain/entities/File.ts +20 -0
  10. package/src/filesystem/domain/types/FileTypes.ts +43 -0
  11. package/src/filesystem/domain/utils/FileUtils.ts +86 -0
  12. package/src/filesystem/index.ts +23 -0
  13. package/src/filesystem/infrastructure/services/FileSystemService.ts +45 -0
  14. package/src/filesystem/infrastructure/services/cache.service.ts +48 -0
  15. package/src/filesystem/infrastructure/services/directory.service.ts +66 -0
  16. package/src/filesystem/infrastructure/services/download.constants.ts +6 -0
  17. package/src/filesystem/infrastructure/services/download.service.ts +74 -0
  18. package/src/filesystem/infrastructure/services/download.types.ts +7 -0
  19. package/src/filesystem/infrastructure/services/encoding.service.ts +25 -0
  20. package/src/filesystem/infrastructure/services/file-info.service.ts +52 -0
  21. package/src/filesystem/infrastructure/services/file-manager.service.ts +81 -0
  22. package/src/filesystem/infrastructure/services/file-path.service.ts +22 -0
  23. package/src/filesystem/infrastructure/services/file-reader.service.ts +52 -0
  24. package/src/filesystem/infrastructure/services/file-writer.service.ts +32 -0
  25. package/src/filesystem/infrastructure/utils/blob.utils.ts +20 -0
  26. package/src/image/infrastructure/services/ImageStorageService.ts +1 -1
  27. package/src/index.ts +9 -0
  28. package/src/molecules/alerts/AlertStore.ts +1 -1
  29. package/src/molecules/calendar/infrastructure/storage/EventActions.ts +1 -1
  30. package/src/molecules/calendar/infrastructure/stores/storageAdapter.ts +1 -1
  31. package/src/offline/infrastructure/storage/OfflineStore.ts +1 -1
  32. package/src/onboarding/infrastructure/storage/OnboardingStore.ts +2 -2
  33. package/src/onboarding/infrastructure/storage/__tests__/OnboardingStore.test.ts +1 -1
  34. package/src/onboarding/infrastructure/storage/actions/answerActions.ts +1 -1
  35. package/src/onboarding/infrastructure/storage/actions/storageHelpers.ts +1 -1
  36. package/src/storage/README.md +185 -0
  37. package/src/storage/__tests__/integration.test.ts +391 -0
  38. package/src/storage/__tests__/mocks/asyncStorage.mock.ts +52 -0
  39. package/src/storage/__tests__/performance.test.tsx +352 -0
  40. package/src/storage/__tests__/setup.ts +63 -0
  41. package/src/storage/application/README.md +158 -0
  42. package/src/storage/application/ports/IStorageRepository.ts +61 -0
  43. package/src/storage/application/ports/README.md +127 -0
  44. package/src/storage/cache/README.md +154 -0
  45. package/src/storage/cache/__tests__/PerformanceAndMemory.test.ts +387 -0
  46. package/src/storage/cache/__tests__/setup.ts +19 -0
  47. package/src/storage/cache/domain/Cache.ts +146 -0
  48. package/src/storage/cache/domain/CacheManager.md +83 -0
  49. package/src/storage/cache/domain/CacheManager.ts +48 -0
  50. package/src/storage/cache/domain/CacheStatsTracker.md +169 -0
  51. package/src/storage/cache/domain/CacheStatsTracker.ts +49 -0
  52. package/src/storage/cache/domain/CachedValue.md +97 -0
  53. package/src/storage/cache/domain/ErrorHandler.md +99 -0
  54. package/src/storage/cache/domain/ErrorHandler.ts +42 -0
  55. package/src/storage/cache/domain/PatternMatcher.md +122 -0
  56. package/src/storage/cache/domain/PatternMatcher.ts +30 -0
  57. package/src/storage/cache/domain/README.md +118 -0
  58. package/src/storage/cache/domain/__tests__/Cache.test.ts +293 -0
  59. package/src/storage/cache/domain/__tests__/CacheManager.test.ts +276 -0
  60. package/src/storage/cache/domain/__tests__/ErrorHandler.test.ts +303 -0
  61. package/src/storage/cache/domain/__tests__/PatternMatcher.test.ts +261 -0
  62. package/src/storage/cache/domain/strategies/EvictionStrategy.ts +9 -0
  63. package/src/storage/cache/domain/strategies/FIFOStrategy.ts +12 -0
  64. package/src/storage/cache/domain/strategies/LFUStrategy.ts +22 -0
  65. package/src/storage/cache/domain/strategies/LRUStrategy.ts +22 -0
  66. package/src/storage/cache/domain/strategies/README.md +117 -0
  67. package/src/storage/cache/domain/strategies/TTLStrategy.ts +23 -0
  68. package/src/storage/cache/domain/strategies/__tests__/EvictionStrategies.test.ts +293 -0
  69. package/src/storage/cache/domain/types/Cache.ts +28 -0
  70. package/src/storage/cache/domain/types/README.md +107 -0
  71. package/src/storage/cache/index.ts +28 -0
  72. package/src/storage/cache/infrastructure/README.md +126 -0
  73. package/src/storage/cache/infrastructure/TTLCache.ts +103 -0
  74. package/src/storage/cache/infrastructure/__tests__/TTLCache.test.ts +303 -0
  75. package/src/storage/cache/presentation/README.md +123 -0
  76. package/src/storage/cache/presentation/__tests__/ReactHooks.test.ts +514 -0
  77. package/src/storage/cache/presentation/useCache.ts +76 -0
  78. package/src/storage/cache/presentation/useCachedValue.ts +88 -0
  79. package/src/storage/cache/types.d.ts +3 -0
  80. package/src/storage/domain/README.md +128 -0
  81. package/src/storage/domain/constants/CacheDefaults.ts +64 -0
  82. package/src/storage/domain/constants/README.md +105 -0
  83. package/src/storage/domain/entities/CachedValue.ts +86 -0
  84. package/src/storage/domain/entities/README.md +109 -0
  85. package/src/storage/domain/entities/StorageResult.ts +75 -0
  86. package/src/storage/domain/entities/__tests__/CachedValue.test.ts +149 -0
  87. package/src/storage/domain/entities/__tests__/StorageResult.test.ts +122 -0
  88. package/src/storage/domain/errors/README.md +126 -0
  89. package/src/storage/domain/errors/StorageError.ts +81 -0
  90. package/src/storage/domain/errors/__tests__/StorageError.test.ts +127 -0
  91. package/src/storage/domain/factories/README.md +138 -0
  92. package/src/storage/domain/factories/StoreFactory.ts +59 -0
  93. package/src/storage/domain/types/README.md +522 -0
  94. package/src/storage/domain/types/Store.ts +44 -0
  95. package/src/storage/domain/utils/CacheKeyGenerator.ts +66 -0
  96. package/src/storage/domain/utils/README.md +127 -0
  97. package/src/storage/domain/utils/__tests__/devUtils.test.ts +97 -0
  98. package/src/storage/domain/utils/devUtils.ts +37 -0
  99. package/src/storage/domain/value-objects/README.md +120 -0
  100. package/src/storage/domain/value-objects/StorageKey.ts +60 -0
  101. package/src/storage/index.ts +175 -0
  102. package/src/storage/infrastructure/README.md +165 -0
  103. package/src/storage/infrastructure/adapters/README.md +175 -0
  104. package/src/storage/infrastructure/adapters/StorageService.md +103 -0
  105. package/src/storage/infrastructure/adapters/StorageService.ts +49 -0
  106. package/src/storage/infrastructure/repositories/AsyncStorageRepository.ts +98 -0
  107. package/src/storage/infrastructure/repositories/BaseStorageOperations.ts +100 -0
  108. package/src/storage/infrastructure/repositories/BatchStorageOperations.ts +42 -0
  109. package/src/storage/infrastructure/repositories/README.md +121 -0
  110. package/src/storage/infrastructure/repositories/StringStorageOperations.ts +44 -0
  111. package/src/storage/infrastructure/repositories/__tests__/AsyncStorageRepository.test.ts +170 -0
  112. package/src/storage/infrastructure/repositories/__tests__/BaseStorageOperations.test.ts +201 -0
  113. package/src/storage/presentation/README.md +181 -0
  114. package/src/storage/presentation/hooks/CacheStorageOperations.ts +94 -0
  115. package/src/storage/presentation/hooks/README.md +128 -0
  116. package/src/storage/presentation/hooks/__tests__/usePersistentCache.test.ts +405 -0
  117. package/src/storage/presentation/hooks/__tests__/useStorage.test.ts +247 -0
  118. package/src/storage/presentation/hooks/__tests__/useStorageState.test.ts +293 -0
  119. package/src/storage/presentation/hooks/useCacheState.ts +53 -0
  120. package/src/storage/presentation/hooks/usePersistentCache.ts +154 -0
  121. package/src/storage/presentation/hooks/useStorage.ts +102 -0
  122. package/src/storage/presentation/hooks/useStorageState.ts +71 -0
  123. package/src/storage/presentation/hooks/useStore.ts +15 -0
  124. package/src/storage/types/README.md +103 -0
  125. package/src/theme/infrastructure/globalThemeStore.ts +1 -1
  126. package/src/theme/infrastructure/storage/ThemeStorage.ts +1 -1
  127. package/src/theme/infrastructure/stores/themeStore.ts +1 -1
  128. package/src/utilities/sharing/infrastructure/services/SharingService.ts +1 -1
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Error Handler for Cache Operations
3
+ */
4
+
5
+ export class CacheError extends Error {
6
+ constructor(message: string, public readonly code: string) {
7
+ super(message);
8
+ this.name = 'CacheError';
9
+ }
10
+ }
11
+
12
+ export class ErrorHandler {
13
+ static handle(error: unknown, context: string): never {
14
+ if (error instanceof CacheError) {
15
+ throw error;
16
+ }
17
+
18
+ if (error instanceof Error) {
19
+ throw new CacheError(`${context}: ${error.message}`, 'CACHE_ERROR');
20
+ }
21
+
22
+ throw new CacheError(`${context}: Unknown error`, 'UNKNOWN_ERROR');
23
+ }
24
+
25
+ static async withTimeout<T>(
26
+ promise: Promise<T>,
27
+ timeoutMs: number,
28
+ context: string
29
+ ): Promise<T> {
30
+ const timeoutPromise = new Promise<never>((_, reject) => {
31
+ setTimeout(() => {
32
+ reject(new CacheError(`${context}: Operation timed out after ${timeoutMs}ms`, 'TIMEOUT'));
33
+ }, timeoutMs);
34
+ });
35
+
36
+ try {
37
+ return await Promise.race([promise, timeoutPromise]);
38
+ } catch (error) {
39
+ this.handle(error, context);
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,122 @@
1
+ # Pattern Matcher
2
+
3
+ Pattern-based cache key matching for bulk operations.
4
+
5
+ ## Overview
6
+
7
+ PatternMatcher provides wildcard-based cache key matching for efficient bulk invalidation and querying operations. Located at `src/cache/domain/PatternMatcher.ts`.
8
+
9
+ ## Strategies
10
+
11
+ ### Pattern Syntax
12
+ - Use `*` wildcard to match any characters (including empty)
13
+ - Use `:` as default separator for structured keys
14
+ - Support multiple wildcards in single pattern
15
+ - Enable regex-compatible patterns for advanced matching
16
+
17
+ ### Performance Optimization
18
+ - Pre-compile frequently used patterns
19
+ - Cache regex objects for repeated use
20
+ - Use pattern indexing for large key sets
21
+ - Implement batch pattern matching for efficiency
22
+
23
+ ### Bulk Operations
24
+ - Use patterns for invalidating multiple keys
25
+ - Filter keys by pattern for querying
26
+ - Extract parameters from matched keys
27
+ - Support hierarchical invalidation
28
+
29
+ ### Pattern Organization
30
+ - Define pattern constants for reuse
31
+ - Document pattern purpose and structure
32
+ - Use consistent naming conventions
33
+ - Group related patterns together
34
+
35
+ ## Restrictions
36
+
37
+ ### Pattern Usage
38
+ - DO NOT use overly broad patterns (e.g., `*`)
39
+ - DO NOT mix different separators in same pattern
40
+ - DO NOT create ambiguous patterns
41
+ - DO NOT use patterns without documenting purpose
42
+
43
+ ### Performance
44
+ - DO NOT compile same pattern repeatedly
45
+ - DO NOT iterate through all keys for pattern matching
46
+ - DO NOT use patterns for single-key operations
47
+ - DO NOT create complex regex patterns unnecessarily
48
+
49
+ ### Pattern Design
50
+ - DO NOT use special regex characters without escaping
51
+ - DO NOT assume pattern separator is always `:`
52
+ - DO NOT create patterns that match unintended keys
53
+ - DO NOT use patterns for exact key matching
54
+
55
+ ### Testing
56
+ - DO NOT skip testing edge cases (empty keys, special characters)
57
+ - DO NOT forget to test non-matching keys
58
+ - DO NOT assume pattern behavior without tests
59
+ - DO NOT use production data in pattern tests
60
+
61
+ ## Rules
62
+
63
+ ### Pattern Syntax
64
+ - MUST use `*` for wildcard matching
65
+ - MUST use `:` as default separator
66
+ - MUST support multiple wildcards per pattern
67
+ - MUST escape special regex characters in patterns
68
+
69
+ ### Method Implementation
70
+ - MUST provide `convertPatternToRegex(pattern: string): RegExp`
71
+ - MUST provide `matchPattern(key: string, pattern: string): boolean`
72
+ - MUST provide `filterKeys(keys: string[], pattern: string): string[]`
73
+ - MUST handle empty pattern strings gracefully
74
+
75
+ ### Pattern Conversion
76
+ - MUST escape special regex characters except `*`
77
+ - MUST replace `*` with `.*` regex equivalent
78
+ - MUST anchor pattern with `^` and `$`
79
+ - MUST return compiled RegExp object
80
+
81
+ ### Matching Logic
82
+ - MUST use RegExp.test() for matching
83
+ - MUST handle empty key strings
84
+ - MUST return boolean for match results
85
+ - MUST be case-sensitive by default
86
+
87
+ ### Filtering Operations
88
+ - MUST return array of matching keys
89
+ - MUST preserve input array order
90
+ - MUST handle empty input arrays
91
+ - MUST not modify input array
92
+
93
+ ### Performance Optimization
94
+ - MUST cache compiled regex objects
95
+ - MUST provide method to clear pattern cache
96
+ - MUST use efficient iteration for large key sets
97
+ - MUST avoid redundant pattern compilation
98
+
99
+ ### Error Handling
100
+ - MUST throw error for invalid patterns
101
+ - MUST handle null/undefined inputs gracefully
102
+ - MUST log pattern compilation errors
103
+ - MUST provide descriptive error messages
104
+
105
+ ### Pattern Documentation
106
+ - MUST document all pattern constants
107
+ - MUST specify pattern structure and purpose
108
+ - MUST provide examples of matching keys
109
+ - MUST warn about performance implications
110
+
111
+ ### Testing Requirements
112
+ - MUST test wildcard matching
113
+ - MUST test multiple wildcards
114
+ - MUST test non-matching keys
115
+ - MUST test edge cases (empty strings, special characters)
116
+ - MUST test pattern extraction
117
+
118
+ ### Integration with Cache
119
+ - MUST work with Cache.invalidatePattern()
120
+ - MUST support bulk operations
121
+ - MUST handle non-existent keys gracefully
122
+ - MUST return count of invalidated keys
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Pattern Matcher for Cache Keys
3
+ */
4
+
5
+ const regexCache = new Map<string, RegExp>();
6
+
7
+ export class PatternMatcher {
8
+ static convertPatternToRegex(pattern: string): RegExp {
9
+ if (regexCache.has(pattern)) {
10
+ return regexCache.get(pattern)!;
11
+ }
12
+
13
+ const escapedPattern = pattern
14
+ .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
15
+ .replace(/\*/g, '.*');
16
+ const regex = new RegExp(`^${escapedPattern}$`);
17
+
18
+ regexCache.set(pattern, regex);
19
+ return regex;
20
+ }
21
+
22
+ static matchesPattern(key: string, pattern: string): boolean {
23
+ const regex = this.convertPatternToRegex(pattern);
24
+ return regex.test(key);
25
+ }
26
+
27
+ static clearCache(): void {
28
+ regexCache.clear();
29
+ }
30
+ }
@@ -0,0 +1,118 @@
1
+ # Cache Domain
2
+
3
+ Core business logic and entities for the cache system.
4
+
5
+ ## Overview
6
+
7
+ Domain layer contains cache entities, managers, error handling, and pattern matching. Located at `src/cache/domain/`.
8
+
9
+ ## Strategies
10
+
11
+ ### Domain Design
12
+ - Implement core cache business logic
13
+ - Define cache entities and value objects
14
+ - Provide error handling and recovery
15
+ - Support pattern-based operations
16
+
17
+ ### Cache Management
18
+ - Use singleton pattern for CacheManager
19
+ - Support multiple named cache instances
20
+ - Provide lifecycle management (create, delete, clear)
21
+ - Track statistics across all caches
22
+
23
+ ### Error Handling
24
+ - Centralize error handling logic
25
+ - Categorize errors by type
26
+ - Provide recovery strategies
27
+ - Log errors for debugging
28
+
29
+ ### Pattern Matching
30
+ - Support wildcard patterns for bulk operations
31
+ - Use consistent separator (`:`)
32
+ - Enable efficient key filtering
33
+ - Support invalidation by pattern
34
+
35
+ ## Restrictions
36
+
37
+ ### Domain Logic
38
+ - DO NOT mix infrastructure concerns in domain
39
+ - DO NOT depend on React or UI libraries
40
+ - DO NOT include I/O operations
41
+ - DO NOT use AsyncStorage directly
42
+
43
+ ### Error Handling
44
+ - DO NOT throw exceptions from domain logic
45
+ - DO NOT ignore error conditions
46
+ - DO NOT lose error context
47
+ - DO NOT create generic error types
48
+
49
+ ### Pattern Matching
50
+ - DO NOT use regex patterns directly (use PatternMatcher)
51
+ - DO NOT mix different separators
52
+ - DO NOT create ambiguous patterns
53
+ - DO NOT use overly broad patterns
54
+
55
+ ### Cache Management
56
+ - DO NOT create unlimited cache instances
57
+ - DO NOT allow duplicate cache names
58
+ - DO NOT leak memory through unreferenced caches
59
+ - DO NOT ignore cache statistics
60
+
61
+ ## Rules
62
+
63
+ ### Cache Entity
64
+ - MUST implement generic Cache<T> class
65
+ - MUST provide CRUD operations (get, set, has, delete)
66
+ - MUST provide clear() operation
67
+ - MUST provide invalidatePattern() operation
68
+ - MUST provide getStats() operation
69
+
70
+ ### CacheManager Singleton
71
+ - MUST provide getCache(name, config?) method
72
+ - MUST return same instance for same name
73
+ - MUST provide deleteCache(name) method
74
+ - MUST provide clearAll() method
75
+ - MUST provide getCacheNames() method
76
+
77
+ ### Error Handling
78
+ - MUST use CacheError base class
79
+ - MUST define specific error types
80
+ - MUST provide error codes
81
+ - MUST preserve error context (key, operation)
82
+
83
+ ### Pattern Matcher
84
+ - MUST provide convertPatternToRegex() method
85
+ - MUST provide matchPattern() method
86
+ - MUST provide filterKeys() method
87
+ - MUST use `*` for wildcard
88
+ - MUST use `:` as default separator
89
+
90
+ ### Statistics Tracking
91
+ - MUST track hits, misses, evictions, expirations
92
+ - MUST calculate hit rate
93
+ - MUST provide getStats() method
94
+ - MUST reset statistics in tests
95
+
96
+ ### Type Safety
97
+ - MUST use generic type parameter <T>
98
+ - MUST enforce type consistency
99
+ - MUST provide type inference
100
+ - MUST avoid `any` types
101
+
102
+ ### Testing Support
103
+ - MUST provide clearAll() for test cleanup
104
+ - MUST reset statistics between tests
105
+ - MUST support mock implementations
106
+ - MUST not have hidden dependencies
107
+
108
+ ### Documentation Rules
109
+ - MUST document all public methods
110
+ - MUST specify parameter types
111
+ - MUST specify return types
112
+ - MUST provide usage examples in comments
113
+
114
+ ### Performance Rules
115
+ - MUST use efficient data structures (Map)
116
+ - MUST provide O(1) access for get/set
117
+ - MUST provide O(n) worst case for pattern operations
118
+ - MUST minimize memory overhead
@@ -0,0 +1,293 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ /**
3
+ * Cache Class Tests
4
+ */
5
+
6
+ import { Cache } from '../Cache';
7
+ import type { CacheConfig } from '../types/Cache';
8
+
9
+ describe('Cache', () => {
10
+ let cache: Cache<string>;
11
+
12
+ beforeEach(() => {
13
+ cache = new Cache<string>({ maxSize: 3, defaultTTL: 1000 });
14
+ });
15
+
16
+ describe('Basic Operations', () => {
17
+ test('should set and get values', () => {
18
+ cache.set('key1', 'value1');
19
+ expect(cache.get('key1')).toBe('value1');
20
+ });
21
+
22
+ test('should return undefined for non-existent keys', () => {
23
+ expect(cache.get('nonexistent')).toBeUndefined();
24
+ });
25
+
26
+ test('should check if key exists', () => {
27
+ cache.set('key1', 'value1');
28
+ expect(cache.has('key1')).toBe(true);
29
+ expect(cache.has('nonexistent')).toBe(false);
30
+ });
31
+
32
+ test('should delete keys', () => {
33
+ cache.set('key1', 'value1');
34
+ expect(cache.delete('key1')).toBe(true);
35
+ expect(cache.has('key1')).toBe(false);
36
+ expect(cache.delete('nonexistent')).toBe(false);
37
+ });
38
+
39
+ test('should clear all keys', () => {
40
+ cache.set('key1', 'value1');
41
+ cache.set('key2', 'value2');
42
+ cache.clear();
43
+ expect(cache.has('key1')).toBe(false);
44
+ expect(cache.has('key2')).toBe(false);
45
+ expect(cache.getStats().size).toBe(0);
46
+ });
47
+
48
+ test('should return all keys', () => {
49
+ cache.set('key1', 'value1');
50
+ cache.set('key2', 'value2');
51
+ const keys = cache.keys();
52
+ expect(keys).toContain('key1');
53
+ expect(keys).toContain('key2');
54
+ expect(keys).toHaveLength(2);
55
+ });
56
+ });
57
+
58
+ describe('TTL (Time To Live)', () => {
59
+ beforeEach(() => {
60
+ jest.useFakeTimers();
61
+ });
62
+
63
+ afterEach(() => {
64
+ jest.useRealTimers();
65
+ });
66
+
67
+ test('should expire entries after TTL', () => {
68
+ cache.set('key1', 'value1', 1000);
69
+ expect(cache.get('key1')).toBe('value1');
70
+
71
+ jest.advanceTimersByTime(1001);
72
+ expect(cache.get('key1')).toBeUndefined();
73
+ });
74
+
75
+ test('should use default TTL when not specified', () => {
76
+ cache.set('key1', 'value1');
77
+ expect(cache.get('key1')).toBe('value1');
78
+
79
+ jest.advanceTimersByTime(1001);
80
+ expect(cache.get('key1')).toBeUndefined();
81
+ });
82
+
83
+ test('should call onExpire callback when entry expires', () => {
84
+ const onExpire = jest.fn();
85
+ const cacheWithCallback = new Cache<string>({
86
+ defaultTTL: 1000,
87
+ onExpire
88
+ });
89
+
90
+ cacheWithCallback.set('key1', 'value1');
91
+ jest.advanceTimersByTime(1001);
92
+ cacheWithCallback.get('key1'); // Trigger expiration check
93
+
94
+ expect(onExpire).toHaveBeenCalledWith('key1', expect.objectContaining({
95
+ value: 'value1',
96
+ timestamp: expect.any(Number),
97
+ ttl: 1000,
98
+ accessCount: 0,
99
+ lastAccess: expect.any(Number)
100
+ }));
101
+ });
102
+ });
103
+
104
+ describe('Eviction', () => {
105
+ test('should evict LRU when cache is full', () => {
106
+ const onEvict = jest.fn();
107
+ const cacheWithCallback = new Cache<string>({
108
+ maxSize: 2,
109
+ onEvict
110
+ });
111
+
112
+ cacheWithCallback.set('key1', 'value1');
113
+ cacheWithCallback.set('key2', 'value2');
114
+
115
+ // Access key1 to make it recently used
116
+ cacheWithCallback.get('key1');
117
+
118
+ // Add third item, should evict key2 (least recently used)
119
+ cacheWithCallback.set('key3', 'value3');
120
+
121
+ expect(cacheWithCallback.has('key1')).toBe(true);
122
+ expect(cacheWithCallback.has('key2')).toBe(false);
123
+ expect(cacheWithCallback.has('key3')).toBe(true);
124
+ expect(onEvict).toHaveBeenCalledWith('key2', expect.any(Object));
125
+ });
126
+
127
+ test('should not evict when updating existing key', () => {
128
+ cache.set('key1', 'value1');
129
+ cache.set('key2', 'value2');
130
+ cache.set('key3', 'value3');
131
+
132
+ // Update existing key, should not cause eviction
133
+ cache.set('key1', 'value1-updated');
134
+
135
+ expect(cache.has('key1')).toBe(true);
136
+ expect(cache.has('key2')).toBe(true);
137
+ expect(cache.has('key3')).toBe(true);
138
+ expect(cache.get('key1')).toBe('value1-updated');
139
+ });
140
+ });
141
+
142
+ describe('Pattern Invalidation', () => {
143
+ test('should invalidate keys matching pattern', () => {
144
+ cache.set('user:1', 'user1');
145
+ cache.set('user:2', 'user2');
146
+ cache.set('post:1', 'post1');
147
+ cache.set('admin', 'admin');
148
+
149
+ const invalidatedCount = cache.invalidatePattern('user:*');
150
+
151
+ expect(invalidatedCount).toBe(2);
152
+ expect(cache.has('user:1')).toBe(false);
153
+ expect(cache.has('user:2')).toBe(false);
154
+ expect(cache.has('post:1')).toBe(true);
155
+ expect(cache.has('admin')).toBe(true);
156
+ });
157
+
158
+ test('should handle complex patterns', () => {
159
+ cache.set('user:1:profile', 'profile1');
160
+ cache.set('user:2:profile', 'profile2');
161
+ cache.set('user:1:settings', 'settings1');
162
+ cache.set('post:1', 'post1');
163
+
164
+ const invalidatedCount = cache.invalidatePattern('user:*:profile');
165
+
166
+ expect(invalidatedCount).toBe(2);
167
+ expect(cache.has('user:1:profile')).toBe(false);
168
+ expect(cache.has('user:2:profile')).toBe(false);
169
+ expect(cache.has('user:1:settings')).toBe(true);
170
+ expect(cache.has('post:1')).toBe(true);
171
+ });
172
+
173
+ test('should return 0 for non-matching patterns', () => {
174
+ cache.set('user:1', 'user1');
175
+ cache.set('post:1', 'post1');
176
+
177
+ const invalidatedCount = cache.invalidatePattern('admin:*');
178
+
179
+ expect(invalidatedCount).toBe(0);
180
+ expect(cache.has('user:1')).toBe(true);
181
+ expect(cache.has('post:1')).toBe(true);
182
+ });
183
+ });
184
+
185
+ describe('Statistics', () => {
186
+ test('should track cache statistics correctly', () => {
187
+ // Initial stats
188
+ let stats = cache.getStats();
189
+ expect(stats.size).toBe(0);
190
+ expect(stats.hits).toBe(0);
191
+ expect(stats.misses).toBe(0);
192
+ expect(stats.evictions).toBe(0);
193
+ expect(stats.expirations).toBe(0);
194
+
195
+ // Set some values
196
+ cache.set('key1', 'value1');
197
+ cache.set('key2', 'value2');
198
+
199
+ stats = cache.getStats();
200
+ expect(stats.size).toBe(2);
201
+
202
+ // Hit
203
+ cache.get('key1');
204
+ stats = cache.getStats();
205
+ expect(stats.hits).toBe(1);
206
+ expect(stats.misses).toBe(0);
207
+
208
+ // Miss
209
+ cache.get('nonexistent');
210
+ stats = cache.getStats();
211
+ expect(stats.hits).toBe(1);
212
+ expect(stats.misses).toBe(1);
213
+
214
+ // Delete
215
+ cache.delete('key1');
216
+ stats = cache.getStats();
217
+ expect(stats.size).toBe(1);
218
+
219
+ // Clear
220
+ cache.clear();
221
+ stats = cache.getStats();
222
+ expect(stats.size).toBe(0);
223
+ expect(stats.hits).toBe(0);
224
+ expect(stats.misses).toBe(0);
225
+ });
226
+
227
+ test('should return immutable stats object', () => {
228
+ cache.set('key1', 'value1');
229
+ const stats1 = cache.getStats();
230
+ const stats2 = cache.getStats();
231
+
232
+ expect(stats1).toEqual(stats2);
233
+ expect(stats1).not.toBe(stats2); // Different references
234
+ });
235
+ });
236
+
237
+ describe('Access Count and Last Access', () => {
238
+ test('should track access count and last access time', () => {
239
+ const startTime = Date.now();
240
+
241
+ cache.set('key1', 'value1');
242
+
243
+ // First access
244
+ cache.get('key1');
245
+ let stats = cache.getStats();
246
+
247
+ // Second access
248
+ jest.advanceTimersByTime(100);
249
+ cache.get('key1');
250
+
251
+ // Verify access tracking (internal implementation detail)
252
+ expect(cache.get('key1')).toBe('value1');
253
+ });
254
+ });
255
+
256
+ describe('Configuration', () => {
257
+ test('should use custom configuration', () => {
258
+ const customConfig: CacheConfig = {
259
+ maxSize: 10,
260
+ defaultTTL: 5000,
261
+ onEvict: jest.fn(),
262
+ onExpire: jest.fn(),
263
+ };
264
+
265
+ const customCache = new Cache<string>(customConfig);
266
+
267
+ customCache.set('key1', 'value1');
268
+ expect(customCache.get('key1')).toBe('value1');
269
+
270
+ // Should not expire before custom TTL
271
+ jest.advanceTimersByTime(4000);
272
+ expect(customCache.get('key1')).toBe('value1');
273
+
274
+ // Should expire after custom TTL
275
+ jest.advanceTimersByTime(1001);
276
+ expect(customCache.get('key1')).toBeUndefined();
277
+ });
278
+
279
+ test('should use default configuration when not provided', () => {
280
+ const defaultCache = new Cache<string>();
281
+
282
+ defaultCache.set('key1', 'value1');
283
+ expect(defaultCache.get('key1')).toBe('value1');
284
+
285
+ // Should use default TTL (5 minutes)
286
+ jest.advanceTimersByTime(5 * 60 * 1000 - 1);
287
+ expect(defaultCache.get('key1')).toBe('value1');
288
+
289
+ jest.advanceTimersByTime(2);
290
+ expect(defaultCache.get('key1')).toBeUndefined();
291
+ });
292
+ });
293
+ });