@umituz/react-native-storage 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +102 -0
  3. package/lib/application/ports/IStorageRepository.d.ts +48 -0
  4. package/lib/application/ports/IStorageRepository.d.ts.map +1 -0
  5. package/lib/application/ports/IStorageRepository.js +10 -0
  6. package/lib/application/ports/IStorageRepository.js.map +1 -0
  7. package/lib/domain/entities/StorageResult.d.ts +38 -0
  8. package/lib/domain/entities/StorageResult.d.ts.map +1 -0
  9. package/lib/domain/entities/StorageResult.js +42 -0
  10. package/lib/domain/entities/StorageResult.js.map +1 -0
  11. package/lib/domain/errors/StorageError.d.ts +51 -0
  12. package/lib/domain/errors/StorageError.d.ts.map +1 -0
  13. package/lib/domain/errors/StorageError.js +69 -0
  14. package/lib/domain/errors/StorageError.js.map +1 -0
  15. package/lib/domain/value-objects/StorageKey.d.ts +43 -0
  16. package/lib/domain/value-objects/StorageKey.d.ts.map +1 -0
  17. package/lib/domain/value-objects/StorageKey.js +46 -0
  18. package/lib/domain/value-objects/StorageKey.js.map +1 -0
  19. package/lib/index.d.ts +27 -0
  20. package/lib/index.d.ts.map +1 -0
  21. package/lib/index.js +33 -0
  22. package/lib/index.js.map +1 -0
  23. package/lib/infrastructure/repositories/AsyncStorageRepository.d.ts +50 -0
  24. package/lib/infrastructure/repositories/AsyncStorageRepository.d.ts.map +1 -0
  25. package/lib/infrastructure/repositories/AsyncStorageRepository.js +137 -0
  26. package/lib/infrastructure/repositories/AsyncStorageRepository.js.map +1 -0
  27. package/lib/presentation/hooks/useStorage.d.ts +23 -0
  28. package/lib/presentation/hooks/useStorage.d.ts.map +1 -0
  29. package/lib/presentation/hooks/useStorage.js +87 -0
  30. package/lib/presentation/hooks/useStorage.js.map +1 -0
  31. package/lib/presentation/hooks/useStorageState.d.ts +21 -0
  32. package/lib/presentation/hooks/useStorageState.d.ts.map +1 -0
  33. package/lib/presentation/hooks/useStorageState.js +43 -0
  34. package/lib/presentation/hooks/useStorageState.js.map +1 -0
  35. package/package.json +55 -0
  36. package/src/application/ports/IStorageRepository.ts +56 -0
  37. package/src/domain/entities/StorageResult.ts +58 -0
  38. package/src/domain/errors/StorageError.ts +83 -0
  39. package/src/domain/value-objects/StorageKey.ts +59 -0
  40. package/src/index.ts +69 -0
  41. package/src/infrastructure/repositories/AsyncStorageRepository.ts +151 -0
  42. package/src/presentation/hooks/useStorage.ts +101 -0
  43. package/src/presentation/hooks/useStorageState.ts +55 -0
package/src/index.ts ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Storage Domain - Public API
3
+ *
4
+ * Domain-Driven Design (DDD) Architecture
5
+ *
6
+ * This is the SINGLE SOURCE OF TRUTH for all storage operations.
7
+ * ALL imports from the storage domain MUST go through this file.
8
+ *
9
+ * Architecture:
10
+ * - domain: Entities, value objects, errors (business logic)
11
+ * - application: Ports (interfaces), use cases (not needed for simple CRUD)
12
+ * - infrastructure: Repository implementation (AsyncStorage adapter)
13
+ * - presentation: Hooks (React integration)
14
+ *
15
+ * Usage:
16
+ * import { useStorage, useStorageState, StorageKey } from '@umituz/react-native-storage';
17
+ */
18
+
19
+ // =============================================================================
20
+ // DOMAIN LAYER - Business Logic
21
+ // =============================================================================
22
+
23
+ export {
24
+ StorageKey,
25
+ createUserKey,
26
+ createAppKey,
27
+ } from './domain/value-objects/StorageKey';
28
+
29
+ export type { DynamicStorageKey } from './domain/value-objects/StorageKey';
30
+
31
+ export {
32
+ StorageError,
33
+ StorageReadError,
34
+ StorageWriteError,
35
+ StorageDeleteError,
36
+ StorageSerializationError,
37
+ StorageDeserializationError,
38
+ } from './domain/errors/StorageError';
39
+
40
+ export type { StorageResult } from './domain/entities/StorageResult';
41
+
42
+ export {
43
+ success,
44
+ failure,
45
+ unwrap,
46
+ map,
47
+ } from './domain/entities/StorageResult';
48
+
49
+ // =============================================================================
50
+ // APPLICATION LAYER - Ports
51
+ // =============================================================================
52
+
53
+ export type { IStorageRepository } from './application/ports/IStorageRepository';
54
+
55
+ // =============================================================================
56
+ // INFRASTRUCTURE LAYER - Implementation
57
+ // =============================================================================
58
+
59
+ export {
60
+ AsyncStorageRepository,
61
+ storageRepository,
62
+ } from './infrastructure/repositories/AsyncStorageRepository';
63
+
64
+ // =============================================================================
65
+ // PRESENTATION LAYER - Hooks
66
+ // =============================================================================
67
+
68
+ export { useStorage } from './presentation/hooks/useStorage';
69
+ export { useStorageState } from './presentation/hooks/useStorageState';
@@ -0,0 +1,151 @@
1
+ /**
2
+ * AsyncStorage Repository
3
+ *
4
+ * Domain-Driven Design: Infrastructure implementation of IStorageRepository
5
+ * Adapts React Native AsyncStorage to domain interface
6
+ */
7
+
8
+ import AsyncStorage from '@react-native-async-storage/async-storage';
9
+ import type { IStorageRepository } from '../../application/ports/IStorageRepository';
10
+ import type { StorageResult } from '../../domain/entities/StorageResult';
11
+ import { success, failure } from '../../domain/entities/StorageResult';
12
+ import {
13
+ StorageReadError,
14
+ StorageWriteError,
15
+ StorageDeleteError,
16
+ StorageSerializationError,
17
+ StorageDeserializationError,
18
+ } from '../../domain/errors/StorageError';
19
+
20
+ /**
21
+ * AsyncStorage Repository Implementation
22
+ */
23
+ export class AsyncStorageRepository implements IStorageRepository {
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
+ // 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
+ }
45
+ }
46
+
47
+ /**
48
+ * Set item in AsyncStorage with automatic JSON serialization
49
+ */
50
+ async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
51
+ try {
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
+ }
64
+ }
65
+
66
+ /**
67
+ * Get string value (no JSON parsing)
68
+ */
69
+ async getString(key: string, defaultValue: string): Promise<StorageResult<string>> {
70
+ try {
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
+ }
82
+ }
83
+
84
+ /**
85
+ * Set string value (no JSON serialization)
86
+ */
87
+ async setString(key: string, value: string): Promise<StorageResult<string>> {
88
+ try {
89
+ await AsyncStorage.setItem(key, value);
90
+ return success(value);
91
+ } catch (error) {
92
+ return failure(new StorageWriteError(key, error));
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Remove item from AsyncStorage
98
+ */
99
+ async removeItem(key: string): Promise<StorageResult<void>> {
100
+ try {
101
+ await AsyncStorage.removeItem(key);
102
+ return success(undefined);
103
+ } catch (error) {
104
+ return failure(new StorageDeleteError(key, error));
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Check if key exists in storage
110
+ */
111
+ async hasItem(key: string): Promise<boolean> {
112
+ try {
113
+ const value = await AsyncStorage.getItem(key);
114
+ return value !== null;
115
+ } catch {
116
+ return false;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Clear all AsyncStorage data (use with caution!)
122
+ */
123
+ async clearAll(): Promise<StorageResult<void>> {
124
+ try {
125
+ await AsyncStorage.clear();
126
+ return success(undefined);
127
+ } catch (error) {
128
+ return failure(new StorageDeleteError('ALL_KEYS', error));
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Get multiple items at once (more efficient than multiple getItem calls)
134
+ */
135
+ async getMultiple(
136
+ keys: string[]
137
+ ): Promise<StorageResult<Record<string, string | null>>> {
138
+ try {
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
+ }
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Singleton instance
150
+ */
151
+ export const storageRepository = new AsyncStorageRepository();
@@ -0,0 +1,101 @@
1
+ /**
2
+ * useStorage Hook
3
+ *
4
+ * Domain-Driven Design: Presentation layer hook for storage operations
5
+ * Provides clean API for components to interact with storage domain
6
+ */
7
+
8
+ import { useCallback } from 'react';
9
+ import { storageRepository } from '../../infrastructure/repositories/AsyncStorageRepository';
10
+ import type { StorageResult } from '../../domain/entities/StorageResult';
11
+ import { unwrap } from '../../domain/entities/StorageResult';
12
+ import type { StorageKey } from '../../domain/value-objects/StorageKey';
13
+
14
+ /**
15
+ * Storage Hook
16
+ * Provides type-safe storage operations
17
+ */
18
+ export const useStorage = () => {
19
+ /**
20
+ * Get item from storage
21
+ */
22
+ const getItem = useCallback(async <T>(key: string | StorageKey, defaultValue: T): Promise<T> => {
23
+ const keyString = typeof key === 'string' ? key : String(key);
24
+ const result = await storageRepository.getItem(keyString, defaultValue);
25
+ return unwrap(result, defaultValue);
26
+ }, []);
27
+
28
+ /**
29
+ * Set item in storage
30
+ */
31
+ const setItem = useCallback(async <T>(key: string | StorageKey, value: T): Promise<boolean> => {
32
+ const keyString = typeof key === 'string' ? key : String(key);
33
+ const result = await storageRepository.setItem(keyString, value);
34
+ return result.success;
35
+ }, []);
36
+
37
+ /**
38
+ * Get string from storage
39
+ */
40
+ const getString = useCallback(async (key: string | StorageKey, defaultValue: string): Promise<string> => {
41
+ const keyString = typeof key === 'string' ? key : String(key);
42
+ const result = await storageRepository.getString(keyString, defaultValue);
43
+ return unwrap(result, defaultValue);
44
+ }, []);
45
+
46
+ /**
47
+ * Set string in storage
48
+ */
49
+ const setString = useCallback(async (key: string | StorageKey, value: string): Promise<boolean> => {
50
+ const keyString = typeof key === 'string' ? key : String(key);
51
+ const result = await storageRepository.setString(keyString, value);
52
+ return result.success;
53
+ }, []);
54
+
55
+ /**
56
+ * Remove item from storage
57
+ */
58
+ const removeItem = useCallback(async (key: string | StorageKey): Promise<boolean> => {
59
+ const keyString = typeof key === 'string' ? key : String(key);
60
+ const result = await storageRepository.removeItem(keyString);
61
+ return result.success;
62
+ }, []);
63
+
64
+ /**
65
+ * Check if item exists
66
+ */
67
+ const hasItem = useCallback(async (key: string | StorageKey): Promise<boolean> => {
68
+ const keyString = typeof key === 'string' ? key : String(key);
69
+ return storageRepository.hasItem(keyString);
70
+ }, []);
71
+
72
+ /**
73
+ * Clear all storage
74
+ */
75
+ const clearAll = useCallback(async (): Promise<boolean> => {
76
+ const result = await storageRepository.clearAll();
77
+ return result.success;
78
+ }, []);
79
+
80
+ /**
81
+ * Get item with full result (success/error)
82
+ */
83
+ const getItemWithResult = useCallback(async <T>(
84
+ key: string | StorageKey,
85
+ defaultValue: T
86
+ ): Promise<StorageResult<T>> => {
87
+ const keyString = typeof key === 'string' ? key : String(key);
88
+ return storageRepository.getItem(keyString, defaultValue);
89
+ }, []);
90
+
91
+ return {
92
+ getItem,
93
+ setItem,
94
+ getString,
95
+ setString,
96
+ removeItem,
97
+ hasItem,
98
+ clearAll,
99
+ getItemWithResult,
100
+ };
101
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * useStorageState Hook
3
+ *
4
+ * Domain-Driven Design: Presentation layer hook for state + storage sync
5
+ * Combines React state with automatic storage persistence
6
+ *
7
+ * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
8
+ */
9
+
10
+ import { useState, useEffect, useCallback } from 'react';
11
+ import { storageRepository } from '../../infrastructure/repositories/AsyncStorageRepository';
12
+ import { unwrap } from '../../domain/entities/StorageResult';
13
+ import type { StorageKey } from '../../domain/value-objects/StorageKey';
14
+
15
+ /**
16
+ * Storage State Hook
17
+ * Syncs React state with AsyncStorage automatically
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const [theme, setTheme] = useStorageState(StorageKey.THEME_MODE, 'light');
22
+ * // State is automatically persisted to storage
23
+ * ```
24
+ */
25
+ export const useStorageState = <T>(
26
+ key: string | StorageKey,
27
+ defaultValue: T
28
+ ): [T, (value: T) => Promise<void>, boolean] => {
29
+ const keyString = typeof key === 'string' ? key : String(key);
30
+ const [state, setState] = useState<T>(defaultValue);
31
+ const [isLoading, setIsLoading] = useState(true);
32
+
33
+ // Load initial value from storage
34
+ useEffect(() => {
35
+ const loadFromStorage = async () => {
36
+ const result = await storageRepository.getItem(keyString, defaultValue);
37
+ const value = unwrap(result, defaultValue);
38
+ setState(value);
39
+ setIsLoading(false);
40
+ };
41
+
42
+ loadFromStorage();
43
+ }, [keyString, defaultValue]);
44
+
45
+ // Update state and persist to storage
46
+ const updateState = useCallback(
47
+ async (value: T) => {
48
+ setState(value);
49
+ await storageRepository.setItem(keyString, value);
50
+ },
51
+ [keyString]
52
+ );
53
+
54
+ return [state, updateState, isLoading];
55
+ };