@umituz/react-native-storage 1.5.0 → 2.3.2
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/README.md +0 -0
- package/package.json +28 -10
- package/src/__tests__/integration.test.ts +391 -0
- package/src/__tests__/mocks/asyncStorage.mock.ts +52 -0
- package/src/__tests__/performance.test.ts +351 -0
- package/src/__tests__/setup.ts +63 -0
- package/src/application/ports/IStorageRepository.ts +0 -12
- package/src/domain/constants/CacheDefaults.ts +64 -0
- package/src/domain/entities/CachedValue.ts +86 -0
- package/src/domain/entities/StorageResult.ts +1 -3
- package/src/domain/entities/__tests__/CachedValue.test.ts +149 -0
- package/src/domain/entities/__tests__/StorageResult.test.ts +122 -0
- package/src/domain/errors/StorageError.ts +0 -2
- package/src/domain/errors/__tests__/StorageError.test.ts +127 -0
- package/src/domain/factories/StoreFactory.ts +33 -0
- package/src/domain/types/Store.ts +18 -0
- package/src/domain/utils/CacheKeyGenerator.ts +66 -0
- package/src/domain/utils/__tests__/devUtils.test.ts +97 -0
- package/src/domain/utils/devUtils.ts +37 -0
- package/src/domain/value-objects/StorageKey.ts +27 -29
- package/src/index.ts +59 -1
- package/src/infrastructure/adapters/StorageService.ts +8 -6
- package/src/infrastructure/repositories/AsyncStorageRepository.ts +27 -108
- package/src/infrastructure/repositories/BaseStorageOperations.ts +101 -0
- package/src/infrastructure/repositories/BatchStorageOperations.ts +42 -0
- package/src/infrastructure/repositories/StringStorageOperations.ts +44 -0
- package/src/infrastructure/repositories/__tests__/AsyncStorageRepository.test.ts +169 -0
- package/src/infrastructure/repositories/__tests__/BaseStorageOperations.test.ts +200 -0
- package/src/presentation/hooks/CacheStorageOperations.ts +95 -0
- package/src/presentation/hooks/__tests__/usePersistentCache.test.ts +404 -0
- package/src/presentation/hooks/__tests__/useStorage.test.ts +246 -0
- package/src/presentation/hooks/__tests__/useStorageState.test.ts +292 -0
- package/src/presentation/hooks/useCacheState.ts +55 -0
- package/src/presentation/hooks/usePersistentCache.ts +154 -0
- package/src/presentation/hooks/useStorage.ts +4 -3
- package/src/presentation/hooks/useStorageState.ts +24 -8
- package/src/presentation/hooks/useStore.ts +15 -0
- package/src/types/global.d.ts +40 -0
- package/LICENSE +0 -22
|
@@ -3,39 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Domain-Driven Design: Value Object for storage keys
|
|
5
5
|
* Ensures type safety and prevents invalid key usage
|
|
6
|
-
*
|
|
7
|
-
* Theme: {{THEME_NAME}} ({{CATEGORY}} category)
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Storage Key Type
|
|
12
|
-
* All valid storage keys must be defined here
|
|
13
6
|
*/
|
|
14
|
-
export enum StorageKey {
|
|
15
|
-
// Onboarding
|
|
16
|
-
ONBOARDING_COMPLETED = '@onboarding_completed',
|
|
17
|
-
|
|
18
|
-
// Auth
|
|
19
|
-
AUTH_SHOW_REGISTER = '@auth_show_register',
|
|
20
|
-
|
|
21
|
-
// Localization
|
|
22
|
-
LANGUAGE = '@app_language',
|
|
23
|
-
|
|
24
|
-
// Theme
|
|
25
|
-
THEME_MODE = '@app_theme_mode',
|
|
26
|
-
|
|
27
|
-
// Settings (requires userId suffix)
|
|
28
|
-
SETTINGS = 'app_settings',
|
|
29
|
-
|
|
30
|
-
// Query Cache (requires app name prefix)
|
|
31
|
-
QUERY_CACHE = '{{APP_NAME}}_query_cache',
|
|
32
|
-
}
|
|
33
7
|
|
|
34
8
|
/**
|
|
35
9
|
* Storage key with dynamic suffix
|
|
36
10
|
*/
|
|
37
11
|
export type DynamicStorageKey = {
|
|
38
|
-
base:
|
|
12
|
+
base: string;
|
|
39
13
|
suffix: string;
|
|
40
14
|
};
|
|
41
15
|
|
|
@@ -46,7 +20,7 @@ export type DynamicStorageKey = {
|
|
|
46
20
|
* @param userId User identifier
|
|
47
21
|
* @returns User-specific key
|
|
48
22
|
*/
|
|
49
|
-
export const createUserKey = (baseKey:
|
|
23
|
+
export const createUserKey = (baseKey: string, userId: string): string => {
|
|
50
24
|
return `${baseKey}_${userId}`;
|
|
51
25
|
};
|
|
52
26
|
|
|
@@ -57,6 +31,30 @@ export const createUserKey = (baseKey: StorageKey, userId: string): string => {
|
|
|
57
31
|
* @param appName App identifier
|
|
58
32
|
* @returns App-specific key
|
|
59
33
|
*/
|
|
60
|
-
export const createAppKey = (baseKey:
|
|
34
|
+
export const createAppKey = (baseKey: string, appName: string): string => {
|
|
61
35
|
return `${appName}_${baseKey}`;
|
|
62
36
|
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Helper to create namespaced storage key
|
|
40
|
+
*
|
|
41
|
+
* @param namespace Key namespace
|
|
42
|
+
* @param key Specific key
|
|
43
|
+
* @returns Namespaced key
|
|
44
|
+
*/
|
|
45
|
+
export const createNamespacedKey = (namespace: string, key: string): string => {
|
|
46
|
+
return `${namespace}:${key}`;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Legacy StorageKey enum for backward compatibility
|
|
51
|
+
* @deprecated Use createNamespacedKey instead
|
|
52
|
+
*/
|
|
53
|
+
export enum StorageKey {
|
|
54
|
+
USER_PREFERENCES = '@user_preferences',
|
|
55
|
+
APP_SETTINGS = '@app_settings',
|
|
56
|
+
LANGUAGE = '@language',
|
|
57
|
+
UI_PREFERENCES = '@ui_preferences',
|
|
58
|
+
QUERY_CACHE = '@query_cache',
|
|
59
|
+
DATA_CACHE = '@data_cache',
|
|
60
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - presentation: Hooks (React integration)
|
|
14
14
|
*
|
|
15
15
|
* Usage:
|
|
16
|
-
* import { useStorage, useStorageState,
|
|
16
|
+
* import { useStorage, useStorageState, createStore } from '@umituz/react-native-storage';
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
// =============================================================================
|
|
@@ -48,6 +48,54 @@ export {
|
|
|
48
48
|
isFailure,
|
|
49
49
|
} from './domain/entities/StorageResult';
|
|
50
50
|
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// DOMAIN LAYER - Cached Value Entity
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
export type { CachedValue } from './domain/entities/CachedValue';
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
createCachedValue,
|
|
59
|
+
isCacheExpired,
|
|
60
|
+
getRemainingTTL,
|
|
61
|
+
getCacheAge,
|
|
62
|
+
} from './domain/entities/CachedValue';
|
|
63
|
+
|
|
64
|
+
// =============================================================================
|
|
65
|
+
// DOMAIN LAYER - Cache Utilities
|
|
66
|
+
// =============================================================================
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
generateCacheKey,
|
|
70
|
+
generateListCacheKey,
|
|
71
|
+
parseCacheKey,
|
|
72
|
+
isCacheKey,
|
|
73
|
+
} from './domain/utils/CacheKeyGenerator';
|
|
74
|
+
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// DOMAIN LAYER - Cache Constants
|
|
77
|
+
// =============================================================================
|
|
78
|
+
|
|
79
|
+
export { TIME_MS, DEFAULT_TTL, CACHE_VERSION } from './domain/constants/CacheDefaults';
|
|
80
|
+
|
|
81
|
+
// =============================================================================
|
|
82
|
+
// DOMAIN LAYER - Development Utilities
|
|
83
|
+
// =============================================================================
|
|
84
|
+
|
|
85
|
+
export { isDev, devWarn, devError, devLog } from './domain/utils/devUtils';
|
|
86
|
+
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// DOMAIN LAYER - Store Types
|
|
89
|
+
// =============================================================================
|
|
90
|
+
|
|
91
|
+
export type { StoreConfig, PersistedState } from './domain/types/Store';
|
|
92
|
+
|
|
93
|
+
// =============================================================================
|
|
94
|
+
// DOMAIN LAYER - Store Factory
|
|
95
|
+
// =============================================================================
|
|
96
|
+
|
|
97
|
+
export { createStore } from './domain/factories/StoreFactory';
|
|
98
|
+
|
|
51
99
|
// =============================================================================
|
|
52
100
|
// APPLICATION LAYER - Ports
|
|
53
101
|
// =============================================================================
|
|
@@ -74,3 +122,13 @@ export {
|
|
|
74
122
|
|
|
75
123
|
export { useStorage } from './presentation/hooks/useStorage';
|
|
76
124
|
export { useStorageState } from './presentation/hooks/useStorageState';
|
|
125
|
+
export { useStore } from './presentation/hooks/useStore';
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
usePersistentCache,
|
|
129
|
+
type PersistentCacheOptions,
|
|
130
|
+
type PersistentCacheResult,
|
|
131
|
+
} from './presentation/hooks/usePersistentCache';
|
|
132
|
+
|
|
133
|
+
export { useCacheState } from './presentation/hooks/useCacheState';
|
|
134
|
+
export { CacheStorageOperations } from './presentation/hooks/CacheStorageOperations';
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
9
|
+
import { devWarn } from '../../domain/utils/devUtils';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* StateStorage interface for Zustand persist middleware
|
|
@@ -18,13 +19,14 @@ export interface StateStorage {
|
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Storage service for Zustand persist middleware
|
|
21
|
-
* Direct AsyncStorage implementation
|
|
22
|
+
* Direct AsyncStorage implementation with proper error handling
|
|
22
23
|
*/
|
|
23
24
|
export const storageService: StateStorage = {
|
|
24
25
|
getItem: async (name: string): Promise<string | null> => {
|
|
25
26
|
try {
|
|
26
27
|
return await AsyncStorage.getItem(name);
|
|
27
|
-
} catch {
|
|
28
|
+
} catch (error) {
|
|
29
|
+
devWarn(`StorageService: Failed to get item "${name}"`, error);
|
|
28
30
|
return null;
|
|
29
31
|
}
|
|
30
32
|
},
|
|
@@ -32,16 +34,16 @@ export const storageService: StateStorage = {
|
|
|
32
34
|
setItem: async (name: string, value: string): Promise<void> => {
|
|
33
35
|
try {
|
|
34
36
|
await AsyncStorage.setItem(name, value);
|
|
35
|
-
} catch {
|
|
36
|
-
|
|
37
|
+
} catch (error) {
|
|
38
|
+
devWarn(`StorageService: Failed to set item "${name}"`, error);
|
|
37
39
|
}
|
|
38
40
|
},
|
|
39
41
|
|
|
40
42
|
removeItem: async (name: string): Promise<void> => {
|
|
41
43
|
try {
|
|
42
44
|
await AsyncStorage.removeItem(name);
|
|
43
|
-
} catch {
|
|
44
|
-
|
|
45
|
+
} catch (error) {
|
|
46
|
+
devWarn(`StorageService: Failed to remove item "${name}"`, error);
|
|
45
47
|
}
|
|
46
48
|
},
|
|
47
49
|
};
|
|
@@ -2,178 +2,97 @@
|
|
|
2
2
|
* AsyncStorage Repository
|
|
3
3
|
*
|
|
4
4
|
* Domain-Driven Design: Infrastructure implementation of IStorageRepository
|
|
5
|
-
* Adapts React Native AsyncStorage to domain interface
|
|
5
|
+
* Adapts React Native AsyncStorage to domain interface using composition
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
9
8
|
import type { IStorageRepository } from '../../application/ports/IStorageRepository';
|
|
10
9
|
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
StorageWriteError,
|
|
15
|
-
StorageDeleteError,
|
|
16
|
-
StorageSerializationError,
|
|
17
|
-
StorageDeserializationError,
|
|
18
|
-
} from '../../domain/errors/StorageError';
|
|
10
|
+
import { BaseStorageOperations } from './BaseStorageOperations';
|
|
11
|
+
import { StringStorageOperations } from './StringStorageOperations';
|
|
12
|
+
import { BatchStorageOperations } from './BatchStorageOperations';
|
|
19
13
|
|
|
20
14
|
/**
|
|
21
15
|
* AsyncStorage Repository Implementation
|
|
16
|
+
* Uses composition to follow Single Responsibility Principle
|
|
22
17
|
*/
|
|
23
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
|
+
|
|
24
29
|
/**
|
|
25
30
|
* Get item from AsyncStorage with type safety
|
|
26
31
|
*/
|
|
27
32
|
async getItem<T>(key: string, defaultValue: T): Promise<StorageResult<T>> {
|
|
28
|
-
|
|
29
|
-
const value = await AsyncStorage.getItem(key);
|
|
30
|
-
|
|
31
|
-
if (value === null) {
|
|
32
|
-
// Missing keys on first app launch are NORMAL, not errors
|
|
33
|
-
return success(defaultValue);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
const parsed = JSON.parse(value) as T;
|
|
38
|
-
return success(parsed);
|
|
39
|
-
} catch (parseError) {
|
|
40
|
-
return failure(new StorageDeserializationError(key, parseError), defaultValue);
|
|
41
|
-
}
|
|
42
|
-
} catch (error) {
|
|
43
|
-
return failure(new StorageReadError(key, error), defaultValue);
|
|
44
|
-
}
|
|
33
|
+
return this.baseOps.getItem(key, defaultValue);
|
|
45
34
|
}
|
|
46
35
|
|
|
47
36
|
/**
|
|
48
37
|
* Set item in AsyncStorage with automatic JSON serialization
|
|
49
38
|
*/
|
|
50
39
|
async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
|
|
51
|
-
|
|
52
|
-
let serialized: string;
|
|
53
|
-
try {
|
|
54
|
-
serialized = JSON.stringify(value);
|
|
55
|
-
} catch (serializeError) {
|
|
56
|
-
return failure(new StorageSerializationError(key, serializeError));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
await AsyncStorage.setItem(key, serialized);
|
|
60
|
-
return success(value);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
return failure(new StorageWriteError(key, error));
|
|
63
|
-
}
|
|
40
|
+
return this.baseOps.setItem(key, value);
|
|
64
41
|
}
|
|
65
42
|
|
|
66
43
|
/**
|
|
67
44
|
* Get string value (no JSON parsing)
|
|
68
45
|
*/
|
|
69
46
|
async getString(key: string, defaultValue: string): Promise<StorageResult<string>> {
|
|
70
|
-
|
|
71
|
-
const value = await AsyncStorage.getItem(key);
|
|
72
|
-
|
|
73
|
-
if (value === null) {
|
|
74
|
-
// Missing keys on first app launch are NORMAL, not errors
|
|
75
|
-
return success(defaultValue);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return success(value);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
return failure(new StorageReadError(key, error), defaultValue);
|
|
81
|
-
}
|
|
47
|
+
return this.stringOps.getString(key, defaultValue);
|
|
82
48
|
}
|
|
83
49
|
|
|
84
50
|
/**
|
|
85
51
|
* Set string value (no JSON serialization)
|
|
86
52
|
*/
|
|
87
53
|
async setString(key: string, value: string): Promise<StorageResult<string>> {
|
|
88
|
-
|
|
89
|
-
await AsyncStorage.setItem(key, value);
|
|
90
|
-
return success(value);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
return failure(new StorageWriteError(key, error));
|
|
93
|
-
}
|
|
54
|
+
return this.stringOps.setString(key, value);
|
|
94
55
|
}
|
|
95
56
|
|
|
96
57
|
/**
|
|
97
58
|
* Remove item from AsyncStorage
|
|
98
59
|
*/
|
|
99
60
|
async removeItem(key: string): Promise<StorageResult<void>> {
|
|
100
|
-
|
|
101
|
-
await AsyncStorage.removeItem(key);
|
|
102
|
-
return success(undefined);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
return failure(new StorageDeleteError(key, error));
|
|
105
|
-
}
|
|
61
|
+
return this.baseOps.removeItem(key);
|
|
106
62
|
}
|
|
107
63
|
|
|
108
64
|
/**
|
|
109
65
|
* Check if key exists in storage
|
|
110
66
|
*/
|
|
111
67
|
async hasItem(key: string): Promise<boolean> {
|
|
112
|
-
|
|
113
|
-
const value = await AsyncStorage.getItem(key);
|
|
114
|
-
return value !== null;
|
|
115
|
-
} catch {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
68
|
+
return this.baseOps.hasItem(key);
|
|
118
69
|
}
|
|
119
70
|
|
|
120
71
|
/**
|
|
121
|
-
* Clear all AsyncStorage data
|
|
72
|
+
* Clear all AsyncStorage data
|
|
122
73
|
*/
|
|
123
74
|
async clearAll(): Promise<StorageResult<void>> {
|
|
124
|
-
|
|
125
|
-
await AsyncStorage.clear();
|
|
126
|
-
return success(undefined);
|
|
127
|
-
} catch (error) {
|
|
128
|
-
return failure(new StorageDeleteError('ALL_KEYS', error));
|
|
129
|
-
}
|
|
75
|
+
return this.baseOps.clearAll();
|
|
130
76
|
}
|
|
131
77
|
|
|
132
78
|
/**
|
|
133
|
-
* Get multiple items at once
|
|
79
|
+
* Get multiple items at once
|
|
134
80
|
*/
|
|
135
81
|
async getMultiple(
|
|
136
82
|
keys: string[]
|
|
137
83
|
): Promise<StorageResult<Record<string, string | null>>> {
|
|
138
|
-
|
|
139
|
-
const pairs = await AsyncStorage.multiGet(keys);
|
|
140
|
-
const result = Object.fromEntries(pairs);
|
|
141
|
-
return success(result);
|
|
142
|
-
} catch (error) {
|
|
143
|
-
return failure(new StorageReadError('MULTIPLE_KEYS', error));
|
|
144
|
-
}
|
|
84
|
+
return this.batchOps.getMultiple(keys);
|
|
145
85
|
}
|
|
146
86
|
|
|
147
87
|
/**
|
|
148
88
|
* Get all keys from storage
|
|
149
89
|
*/
|
|
150
90
|
async getAllKeys(): Promise<StorageResult<string[]>> {
|
|
151
|
-
|
|
152
|
-
const keys = await AsyncStorage.getAllKeys();
|
|
153
|
-
return success([...keys]);
|
|
154
|
-
} catch (error) {
|
|
155
|
-
return failure(new StorageReadError('ALL_KEYS', error));
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Get object from storage (alias for getItem for backward compatibility)
|
|
161
|
-
* @deprecated Use getItem instead
|
|
162
|
-
*/
|
|
163
|
-
async getObject<T>(key: string, defaultValue: T): Promise<StorageResult<T>> {
|
|
164
|
-
return this.getItem(key, defaultValue);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Set object in storage (alias for setItem for backward compatibility)
|
|
169
|
-
* @deprecated Use setItem instead
|
|
170
|
-
*/
|
|
171
|
-
async setObject<T>(key: string, value: T): Promise<StorageResult<T>> {
|
|
172
|
-
return this.setItem(key, value);
|
|
91
|
+
return this.batchOps.getAllKeys();
|
|
173
92
|
}
|
|
174
93
|
}
|
|
175
94
|
|
|
176
95
|
/**
|
|
177
96
|
* Singleton instance
|
|
178
97
|
*/
|
|
179
|
-
export const storageRepository = new AsyncStorageRepository();
|
|
98
|
+
export const storageRepository = new AsyncStorageRepository();
|
|
@@ -0,0 +1,101 @@
|
|
|
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 { IStorageRepository } from '../../application/ports/IStorageRepository';
|
|
9
|
+
import type { StorageResult } from '../../domain/entities/StorageResult';
|
|
10
|
+
import { success, failure } from '../../domain/entities/StorageResult';
|
|
11
|
+
import {
|
|
12
|
+
StorageReadError,
|
|
13
|
+
StorageWriteError,
|
|
14
|
+
StorageDeleteError,
|
|
15
|
+
StorageSerializationError,
|
|
16
|
+
StorageDeserializationError,
|
|
17
|
+
} from '../../domain/errors/StorageError';
|
|
18
|
+
import { devWarn } from '../../domain/utils/devUtils';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Base storage operations implementation
|
|
22
|
+
*/
|
|
23
|
+
export class BaseStorageOperations {
|
|
24
|
+
/**
|
|
25
|
+
* Get item from AsyncStorage with type safety
|
|
26
|
+
*/
|
|
27
|
+
async getItem<T>(key: string, defaultValue: T): Promise<StorageResult<T>> {
|
|
28
|
+
try {
|
|
29
|
+
const value = await AsyncStorage.getItem(key);
|
|
30
|
+
|
|
31
|
+
if (value === null) {
|
|
32
|
+
return success(defaultValue);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(value) as T;
|
|
37
|
+
return success(parsed);
|
|
38
|
+
} catch (parseError) {
|
|
39
|
+
return failure(new StorageDeserializationError(key, parseError), defaultValue);
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return failure(new StorageReadError(key, error), defaultValue);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Set item in AsyncStorage with automatic JSON serialization
|
|
48
|
+
*/
|
|
49
|
+
async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
|
|
50
|
+
try {
|
|
51
|
+
let serialized: string;
|
|
52
|
+
try {
|
|
53
|
+
serialized = JSON.stringify(value);
|
|
54
|
+
} catch (serializeError) {
|
|
55
|
+
return failure(new StorageSerializationError(key, serializeError));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await AsyncStorage.setItem(key, serialized);
|
|
59
|
+
return success(value);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return failure(new StorageWriteError(key, error));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Remove item from AsyncStorage
|
|
67
|
+
*/
|
|
68
|
+
async removeItem(key: string): Promise<StorageResult<void>> {
|
|
69
|
+
try {
|
|
70
|
+
await AsyncStorage.removeItem(key);
|
|
71
|
+
return success(undefined);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
return failure(new StorageDeleteError(key, error));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Check if key exists in storage
|
|
79
|
+
*/
|
|
80
|
+
async hasItem(key: string): Promise<boolean> {
|
|
81
|
+
try {
|
|
82
|
+
const value = await AsyncStorage.getItem(key);
|
|
83
|
+
return value !== null;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
devWarn(`BaseStorageOperations: Failed to check if key "${key}" exists`, error);
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Clear all AsyncStorage data
|
|
92
|
+
*/
|
|
93
|
+
async clearAll(): Promise<StorageResult<void>> {
|
|
94
|
+
try {
|
|
95
|
+
await AsyncStorage.clear();
|
|
96
|
+
return success(undefined);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return failure(new StorageDeleteError('ALL_KEYS', error));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -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,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
|
+
}
|