@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,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AsyncStorage Repository
|
|
3
|
+
*
|
|
4
|
+
* Domain-Driven Design: Infrastructure implementation of IStorageRepository
|
|
5
|
+
* Adapts React Native AsyncStorage to domain interface using composition
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { IStorageRepository } from '../../application/ports/IStorageRepository';
|
|
9
|
+
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
10
|
+
import { BaseStorageOperations } from './BaseStorageOperations';
|
|
11
|
+
import { StringStorageOperations } from './StringStorageOperations';
|
|
12
|
+
import { BatchStorageOperations } from './BatchStorageOperations';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* AsyncStorage Repository Implementation
|
|
16
|
+
* Uses composition to follow Single Responsibility Principle
|
|
17
|
+
*/
|
|
18
|
+
export class AsyncStorageRepository implements IStorageRepository {
|
|
19
|
+
private baseOps: BaseStorageOperations;
|
|
20
|
+
private stringOps: StringStorageOperations;
|
|
21
|
+
private batchOps: BatchStorageOperations;
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
this.baseOps = new BaseStorageOperations();
|
|
25
|
+
this.stringOps = new StringStorageOperations();
|
|
26
|
+
this.batchOps = new BatchStorageOperations();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get item from AsyncStorage with type safety
|
|
31
|
+
*/
|
|
32
|
+
async getItem<T>(key: string, defaultValue: T): Promise<StorageResult<T>> {
|
|
33
|
+
return this.baseOps.getItem(key, defaultValue);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Set item in AsyncStorage with automatic JSON serialization
|
|
38
|
+
*/
|
|
39
|
+
async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
|
|
40
|
+
return this.baseOps.setItem(key, value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get string value (no JSON parsing)
|
|
45
|
+
*/
|
|
46
|
+
async getString(key: string, defaultValue: string): Promise<StorageResult<string>> {
|
|
47
|
+
return this.stringOps.getString(key, defaultValue);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Set string value (no JSON serialization)
|
|
52
|
+
*/
|
|
53
|
+
async setString(key: string, value: string): Promise<StorageResult<string>> {
|
|
54
|
+
return this.stringOps.setString(key, value);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Remove item from AsyncStorage
|
|
59
|
+
*/
|
|
60
|
+
async removeItem(key: string): Promise<StorageResult<void>> {
|
|
61
|
+
return this.baseOps.removeItem(key);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check if key exists in storage
|
|
66
|
+
*/
|
|
67
|
+
async hasItem(key: string): Promise<boolean> {
|
|
68
|
+
return this.baseOps.hasItem(key);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Clear all AsyncStorage data
|
|
73
|
+
*/
|
|
74
|
+
async clearAll(): Promise<StorageResult<void>> {
|
|
75
|
+
return this.baseOps.clearAll();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get multiple items at once
|
|
80
|
+
*/
|
|
81
|
+
async getMultiple(
|
|
82
|
+
keys: string[]
|
|
83
|
+
): Promise<StorageResult<Record<string, string | null>>> {
|
|
84
|
+
return this.batchOps.getMultiple(keys);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get all keys from storage
|
|
89
|
+
*/
|
|
90
|
+
async getAllKeys(): Promise<StorageResult<string[]>> {
|
|
91
|
+
return this.batchOps.getAllKeys();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Singleton instance
|
|
97
|
+
*/
|
|
98
|
+
export const storageRepository = new AsyncStorageRepository();
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Storage Operations
|
|
3
|
+
*
|
|
4
|
+
* Core storage operations following Single Responsibility Principle
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
8
|
+
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
9
|
+
import { success, failure } from '../../domain/entities/StorageResult';
|
|
10
|
+
import {
|
|
11
|
+
StorageReadError,
|
|
12
|
+
StorageWriteError,
|
|
13
|
+
StorageDeleteError,
|
|
14
|
+
StorageSerializationError,
|
|
15
|
+
StorageDeserializationError,
|
|
16
|
+
} from '../../domain/errors/StorageError';
|
|
17
|
+
import { devWarn } from '../../domain/utils/devUtils';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Base storage operations implementation
|
|
21
|
+
*/
|
|
22
|
+
export class BaseStorageOperations {
|
|
23
|
+
/**
|
|
24
|
+
* Get item from AsyncStorage with type safety
|
|
25
|
+
*/
|
|
26
|
+
async getItem<T>(key: string, defaultValue: T): Promise<StorageResult<T>> {
|
|
27
|
+
try {
|
|
28
|
+
const value = await AsyncStorage.getItem(key);
|
|
29
|
+
|
|
30
|
+
if (value === null) {
|
|
31
|
+
return success(defaultValue);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(value) as T;
|
|
36
|
+
return success(parsed);
|
|
37
|
+
} catch (parseError) {
|
|
38
|
+
return failure(new StorageDeserializationError(key, parseError), defaultValue);
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return failure(new StorageReadError(key, error), defaultValue);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Set item in AsyncStorage with automatic JSON serialization
|
|
47
|
+
*/
|
|
48
|
+
async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
|
|
49
|
+
try {
|
|
50
|
+
let serialized: string;
|
|
51
|
+
try {
|
|
52
|
+
serialized = JSON.stringify(value);
|
|
53
|
+
} catch (serializeError) {
|
|
54
|
+
return failure(new StorageSerializationError(key, serializeError));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await AsyncStorage.setItem(key, serialized);
|
|
58
|
+
return success(value);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return failure(new StorageWriteError(key, error));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Remove item from AsyncStorage
|
|
66
|
+
*/
|
|
67
|
+
async removeItem(key: string): Promise<StorageResult<void>> {
|
|
68
|
+
try {
|
|
69
|
+
await AsyncStorage.removeItem(key);
|
|
70
|
+
return success(undefined);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return failure(new StorageDeleteError(key, error));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Check if key exists in storage
|
|
78
|
+
*/
|
|
79
|
+
async hasItem(key: string): Promise<boolean> {
|
|
80
|
+
try {
|
|
81
|
+
const value = await AsyncStorage.getItem(key);
|
|
82
|
+
return value !== null;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
devWarn(`BaseStorageOperations: Failed to check if key "${key}" exists`, error);
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Clear all AsyncStorage data
|
|
91
|
+
*/
|
|
92
|
+
async clearAll(): Promise<StorageResult<void>> {
|
|
93
|
+
try {
|
|
94
|
+
await AsyncStorage.clear();
|
|
95
|
+
return success(undefined);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return failure(new StorageDeleteError('ALL_KEYS', error));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch Storage Operations
|
|
3
|
+
*
|
|
4
|
+
* Batch operations following Single Responsibility Principle
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
8
|
+
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
9
|
+
import { success, failure } from '../../domain/entities/StorageResult';
|
|
10
|
+
import { StorageReadError } from '../../domain/errors/StorageError';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Batch storage operations for efficiency
|
|
14
|
+
*/
|
|
15
|
+
export class BatchStorageOperations {
|
|
16
|
+
/**
|
|
17
|
+
* Get multiple items at once
|
|
18
|
+
*/
|
|
19
|
+
async getMultiple(
|
|
20
|
+
keys: string[]
|
|
21
|
+
): Promise<StorageResult<Record<string, string | null>>> {
|
|
22
|
+
try {
|
|
23
|
+
const pairs = await AsyncStorage.multiGet(keys);
|
|
24
|
+
const result = Object.fromEntries(pairs);
|
|
25
|
+
return success(result);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return failure(new StorageReadError('MULTIPLE_KEYS', error));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get all keys from storage
|
|
33
|
+
*/
|
|
34
|
+
async getAllKeys(): Promise<StorageResult<string[]>> {
|
|
35
|
+
try {
|
|
36
|
+
const keys = await AsyncStorage.getAllKeys();
|
|
37
|
+
return success([...keys]);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return failure(new StorageReadError('ALL_KEYS', error));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Storage Repositories
|
|
2
|
+
|
|
3
|
+
Repository pattern implementation for data persistence with error handling.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
AsyncStorageRepository provides storage operations with Result pattern for error handling. Located at `src/infrastructure/repositories/`.
|
|
8
|
+
|
|
9
|
+
## Strategies
|
|
10
|
+
|
|
11
|
+
### Result Pattern
|
|
12
|
+
- Return Result objects instead of throwing exceptions
|
|
13
|
+
- Use success/failure states for predictable error handling
|
|
14
|
+
- Include error details in failure results
|
|
15
|
+
- Allow graceful degradation when operations fail
|
|
16
|
+
|
|
17
|
+
### Serialization Strategy
|
|
18
|
+
- Automatically serialize objects to JSON
|
|
19
|
+
- Deserialize with type safety using generics
|
|
20
|
+
- Handle circular reference errors gracefully
|
|
21
|
+
- Support all JSON-serializable types
|
|
22
|
+
|
|
23
|
+
### Error Handling Strategy
|
|
24
|
+
- Categorize errors by type (read, write, serialization)
|
|
25
|
+
- Include key name in error context
|
|
26
|
+
- Preserve original error as cause
|
|
27
|
+
- Log errors for debugging
|
|
28
|
+
|
|
29
|
+
### Performance Strategy
|
|
30
|
+
- Use parallel operations for batch operations
|
|
31
|
+
- Implement chunking for large datasets
|
|
32
|
+
- Avoid synchronous operations on main thread
|
|
33
|
+
- Cache frequently accessed values
|
|
34
|
+
|
|
35
|
+
## Restrictions
|
|
36
|
+
|
|
37
|
+
### Storage Operations
|
|
38
|
+
- DO NOT store non-JSON-serializable data (functions, class instances)
|
|
39
|
+
- DO NOT store circular references
|
|
40
|
+
- DO NOT use undefined values (use null instead)
|
|
41
|
+
- DO NOT store extremely large payloads (> 1MB) without chunking
|
|
42
|
+
|
|
43
|
+
### Error Handling
|
|
44
|
+
- DO NOT ignore error states in Result objects
|
|
45
|
+
- DO NOT throw exceptions from repository methods
|
|
46
|
+
- DO NOT return null for missing keys (use defaultValue)
|
|
47
|
+
- DO NOT suppress error logging in production
|
|
48
|
+
|
|
49
|
+
### Type Safety
|
|
50
|
+
- DO NOT use `any` type for stored values
|
|
51
|
+
- DO NOT skip type parameters on generic methods
|
|
52
|
+
- DO NOT assume Result is always successful
|
|
53
|
+
- DO NOT cast results without checking success flag
|
|
54
|
+
|
|
55
|
+
### Performance
|
|
56
|
+
- DO NOT perform sequential operations in loops (use parallel)
|
|
57
|
+
- DO NOT read same key multiple times without caching
|
|
58
|
+
- DO NOT write to storage on every state change (debounce)
|
|
59
|
+
- DO NOT store redundant data
|
|
60
|
+
|
|
61
|
+
## Rules
|
|
62
|
+
|
|
63
|
+
### Interface Implementation
|
|
64
|
+
- MUST implement IStorageRepository interface completely
|
|
65
|
+
- MUST return StorageResult<T> for all methods
|
|
66
|
+
- MUST include error details in failure results
|
|
67
|
+
- MUST handle all exceptions internally
|
|
68
|
+
|
|
69
|
+
### Method Signatures
|
|
70
|
+
- MUST provide generic type parameter for type safety
|
|
71
|
+
- MUST accept defaultValue parameter for getItem methods
|
|
72
|
+
- MUST return Promise for all async operations
|
|
73
|
+
- MUST preserve key names in error messages
|
|
74
|
+
|
|
75
|
+
### Error Handling
|
|
76
|
+
- MUST wrap all storage operations in try-catch
|
|
77
|
+
- MUST return failure Result on errors
|
|
78
|
+
- MUST include error code and message
|
|
79
|
+
- MUST log errors before returning failure
|
|
80
|
+
|
|
81
|
+
### Serialization
|
|
82
|
+
- MUST use JSON.stringify for setItem operations
|
|
83
|
+
- MUST use JSON.parse for getItem operations
|
|
84
|
+
- MUST handle JSON parsing errors
|
|
85
|
+
- MUST validate deserialized structure
|
|
86
|
+
|
|
87
|
+
### Type Safety
|
|
88
|
+
- MUST enforce type parameter consistency
|
|
89
|
+
- MUST use type guards for runtime validation
|
|
90
|
+
- MUST provide type inference for return values
|
|
91
|
+
- MUST avoid type assertions when possible
|
|
92
|
+
|
|
93
|
+
### Result Objects
|
|
94
|
+
- MUST set success flag to true or false
|
|
95
|
+
- MUST include data in success results
|
|
96
|
+
- MUST include error in failure results
|
|
97
|
+
- MUST check success flag before accessing data
|
|
98
|
+
|
|
99
|
+
### Singleton Usage
|
|
100
|
+
- MUST use exported singleton instance
|
|
101
|
+
- MUST NOT create multiple repository instances
|
|
102
|
+
- MUST reset state between tests
|
|
103
|
+
- MUST clear storage in test setup
|
|
104
|
+
|
|
105
|
+
### Testing
|
|
106
|
+
- MUST mock repository for unit tests
|
|
107
|
+
- MUST use real repository for integration tests
|
|
108
|
+
- MUST clear storage before each test
|
|
109
|
+
- MUST test both success and failure scenarios
|
|
110
|
+
|
|
111
|
+
### Performance
|
|
112
|
+
- MUST use Promise.all for parallel operations
|
|
113
|
+
- MUST implement debouncing for frequent writes
|
|
114
|
+
- MUST use appropriate chunk sizes for large data
|
|
115
|
+
- MUST monitor storage size limits
|
|
116
|
+
|
|
117
|
+
### Error Categories
|
|
118
|
+
- MUST use StorageReadError for read failures
|
|
119
|
+
- MUST use StorageWriteError for write failures
|
|
120
|
+
- MUST use StorageSerializationError for serialization failures
|
|
121
|
+
- MUST use StorageDeserializationError for parsing failures
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Storage Operations
|
|
3
|
+
*
|
|
4
|
+
* Specialized string operations following Single Responsibility Principle
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
8
|
+
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
9
|
+
import { success, failure } from '../../domain/entities/StorageResult';
|
|
10
|
+
import { StorageReadError, StorageWriteError } from '../../domain/errors/StorageError';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* String-specific storage operations
|
|
14
|
+
*/
|
|
15
|
+
export class StringStorageOperations {
|
|
16
|
+
/**
|
|
17
|
+
* Get string value (no JSON parsing)
|
|
18
|
+
*/
|
|
19
|
+
async getString(key: string, defaultValue: string): Promise<StorageResult<string>> {
|
|
20
|
+
try {
|
|
21
|
+
const value = await AsyncStorage.getItem(key);
|
|
22
|
+
|
|
23
|
+
if (value === null) {
|
|
24
|
+
return success(defaultValue);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return success(value);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return failure(new StorageReadError(key, error), defaultValue);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Set string value (no JSON serialization)
|
|
35
|
+
*/
|
|
36
|
+
async setString(key: string, value: string): Promise<StorageResult<string>> {
|
|
37
|
+
try {
|
|
38
|
+
await AsyncStorage.setItem(key, value);
|
|
39
|
+
return success(value);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return failure(new StorageWriteError(key, error));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/**
|
|
3
|
+
* AsyncStorageRepository Tests
|
|
4
|
+
*
|
|
5
|
+
* Unit tests for AsyncStorageRepository
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { AsyncStorageRepository } from '../AsyncStorageRepository';
|
|
9
|
+
import { AsyncStorage } from '../../__tests__/mocks/asyncStorage.mock';
|
|
10
|
+
|
|
11
|
+
describe('AsyncStorageRepository', () => {
|
|
12
|
+
let repository: AsyncStorageRepository;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
repository = new AsyncStorageRepository();
|
|
16
|
+
(AsyncStorage as any).__clear();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('Composition', () => {
|
|
20
|
+
it('should use composition pattern', () => {
|
|
21
|
+
expect(repository).toBeInstanceOf(AsyncStorageRepository);
|
|
22
|
+
|
|
23
|
+
// Test that all methods are available
|
|
24
|
+
expect(typeof repository.getItem).toBe('function');
|
|
25
|
+
expect(typeof repository.setItem).toBe('function');
|
|
26
|
+
expect(typeof repository.getString).toBe('function');
|
|
27
|
+
expect(typeof repository.setString).toBe('function');
|
|
28
|
+
expect(typeof repository.removeItem).toBe('function');
|
|
29
|
+
expect(typeof repository.hasItem).toBe('function');
|
|
30
|
+
expect(typeof repository.clearAll).toBe('function');
|
|
31
|
+
expect(typeof repository.getMultiple).toBe('function');
|
|
32
|
+
expect(typeof repository.getAllKeys).toBe('function');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('Delegation', () => {
|
|
37
|
+
it('should delegate getItem to BaseStorageOperations', async () => {
|
|
38
|
+
const key = 'test-key';
|
|
39
|
+
const defaultValue = 'default';
|
|
40
|
+
const expectedValue = 'test-value';
|
|
41
|
+
|
|
42
|
+
await AsyncStorage.setItem(key, JSON.stringify(expectedValue));
|
|
43
|
+
|
|
44
|
+
const result = await repository.getItem(key, defaultValue);
|
|
45
|
+
|
|
46
|
+
expect(result.success).toBe(true);
|
|
47
|
+
expect(result.data).toBe(expectedValue);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should delegate setItem to BaseStorageOperations', async () => {
|
|
51
|
+
const key = 'test-key';
|
|
52
|
+
const value = 'test-value';
|
|
53
|
+
|
|
54
|
+
const result = await repository.setItem(key, value);
|
|
55
|
+
|
|
56
|
+
expect(result.success).toBe(true);
|
|
57
|
+
expect(result.data).toBe(value);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should delegate getString to StringStorageOperations', async () => {
|
|
61
|
+
const key = 'test-key';
|
|
62
|
+
const defaultValue = 'default';
|
|
63
|
+
const expectedValue = 'test-string';
|
|
64
|
+
|
|
65
|
+
await AsyncStorage.setItem(key, expectedValue);
|
|
66
|
+
|
|
67
|
+
const result = await repository.getString(key, defaultValue);
|
|
68
|
+
|
|
69
|
+
expect(result.success).toBe(true);
|
|
70
|
+
expect(result.data).toBe(expectedValue);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should delegate setString to StringStorageOperations', async () => {
|
|
74
|
+
const key = 'test-key';
|
|
75
|
+
const value = 'test-string';
|
|
76
|
+
|
|
77
|
+
const result = await repository.setString(key, value);
|
|
78
|
+
|
|
79
|
+
expect(result.success).toBe(true);
|
|
80
|
+
expect(result.data).toBe(value);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should delegate removeItem to BaseStorageOperations', async () => {
|
|
84
|
+
const key = 'test-key';
|
|
85
|
+
|
|
86
|
+
await AsyncStorage.setItem(key, 'test-value');
|
|
87
|
+
|
|
88
|
+
const result = await repository.removeItem(key);
|
|
89
|
+
|
|
90
|
+
expect(result.success).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should delegate hasItem to BaseStorageOperations', async () => {
|
|
94
|
+
const key = 'test-key';
|
|
95
|
+
|
|
96
|
+
await AsyncStorage.setItem(key, 'test-value');
|
|
97
|
+
|
|
98
|
+
const exists = await repository.hasItem(key);
|
|
99
|
+
|
|
100
|
+
expect(exists).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should delegate clearAll to BaseStorageOperations', async () => {
|
|
104
|
+
await AsyncStorage.setItem('key1', 'value1');
|
|
105
|
+
await AsyncStorage.setItem('key2', 'value2');
|
|
106
|
+
|
|
107
|
+
const result = await repository.clearAll();
|
|
108
|
+
|
|
109
|
+
expect(result.success).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should delegate getMultiple to BatchStorageOperations', async () => {
|
|
113
|
+
const keys = ['key1', 'key2'];
|
|
114
|
+
|
|
115
|
+
await AsyncStorage.setItem('key1', 'value1');
|
|
116
|
+
await AsyncStorage.setItem('key2', 'value2');
|
|
117
|
+
|
|
118
|
+
const result = await repository.getMultiple(keys);
|
|
119
|
+
|
|
120
|
+
expect(result.success).toBe(true);
|
|
121
|
+
expect(result.data).toEqual({
|
|
122
|
+
key1: 'value1',
|
|
123
|
+
key2: 'value2'
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should delegate getAllKeys to BatchStorageOperations', async () => {
|
|
128
|
+
await AsyncStorage.setItem('key1', 'value1');
|
|
129
|
+
await AsyncStorage.setItem('key2', 'value2');
|
|
130
|
+
|
|
131
|
+
const result = await repository.getAllKeys();
|
|
132
|
+
|
|
133
|
+
expect(result.success).toBe(true);
|
|
134
|
+
expect(result.data).toEqual(expect.arrayContaining(['key1', 'key2']));
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('Error Handling', () => {
|
|
139
|
+
it('should handle errors from delegated operations', async () => {
|
|
140
|
+
const key = 'test-key';
|
|
141
|
+
|
|
142
|
+
// Mock error in AsyncStorage
|
|
143
|
+
(AsyncStorage.getItem as jest.Mock).mockRejectedValue(new Error('Storage error'));
|
|
144
|
+
|
|
145
|
+
const result = await repository.getItem(key, 'default');
|
|
146
|
+
|
|
147
|
+
expect(result.success).toBe(false);
|
|
148
|
+
expect(result.error).toBeDefined();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('Type Safety', () => {
|
|
153
|
+
it('should maintain type safety for generic methods', async () => {
|
|
154
|
+
interface TestData {
|
|
155
|
+
id: number;
|
|
156
|
+
name: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const key = 'typed-key';
|
|
160
|
+
const data: TestData = { id: 1, name: 'test' };
|
|
161
|
+
|
|
162
|
+
const setResult = await repository.setItem<TestData>(key, data);
|
|
163
|
+
const getResult = await repository.getItem<TestData>(key, { id: 0, name: '' });
|
|
164
|
+
|
|
165
|
+
expect(setResult.success).toBe(true);
|
|
166
|
+
expect(getResult.success).toBe(true);
|
|
167
|
+
expect(getResult.data).toEqual(data);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|