@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,201 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/**
|
|
3
|
+
* BaseStorageOperations Tests
|
|
4
|
+
*
|
|
5
|
+
* Unit tests for BaseStorageOperations
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { BaseStorageOperations } from '../BaseStorageOperations';
|
|
9
|
+
import { AsyncStorage } from '../../__tests__/mocks/asyncStorage.mock';
|
|
10
|
+
import { StorageReadError, StorageWriteError, StorageDeleteError } from '../../../domain/errors/StorageError';
|
|
11
|
+
|
|
12
|
+
describe('BaseStorageOperations', () => {
|
|
13
|
+
let baseOps: BaseStorageOperations;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
baseOps = new BaseStorageOperations();
|
|
17
|
+
(AsyncStorage as any).__clear();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('getItem', () => {
|
|
21
|
+
it('should get item successfully', async () => {
|
|
22
|
+
const key = 'test-key';
|
|
23
|
+
const value = { test: 'value' };
|
|
24
|
+
const defaultValue = { default: true };
|
|
25
|
+
|
|
26
|
+
// Setup
|
|
27
|
+
await AsyncStorage.setItem(key, JSON.stringify(value));
|
|
28
|
+
|
|
29
|
+
// Test
|
|
30
|
+
const result = await baseOps.getItem(key, defaultValue);
|
|
31
|
+
|
|
32
|
+
expect(result.success).toBe(true);
|
|
33
|
+
expect(result.data).toEqual(value);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should return default value for missing key', async () => {
|
|
37
|
+
const key = 'missing-key';
|
|
38
|
+
const defaultValue = { default: true };
|
|
39
|
+
|
|
40
|
+
const result = await baseOps.getItem(key, defaultValue);
|
|
41
|
+
|
|
42
|
+
expect(result.success).toBe(true);
|
|
43
|
+
expect(result.data).toBe(defaultValue);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should handle deserialization error', async () => {
|
|
47
|
+
const key = 'invalid-json-key';
|
|
48
|
+
const defaultValue = { default: true };
|
|
49
|
+
|
|
50
|
+
// Setup invalid JSON
|
|
51
|
+
await AsyncStorage.setItem(key, 'invalid-json');
|
|
52
|
+
|
|
53
|
+
const result = await baseOps.getItem(key, defaultValue);
|
|
54
|
+
|
|
55
|
+
expect(result.success).toBe(false);
|
|
56
|
+
expect(result.data).toBe(defaultValue);
|
|
57
|
+
expect(result.error).toBeInstanceOf(StorageReadError);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should handle storage read error', async () => {
|
|
61
|
+
const key = 'test-key';
|
|
62
|
+
const defaultValue = { default: true };
|
|
63
|
+
|
|
64
|
+
// Mock storage error
|
|
65
|
+
(AsyncStorage.getItem as jest.Mock).mockRejectedValue(new Error('Storage error'));
|
|
66
|
+
|
|
67
|
+
const result = await baseOps.getItem(key, defaultValue);
|
|
68
|
+
|
|
69
|
+
expect(result.success).toBe(false);
|
|
70
|
+
expect(result.data).toBe(defaultValue);
|
|
71
|
+
expect(result.error).toBeInstanceOf(StorageReadError);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('setItem', () => {
|
|
76
|
+
it('should set item successfully', async () => {
|
|
77
|
+
const key = 'test-key';
|
|
78
|
+
const value = { test: 'value' };
|
|
79
|
+
|
|
80
|
+
const result = await baseOps.setItem(key, value);
|
|
81
|
+
|
|
82
|
+
expect(result.success).toBe(true);
|
|
83
|
+
expect(result.data).toEqual(value);
|
|
84
|
+
|
|
85
|
+
// Verify storage
|
|
86
|
+
const stored = await AsyncStorage.getItem(key);
|
|
87
|
+
expect(JSON.parse(stored!)).toEqual(value);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should handle serialization error', async () => {
|
|
91
|
+
const key = 'test-key';
|
|
92
|
+
const value = { circular: {} };
|
|
93
|
+
value.circular = value; // Create circular reference
|
|
94
|
+
|
|
95
|
+
const result = await baseOps.setItem(key, value);
|
|
96
|
+
|
|
97
|
+
expect(result.success).toBe(false);
|
|
98
|
+
expect(result.error).toBeInstanceOf(StorageWriteError);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should handle storage write error', async () => {
|
|
102
|
+
const key = 'test-key';
|
|
103
|
+
const value = { test: 'value' };
|
|
104
|
+
|
|
105
|
+
// Mock storage error
|
|
106
|
+
(AsyncStorage.setItem as jest.Mock).mockRejectedValue(new Error('Storage error'));
|
|
107
|
+
|
|
108
|
+
const result = await baseOps.setItem(key, value);
|
|
109
|
+
|
|
110
|
+
expect(result.success).toBe(false);
|
|
111
|
+
expect(result.error).toBeInstanceOf(StorageWriteError);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('removeItem', () => {
|
|
116
|
+
it('should remove item successfully', async () => {
|
|
117
|
+
const key = 'test-key';
|
|
118
|
+
|
|
119
|
+
// Setup
|
|
120
|
+
await AsyncStorage.setItem(key, 'test-value');
|
|
121
|
+
|
|
122
|
+
const result = await baseOps.removeItem(key);
|
|
123
|
+
|
|
124
|
+
expect(result.success).toBe(true);
|
|
125
|
+
|
|
126
|
+
// Verify removal
|
|
127
|
+
const stored = await AsyncStorage.getItem(key);
|
|
128
|
+
expect(stored).toBeNull();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should handle storage delete error', async () => {
|
|
132
|
+
const key = 'test-key';
|
|
133
|
+
|
|
134
|
+
// Mock storage error
|
|
135
|
+
(AsyncStorage.removeItem as jest.Mock).mockRejectedValue(new Error('Storage error'));
|
|
136
|
+
|
|
137
|
+
const result = await baseOps.removeItem(key);
|
|
138
|
+
|
|
139
|
+
expect(result.success).toBe(false);
|
|
140
|
+
expect(result.error).toBeInstanceOf(StorageDeleteError);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('hasItem', () => {
|
|
145
|
+
it('should return true for existing item', async () => {
|
|
146
|
+
const key = 'test-key';
|
|
147
|
+
|
|
148
|
+
// Setup
|
|
149
|
+
await AsyncStorage.setItem(key, 'test-value');
|
|
150
|
+
|
|
151
|
+
const exists = await baseOps.hasItem(key);
|
|
152
|
+
|
|
153
|
+
expect(exists).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should return false for missing item', async () => {
|
|
157
|
+
const key = 'missing-key';
|
|
158
|
+
|
|
159
|
+
const exists = await baseOps.hasItem(key);
|
|
160
|
+
|
|
161
|
+
expect(exists).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should return false on storage error', async () => {
|
|
165
|
+
const key = 'test-key';
|
|
166
|
+
|
|
167
|
+
// Mock storage error
|
|
168
|
+
(AsyncStorage.getItem as jest.Mock).mockRejectedValue(new Error('Storage error'));
|
|
169
|
+
|
|
170
|
+
const exists = await baseOps.hasItem(key);
|
|
171
|
+
|
|
172
|
+
expect(exists).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('clearAll', () => {
|
|
177
|
+
it('should clear all storage successfully', async () => {
|
|
178
|
+
// Setup
|
|
179
|
+
await AsyncStorage.setItem('key1', 'value1');
|
|
180
|
+
await AsyncStorage.setItem('key2', 'value2');
|
|
181
|
+
|
|
182
|
+
const result = await baseOps.clearAll();
|
|
183
|
+
|
|
184
|
+
expect(result.success).toBe(true);
|
|
185
|
+
|
|
186
|
+
// Verify clear
|
|
187
|
+
const keys = await AsyncStorage.getAllKeys();
|
|
188
|
+
expect(keys).toHaveLength(0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should handle storage clear error', async () => {
|
|
192
|
+
// Mock storage error
|
|
193
|
+
(AsyncStorage.clear as jest.Mock).mockRejectedValue(new Error('Storage error'));
|
|
194
|
+
|
|
195
|
+
const result = await baseOps.clearAll();
|
|
196
|
+
|
|
197
|
+
expect(result.success).toBe(false);
|
|
198
|
+
expect(result.error).toBeInstanceOf(StorageDeleteError);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Presentation Layer
|
|
2
|
+
|
|
3
|
+
React hooks and components for UI integration with storage and cache.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Presentation layer containing React hooks for storage and cache operations. Located at `src/presentation/`.
|
|
8
|
+
|
|
9
|
+
## Directory Structure
|
|
10
|
+
|
|
11
|
+
- `hooks/` - React hooks for storage and cache integration
|
|
12
|
+
|
|
13
|
+
## Strategies
|
|
14
|
+
|
|
15
|
+
### React Integration
|
|
16
|
+
- Use custom hooks for storage operations
|
|
17
|
+
- Use custom hooks for cache operations
|
|
18
|
+
- Enable reactive state management
|
|
19
|
+
- Support automatic re-renders on state changes
|
|
20
|
+
|
|
21
|
+
### State Management
|
|
22
|
+
- Use useStorageState for persistent state
|
|
23
|
+
- Use useCacheState for cached state
|
|
24
|
+
- Enable automatic sync with storage/cache
|
|
25
|
+
- Support default values and initialization
|
|
26
|
+
|
|
27
|
+
### Cache Integration
|
|
28
|
+
- Use useCache for cache instance access
|
|
29
|
+
- Use useCachedValue for single value caching
|
|
30
|
+
- Use usePersistentCache for hybrid cache+storage
|
|
31
|
+
- Enable TTL-based invalidation
|
|
32
|
+
|
|
33
|
+
### Hook Design
|
|
34
|
+
- Follow React hooks rules and best practices
|
|
35
|
+
- Enable composition and reuse
|
|
36
|
+
- Support dependency arrays for optimization
|
|
37
|
+
- Provide cleanup functions for side effects
|
|
38
|
+
|
|
39
|
+
## Restrictions
|
|
40
|
+
|
|
41
|
+
### Hook Usage
|
|
42
|
+
- DO NOT call hooks outside React components
|
|
43
|
+
- DO NOT call hooks conditionally or in loops
|
|
44
|
+
- DO NOT mutate state directly
|
|
45
|
+
- DO NOT ignore loading and error states
|
|
46
|
+
|
|
47
|
+
### State Management
|
|
48
|
+
- DO NOT mix storage and cache state unnecessarily
|
|
49
|
+
- DO NOT create redundant state for same data
|
|
50
|
+
- DO NOT forget to handle undefined/null states
|
|
51
|
+
- DO NOT use stale state in closures
|
|
52
|
+
|
|
53
|
+
### Performance
|
|
54
|
+
- DO NOT create new cache instances on every render
|
|
55
|
+
- DO NOT skip dependency arrays in useEffect
|
|
56
|
+
- DO NOT cause unnecessary re-renders
|
|
57
|
+
- DO NOT forget cleanup in useEffect
|
|
58
|
+
|
|
59
|
+
### Error Handling
|
|
60
|
+
- DO NOT ignore errors from async operations
|
|
61
|
+
- DO NOT expose raw error objects to UI
|
|
62
|
+
- DO NOT retry indefinitely on failures
|
|
63
|
+
- DO NOT lose error context
|
|
64
|
+
|
|
65
|
+
## Rules
|
|
66
|
+
|
|
67
|
+
### Hook Implementation
|
|
68
|
+
- MUST follow React hooks rules
|
|
69
|
+
- MUST use useMemo for expensive computations
|
|
70
|
+
- MUST use useCallback for stable function references
|
|
71
|
+
- MUST provide cleanup functions for effects
|
|
72
|
+
- MUST support dependency arrays
|
|
73
|
+
|
|
74
|
+
### Storage Hooks
|
|
75
|
+
- MUST provide useStorage for low-level operations
|
|
76
|
+
- MUST provide useStorageState for state sync
|
|
77
|
+
- MUST support generic type parameters
|
|
78
|
+
- MUST handle loading states
|
|
79
|
+
- MUST handle error states
|
|
80
|
+
- MUST return stable references
|
|
81
|
+
|
|
82
|
+
### Cache Hooks
|
|
83
|
+
- MUST provide useCache for cache instance access
|
|
84
|
+
- MUST provide useCachedValue for value caching
|
|
85
|
+
- MUST provide useCacheState for state integration
|
|
86
|
+
- MUST support TTL configuration
|
|
87
|
+
- MUST support invalidation callbacks
|
|
88
|
+
- MUST handle cache misses gracefully
|
|
89
|
+
|
|
90
|
+
### Persistent Cache Hooks
|
|
91
|
+
- MUST provide usePersistentCache for hybrid caching
|
|
92
|
+
- MUST support storage fallback
|
|
93
|
+
- MUST handle rehydration from storage
|
|
94
|
+
- MUST support stale-while-revalidate strategy
|
|
95
|
+
- MUST provide loading, error, and data states
|
|
96
|
+
- MUST support refresh callbacks
|
|
97
|
+
|
|
98
|
+
### State Synchronization
|
|
99
|
+
- MUST sync state changes to storage/cache
|
|
100
|
+
- MUST re-render on state changes
|
|
101
|
+
- MUST provide latest state to consumers
|
|
102
|
+
- MUST handle race conditions properly
|
|
103
|
+
- MUST support optimistic updates
|
|
104
|
+
|
|
105
|
+
### Error Handling
|
|
106
|
+
- MUST expose error state from hooks
|
|
107
|
+
- MUST enable error callbacks
|
|
108
|
+
- MUST not throw exceptions from hooks
|
|
109
|
+
- MUST preserve error context
|
|
110
|
+
- MUST support error recovery
|
|
111
|
+
|
|
112
|
+
### Loading States
|
|
113
|
+
- MUST expose loading state from async hooks
|
|
114
|
+
- MUST indicate initial load vs refresh
|
|
115
|
+
- MUST support loading callbacks
|
|
116
|
+
- MUST prevent duplicate requests
|
|
117
|
+
- MUST handle concurrent operations
|
|
118
|
+
|
|
119
|
+
### Type Safety
|
|
120
|
+
- MUST use generic type parameters
|
|
121
|
+
- MUST infer types from default values
|
|
122
|
+
- MUST provide type-safe return values
|
|
123
|
+
- MUST support custom type definitions
|
|
124
|
+
- MUST not use type assertions
|
|
125
|
+
|
|
126
|
+
### Performance Optimization
|
|
127
|
+
- MUST use useMemo for cache instances
|
|
128
|
+
- MUST use useCallback for event handlers
|
|
129
|
+
- MUST provide dependency array parameters
|
|
130
|
+
- MUST prevent unnecessary re-renders
|
|
131
|
+
- MUST enable memoization where appropriate
|
|
132
|
+
|
|
133
|
+
### Cleanup and Lifecycle
|
|
134
|
+
- MUST clean up subscriptions on unmount
|
|
135
|
+
- MUST clear temporary cache on unmount
|
|
136
|
+
- MUST prevent memory leaks
|
|
137
|
+
- MUST handle component unmount gracefully
|
|
138
|
+
- MUST not update state after unmount
|
|
139
|
+
|
|
140
|
+
### Hook Naming
|
|
141
|
+
- MUST use 'use' prefix for all hooks
|
|
142
|
+
- MUST use descriptive names for functionality
|
|
143
|
+
- MUST follow naming conventions
|
|
144
|
+
- MUST be consistent with ecosystem
|
|
145
|
+
- MUST indicate purpose in name
|
|
146
|
+
|
|
147
|
+
### Cache Naming
|
|
148
|
+
- MUST use descriptive cache names
|
|
149
|
+
- MUST avoid generic cache names
|
|
150
|
+
- MUST enable pattern matching for invalidation
|
|
151
|
+
- MUST follow naming conventions
|
|
152
|
+
- MUST prevent naming conflicts
|
|
153
|
+
|
|
154
|
+
### Testing Support
|
|
155
|
+
- MUST enable hook testing with renderHook
|
|
156
|
+
- MUST provide cleanup for testing
|
|
157
|
+
- MUST support mocking dependencies
|
|
158
|
+
- MUST reset state between tests
|
|
159
|
+
- MUST not have hidden side effects
|
|
160
|
+
|
|
161
|
+
### Export Rules
|
|
162
|
+
- MUST export all hooks from presentation layer
|
|
163
|
+
- MUST provide TypeScript types
|
|
164
|
+
- MUST document hook behavior
|
|
165
|
+
- MUST specify hook contracts
|
|
166
|
+
- MUST not export internal utilities
|
|
167
|
+
|
|
168
|
+
### Documentation
|
|
169
|
+
- MUST document all public hooks
|
|
170
|
+
- MUST specify parameter types and requirements
|
|
171
|
+
- MUST explain return value structure
|
|
172
|
+
- MUST provide usage guidance
|
|
173
|
+
- MUST warn about common mistakes
|
|
174
|
+
- MUST not include code examples
|
|
175
|
+
|
|
176
|
+
### Best Practices Compliance
|
|
177
|
+
- MUST follow React hooks rules
|
|
178
|
+
- MUST enable proper dependency tracking
|
|
179
|
+
- MUST support concurrent mode
|
|
180
|
+
- MUST be compatible with Strict Mode
|
|
181
|
+
- MUST not cause memory leaks
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Storage Operations
|
|
3
|
+
*
|
|
4
|
+
* Handles cache storage operations following Single Responsibility Principle
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { storageRepository } from '../../infrastructure/repositories/AsyncStorageRepository';
|
|
8
|
+
import type { CachedValue } from '../../domain/entities/CachedValue';
|
|
9
|
+
import { createCachedValue } from '../../domain/entities/CachedValue';
|
|
10
|
+
import { devWarn } from '../../domain/utils/devUtils';
|
|
11
|
+
|
|
12
|
+
export interface CacheStorageOptions {
|
|
13
|
+
ttl?: number;
|
|
14
|
+
version?: number;
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Handles cache storage operations with proper error handling and memory management
|
|
20
|
+
*/
|
|
21
|
+
export class CacheStorageOperations {
|
|
22
|
+
private static instance: CacheStorageOperations | null = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Singleton pattern to prevent memory leaks
|
|
26
|
+
*/
|
|
27
|
+
static getInstance(): CacheStorageOperations {
|
|
28
|
+
if (!CacheStorageOperations.instance) {
|
|
29
|
+
CacheStorageOperations.instance = new CacheStorageOperations();
|
|
30
|
+
}
|
|
31
|
+
return CacheStorageOperations.instance;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Reset singleton instance (useful for testing)
|
|
36
|
+
*/
|
|
37
|
+
static resetInstance(): void {
|
|
38
|
+
CacheStorageOperations.instance = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Load cached data from storage
|
|
43
|
+
*/
|
|
44
|
+
async loadFromStorage<T>(
|
|
45
|
+
key: string
|
|
46
|
+
): Promise<CachedValue<T> | null> {
|
|
47
|
+
try {
|
|
48
|
+
const result = await storageRepository.getString(key, '');
|
|
49
|
+
|
|
50
|
+
if (result.success && result.data) {
|
|
51
|
+
const cached = JSON.parse(result.data) as CachedValue<T>;
|
|
52
|
+
return cached;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
devWarn(`CacheStorageOperations: Failed to load cache for key "${key}"`, error);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Save data to cache storage
|
|
64
|
+
*/
|
|
65
|
+
async saveToStorage<T>(
|
|
66
|
+
key: string,
|
|
67
|
+
value: T,
|
|
68
|
+
options: CacheStorageOptions = {}
|
|
69
|
+
): Promise<void> {
|
|
70
|
+
const { ttl, version, enabled = true } = options;
|
|
71
|
+
|
|
72
|
+
if (!enabled) return;
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const cached = createCachedValue(value, ttl || 0, version);
|
|
76
|
+
await storageRepository.setString(key, JSON.stringify(cached));
|
|
77
|
+
} catch (error) {
|
|
78
|
+
devWarn(`CacheStorageOperations: Failed to save cache for key "${key}"`, error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Clear cached data from storage
|
|
84
|
+
*/
|
|
85
|
+
async clearFromStorage(key: string, enabled = true): Promise<void> {
|
|
86
|
+
if (!enabled) return;
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
await storageRepository.removeItem(key);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
devWarn(`CacheStorageOperations: Failed to clear cache for key "${key}"`, error);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# React Hooks
|
|
2
|
+
|
|
3
|
+
Integration layer for storage and cache functionality with React components.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains React hooks that integrate storage and cache functionality with React components. Located at `src/presentation/hooks/`.
|
|
8
|
+
|
|
9
|
+
## Available Hooks
|
|
10
|
+
|
|
11
|
+
| Hook | Purpose | Use Case |
|
|
12
|
+
|------|---------|----------|
|
|
13
|
+
| `useStorage` | Low-level storage operations | Manual CRUD operations |
|
|
14
|
+
| `useStorageState` | State with auto-persistence | Form data, user preferences |
|
|
15
|
+
| `useStore` | Zustand store selector | State management |
|
|
16
|
+
| `usePersistentCache` | API caching with persistence | Data fetching with offline support |
|
|
17
|
+
| `useCacheState` | In-memory cache state | Temporary state management |
|
|
18
|
+
| `useCache` | In-memory cache operations | Manual cache management |
|
|
19
|
+
| `useCachedValue` | Single value caching | Simple value caching |
|
|
20
|
+
|
|
21
|
+
## Strategies
|
|
22
|
+
|
|
23
|
+
### Hook Selection
|
|
24
|
+
- Use `useStorageState` for form inputs and settings
|
|
25
|
+
- Use `useStore` for Zustand integration
|
|
26
|
+
- Use `usePersistentCache` for API data with offline support
|
|
27
|
+
- Use `useCache` for complex caching patterns
|
|
28
|
+
- Use `useCachedValue` for simple single-value caching
|
|
29
|
+
|
|
30
|
+
### Performance Optimization
|
|
31
|
+
- Use selectors with `useStore` to prevent unnecessary re-renders
|
|
32
|
+
- Memoize callbacks passed to cache hooks
|
|
33
|
+
- Avoid inline fetcher functions
|
|
34
|
+
- Use appropriate TTL based on data change frequency
|
|
35
|
+
|
|
36
|
+
### Composition Patterns
|
|
37
|
+
- Combine multiple hooks for complex scenarios
|
|
38
|
+
- Use conditional fetching with enabled flag
|
|
39
|
+
- Implement dependent queries with proper data flow
|
|
40
|
+
- Share cache instances across components
|
|
41
|
+
|
|
42
|
+
## Restrictions
|
|
43
|
+
|
|
44
|
+
### Hook Usage
|
|
45
|
+
- DO NOT call hooks outside React components or custom hooks
|
|
46
|
+
- DO NOT call hooks conditionally or inside loops
|
|
47
|
+
- DO NOT use `useStorage` when `useStorageState` is sufficient
|
|
48
|
+
- DO NOT use inline fetcher functions (define them first)
|
|
49
|
+
|
|
50
|
+
### State Management
|
|
51
|
+
- DO NOT mix storage and cache for same data
|
|
52
|
+
- DO NOT use `useStorageState` for large datasets (> 1MB)
|
|
53
|
+
- DO NOT use `useCache` when simple state would suffice
|
|
54
|
+
- DO NOT create new cache instances on every render
|
|
55
|
+
|
|
56
|
+
### Performance
|
|
57
|
+
- DO NOT select entire store with `useStore` (use selectors)
|
|
58
|
+
- DO NOT recreate fetcher functions on every render
|
|
59
|
+
- DO NOT use very short TTLs (< 5 seconds)
|
|
60
|
+
- DO NOT cache large objects (> 100KB)
|
|
61
|
+
|
|
62
|
+
### Error Handling
|
|
63
|
+
- DO NOT ignore error states from cache hooks
|
|
64
|
+
- DO NOT silently fail storage operations
|
|
65
|
+
- DO NOT use try-catch inside hooks (handle errors at call site)
|
|
66
|
+
|
|
67
|
+
## Rules
|
|
68
|
+
|
|
69
|
+
### Hook Implementation
|
|
70
|
+
- MUST follow React rules of hooks
|
|
71
|
+
- MUST provide TypeScript types for all parameters
|
|
72
|
+
- MUST handle loading states appropriately
|
|
73
|
+
- MUST provide error state for async operations
|
|
74
|
+
|
|
75
|
+
### Storage Hooks (`useStorage`, `useStorageState`)
|
|
76
|
+
- MUST use unique keys for different state
|
|
77
|
+
- MUST specify initial value for `useStorageState`
|
|
78
|
+
- MUST handle storage errors gracefully
|
|
79
|
+
- MUST provide type parameter for type safety
|
|
80
|
+
|
|
81
|
+
### Cache Hooks (`useCache`, `useCacheState`, `useCachedValue`)
|
|
82
|
+
- MUST specify cache name when using `useCache`
|
|
83
|
+
- MUST provide fetcher function for `useCachedValue`
|
|
84
|
+
- MUST set appropriate TTL for data type
|
|
85
|
+
- MUST handle cache misses gracefully
|
|
86
|
+
|
|
87
|
+
### Persistent Cache (`usePersistentCache`)
|
|
88
|
+
- MUST provide unique key for each query
|
|
89
|
+
- MUST define fetcher function that returns Promise
|
|
90
|
+
- MUST specify TTL based on data freshness requirements
|
|
91
|
+
- MUST handle loading and error states
|
|
92
|
+
- MUST implement refresh mechanism
|
|
93
|
+
|
|
94
|
+
### Store Hook (`useStore`)
|
|
95
|
+
- MUST provide selector function for partial state
|
|
96
|
+
- MUST use memoized selectors to prevent re-renders
|
|
97
|
+
- MUST select only needed state slices
|
|
98
|
+
- MUST avoid selecting entire store
|
|
99
|
+
|
|
100
|
+
### Testing
|
|
101
|
+
- MUST test hooks with `@testing-library/react-hooks`
|
|
102
|
+
- MUST test loading, success, and error states
|
|
103
|
+
- MUST test cleanup and unmount behavior
|
|
104
|
+
- MUST mock storage/cache in tests
|
|
105
|
+
|
|
106
|
+
### Error Handling
|
|
107
|
+
- MUST expose error state from async hooks
|
|
108
|
+
- MUST allow error handling at component level
|
|
109
|
+
- MUST log errors in development mode
|
|
110
|
+
- MUST provide retry mechanisms for transient failures
|
|
111
|
+
|
|
112
|
+
### Type Safety
|
|
113
|
+
- MUST provide generic type parameters
|
|
114
|
+
- MUST enforce types for storage keys
|
|
115
|
+
- MUST use TypeScript strict mode
|
|
116
|
+
- MUST avoid `any` types in hook implementations
|
|
117
|
+
|
|
118
|
+
### Performance
|
|
119
|
+
- MUST use `useCallback` for functions passed to hooks
|
|
120
|
+
- MUST use `useMemo` for computed values
|
|
121
|
+
- MUST implement proper dependency arrays
|
|
122
|
+
- MUST avoid unnecessary re-renders
|
|
123
|
+
|
|
124
|
+
### Deprecation
|
|
125
|
+
- MUST document deprecated hooks
|
|
126
|
+
- MUST provide migration path for deprecated hooks
|
|
127
|
+
- MUST maintain backward compatibility when possible
|
|
128
|
+
- MUST remove deprecated hooks after major version bump
|