@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,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
|
+
});
|