react-native-nitro-storage 0.1.0 → 0.1.1
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 +73 -7
- package/android/CMakeLists.txt +30 -24
- package/android/src/main/cpp/cpp-adapter.cpp +3 -2
- package/cpp/bindings/HybridStorage.cpp +9 -17
- package/ios/IOSStorageAdapterCpp.mm +28 -77
- package/lib/commonjs/index.js +64 -12
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/migration.js +39 -0
- package/lib/commonjs/migration.js.map +1 -0
- package/lib/module/index.js +63 -12
- package/lib/module/index.js.map +1 -1
- package/lib/module/migration.js +35 -0
- package/lib/module/migration.js.map +1 -0
- package/lib/typescript/index.d.ts +9 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/migration.d.ts +12 -0
- package/lib/typescript/migration.d.ts.map +1 -0
- package/package.json +2 -1
- package/src/index.ts +79 -16
- package/src/migration.ts +53 -0
package/README.md
CHANGED
|
@@ -139,17 +139,25 @@ const value = counterAtom.get(); // 42, instantly
|
|
|
139
139
|
|
|
140
140
|
_Replaces: Zustand, Jotai, Redux_
|
|
141
141
|
|
|
142
|
-
Fast, in-memory state.
|
|
142
|
+
Fast, in-memory state. **Now Pure JS** - can store complex objects, functions, and React nodes!
|
|
143
143
|
|
|
144
144
|
```typescript
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
// Store a function
|
|
146
|
+
const callbackAtom = createStorageItem({
|
|
147
|
+
key: "on-click",
|
|
147
148
|
scope: StorageScope.Memory,
|
|
148
|
-
defaultValue:
|
|
149
|
+
defaultValue: () => console.log("Clicked!"),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Store a React Component
|
|
153
|
+
const modalAtom = createStorageItem({
|
|
154
|
+
key: "active-modal",
|
|
155
|
+
scope: StorageScope.Memory,
|
|
156
|
+
defaultValue: <View />,
|
|
149
157
|
});
|
|
150
158
|
```
|
|
151
159
|
|
|
152
|
-
**Performance:** < 0.001ms per operation
|
|
160
|
+
**Performance:** < 0.001ms per operation (Zero JSI overhead)
|
|
153
161
|
|
|
154
162
|
### **Disk Storage**
|
|
155
163
|
|
|
@@ -299,6 +307,53 @@ const unsubscribe = counterAtom.subscribe(() => {
|
|
|
299
307
|
unsubscribe();
|
|
300
308
|
```
|
|
301
309
|
|
|
310
|
+
### Functional Updates
|
|
311
|
+
|
|
312
|
+
Update state based on the previous value, just like `useState`.
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// Increment counter
|
|
316
|
+
counterAtom.set((prev) => prev + 1);
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Optimized Writes
|
|
320
|
+
|
|
321
|
+
Use `useSetStorage` to set values without subscribing to updates (avoids re-renders).
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import { useSetStorage } from "react-native-nitro-storage";
|
|
325
|
+
|
|
326
|
+
function IncrementButton() {
|
|
327
|
+
const setCount = useSetStorage(counterAtom);
|
|
328
|
+
return <Button onPress={() => setCount((c) => c + 1)} title="+" />;
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Clearing Data
|
|
333
|
+
|
|
334
|
+
Clear all data in `Memory` scope (Native scopes coming soon).
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
import { storage } from "react-native-nitro-storage";
|
|
338
|
+
|
|
339
|
+
// Clear all memory state (e.g. on logout)
|
|
340
|
+
storage.clearAll();
|
|
341
|
+
|
|
342
|
+
// Or specific scope
|
|
343
|
+
storage.clear(StorageScope.Memory);
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Migration from MMKV
|
|
347
|
+
|
|
348
|
+
Easily migrate data from `react-native-mmkv` to Nitro Storage.
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
import { migrateFromMMKV } from "react-native-nitro-storage/src/migration";
|
|
352
|
+
|
|
353
|
+
// Migrate 'user-settings' and delete from MMKV
|
|
354
|
+
migrateFromMMKV(mmkvInstance, settingsAtom, true);
|
|
355
|
+
```
|
|
356
|
+
|
|
302
357
|
---
|
|
303
358
|
|
|
304
359
|
## 📊 Performance Benchmarks
|
|
@@ -344,7 +399,7 @@ Creates a storage atom.
|
|
|
344
399
|
**Methods:**
|
|
345
400
|
|
|
346
401
|
- `get(): T` - Get current value (synchronous)
|
|
347
|
-
- `set(value: T): void` - Set new value (synchronous)
|
|
402
|
+
- `set(value: T | ((prev: T) => T)): void` - Set new value (synchronous)
|
|
348
403
|
- `delete(): void` - Remove value (synchronous)
|
|
349
404
|
- `subscribe(callback: () => void): () => void` - Subscribe to changes
|
|
350
405
|
|
|
@@ -352,7 +407,18 @@ Creates a storage atom.
|
|
|
352
407
|
|
|
353
408
|
React hook using `useSyncExternalStore` for automatic re-renders.
|
|
354
409
|
|
|
355
|
-
**Returns:** `[value: T, setValue: (value: T) => void]`
|
|
410
|
+
**Returns:** `[value: T, setValue: (value: T | ((prev: T) => T)) => void]`
|
|
411
|
+
|
|
412
|
+
### `useSetStorage<T>(item: StorageItem<T>)`
|
|
413
|
+
|
|
414
|
+
Returns the setter function only. Does not subscribe to updates.
|
|
415
|
+
|
|
416
|
+
**Returns:** `(value: T | ((prev: T) => T)) => void`
|
|
417
|
+
|
|
418
|
+
### `storage` Object
|
|
419
|
+
|
|
420
|
+
- `clearAll()`: Clears all `Memory` storage.
|
|
421
|
+
- `clear(scope: StorageScope.Memory)`: Clears specific scope.
|
|
356
422
|
|
|
357
423
|
---
|
|
358
424
|
|
package/android/CMakeLists.txt
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
cmake_minimum_required(VERSION 3.
|
|
1
|
+
cmake_minimum_required(VERSION 3.18.0)
|
|
2
|
+
|
|
2
3
|
project(NitroStorage)
|
|
3
4
|
|
|
5
|
+
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
4
6
|
set(CMAKE_CXX_STANDARD 20)
|
|
5
|
-
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
-
set(CMAKE_C_STANDARD 11)
|
|
7
|
-
set(CMAKE_C_STANDARD_REQUIRED ON)
|
|
8
|
-
|
|
9
|
-
# Define the path to our C++ sources
|
|
10
|
-
set(CPP_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../cpp")
|
|
11
7
|
|
|
12
|
-
#
|
|
13
|
-
file(GLOB
|
|
14
|
-
|
|
8
|
+
# 1. Define source files (Manual implementation)
|
|
9
|
+
file(GLOB SOURCES
|
|
10
|
+
"../cpp/bindings/*.cpp"
|
|
11
|
+
"../cpp/core/*.cpp"
|
|
12
|
+
"./src/main/cpp/*.cpp"
|
|
13
|
+
)
|
|
15
14
|
|
|
16
|
-
# Create the
|
|
17
|
-
add_library(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
src/main/cpp/cpp-adapter.cpp
|
|
22
|
-
src/main/cpp/AndroidStorageAdapterCpp.cpp
|
|
15
|
+
# 2. Create the library target
|
|
16
|
+
add_library(
|
|
17
|
+
NitroStorage
|
|
18
|
+
SHARED
|
|
19
|
+
${SOURCES}
|
|
23
20
|
)
|
|
24
21
|
|
|
25
|
-
# Include directories
|
|
26
|
-
target_include_directories(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
# 3. Include directories for manual sources
|
|
23
|
+
target_include_directories(
|
|
24
|
+
NitroStorage
|
|
25
|
+
PRIVATE
|
|
26
|
+
"../cpp/core"
|
|
27
|
+
"../cpp/bindings"
|
|
28
|
+
"./src/main/cpp"
|
|
30
29
|
)
|
|
31
30
|
|
|
32
|
-
# Include
|
|
33
|
-
include(
|
|
31
|
+
# 4. Include Nitrogen Autolinking (adds generated sources, finding packages, linking libs)
|
|
32
|
+
include("../nitrogen/generated/android/NitroStorage+autolinking.cmake")
|
|
33
|
+
|
|
34
|
+
# 5. Link standard Android libraries
|
|
35
|
+
target_link_libraries(
|
|
36
|
+
NitroStorage
|
|
37
|
+
android
|
|
38
|
+
log
|
|
39
|
+
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include <jni.h>
|
|
2
|
-
#include
|
|
2
|
+
#include <fbjni/fbjni.h>
|
|
3
|
+
#include "../../../nitrogen/generated/android/NitroStorageOnLoad.hpp"
|
|
3
4
|
|
|
4
|
-
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*
|
|
5
|
+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
|
|
5
6
|
return margelo::nitro::NitroStorage::initialize(vm);
|
|
6
7
|
}
|
|
@@ -24,14 +24,7 @@ HybridStorage::HybridStorage(std::shared_ptr<::NitroStorage::NativeStorageAdapte
|
|
|
24
24
|
: HybridObject(TAG), HybridStorageSpec(), nativeAdapter_(std::move(adapter)) {}
|
|
25
25
|
|
|
26
26
|
HybridStorage::Scope HybridStorage::toScope(double scopeValue) {
|
|
27
|
-
|
|
28
|
-
if (intScope < 0 || intScope > 2) {
|
|
29
|
-
throw std::invalid_argument(
|
|
30
|
-
"Invalid storage scope: " + std::to_string(intScope) +
|
|
31
|
-
". Must be 0 (Memory), 1 (Disk), or 2 (Secure)"
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
return static_cast<Scope>(intScope);
|
|
27
|
+
return static_cast<Scope>(static_cast<int>(scopeValue));
|
|
35
28
|
}
|
|
36
29
|
|
|
37
30
|
void HybridStorage::set(const std::string& key, const std::string& value, double scope) {
|
|
@@ -100,8 +93,8 @@ std::function<void()> HybridStorage::addOnChange(
|
|
|
100
93
|
const std::function<void(const std::string&, const std::optional<std::string>&)>& callback
|
|
101
94
|
) {
|
|
102
95
|
int intScope = static_cast<int>(scope);
|
|
103
|
-
|
|
104
96
|
size_t listenerId;
|
|
97
|
+
|
|
105
98
|
{
|
|
106
99
|
std::lock_guard<std::mutex> lock(listenersMutex_);
|
|
107
100
|
listenerId = nextListenerId_++;
|
|
@@ -111,14 +104,12 @@ std::function<void()> HybridStorage::addOnChange(
|
|
|
111
104
|
return [this, intScope, listenerId]() {
|
|
112
105
|
std::lock_guard<std::mutex> lock(listenersMutex_);
|
|
113
106
|
auto& scopeListeners = listeners_[intScope];
|
|
114
|
-
scopeListeners.
|
|
115
|
-
|
|
116
|
-
scopeListeners.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
scopeListeners.end()
|
|
121
|
-
);
|
|
107
|
+
for (auto it = scopeListeners.begin(); it != scopeListeners.end(); ++it) {
|
|
108
|
+
if (it->id == listenerId) {
|
|
109
|
+
scopeListeners.erase(it);
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
122
113
|
};
|
|
123
114
|
}
|
|
124
115
|
|
|
@@ -132,6 +123,7 @@ void HybridStorage::notifyListeners(
|
|
|
132
123
|
std::lock_guard<std::mutex> lock(listenersMutex_);
|
|
133
124
|
auto it = listeners_.find(scope);
|
|
134
125
|
if (it != listeners_.end()) {
|
|
126
|
+
listenersCopy.reserve(it->second.size());
|
|
135
127
|
listenersCopy = it->second;
|
|
136
128
|
}
|
|
137
129
|
}
|
|
@@ -4,124 +4,75 @@
|
|
|
4
4
|
|
|
5
5
|
namespace NitroStorage {
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
static NSString* const kKeychainService = @"com.nitrostorage.keychain";
|
|
8
8
|
|
|
9
|
+
IOSStorageAdapterCpp::IOSStorageAdapterCpp() {}
|
|
9
10
|
IOSStorageAdapterCpp::~IOSStorageAdapterCpp() {}
|
|
10
11
|
|
|
11
12
|
void IOSStorageAdapterCpp::setDisk(const std::string& key, const std::string& value) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
[[NSUserDefaults standardUserDefaults] setObject:nsValue forKey:nsKey];
|
|
13
|
+
[[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithUTF8String:value.c_str()]
|
|
14
|
+
forKey:[NSString stringWithUTF8String:key.c_str()]];
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
std::optional<std::string> IOSStorageAdapterCpp::getDisk(const std::string& key) {
|
|
18
|
-
NSString*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (result) {
|
|
22
|
-
const char* utf8String = [result UTF8String];
|
|
23
|
-
if (utf8String) {
|
|
24
|
-
std::string value;
|
|
25
|
-
value.reserve([result lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
|
26
|
-
value.assign(utf8String);
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return std::nullopt;
|
|
18
|
+
NSString* result = [[NSUserDefaults standardUserDefaults] stringForKey:[NSString stringWithUTF8String:key.c_str()]];
|
|
19
|
+
if (!result) return std::nullopt;
|
|
20
|
+
return std::string([result UTF8String]);
|
|
31
21
|
}
|
|
32
22
|
|
|
33
23
|
void IOSStorageAdapterCpp::deleteDisk(const std::string& key) {
|
|
34
|
-
|
|
35
|
-
[[NSUserDefaults standardUserDefaults] removeObjectForKey:nsKey];
|
|
24
|
+
[[NSUserDefaults standardUserDefaults] removeObjectForKey:[NSString stringWithUTF8String:key.c_str()]];
|
|
36
25
|
}
|
|
37
26
|
|
|
38
27
|
void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string& value) {
|
|
39
28
|
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
NSString* service = @"com.nitrostorage.keychain";
|
|
44
|
-
|
|
29
|
+
NSData* data = [[NSString stringWithUTF8String:value.c_str()] dataUsingEncoding:NSUTF8StringEncoding];
|
|
30
|
+
|
|
45
31
|
NSDictionary* query = @{
|
|
46
32
|
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
47
|
-
(__bridge id)kSecAttrService:
|
|
33
|
+
(__bridge id)kSecAttrService: kKeychainService,
|
|
48
34
|
(__bridge id)kSecAttrAccount: nsKey
|
|
49
35
|
};
|
|
50
|
-
|
|
36
|
+
|
|
51
37
|
NSDictionary* updateAttributes = @{
|
|
52
38
|
(__bridge id)kSecValueData: data
|
|
53
39
|
};
|
|
40
|
+
|
|
41
|
+
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)updateAttributes);
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
NSDictionary* addQuery = @{
|
|
61
|
-
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
62
|
-
(__bridge id)kSecAttrService: service,
|
|
63
|
-
(__bridge id)kSecAttrAccount: nsKey,
|
|
64
|
-
(__bridge id)kSecValueData: data,
|
|
65
|
-
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
|
|
69
|
-
if (addStatus != errSecSuccess) {
|
|
70
|
-
NSLog(@"NitroStorage: Failed to add to Keychain for key '%@'. Error: %d", nsKey, (int)addStatus);
|
|
71
|
-
}
|
|
72
|
-
} else if (updateStatus != errSecSuccess) {
|
|
73
|
-
NSLog(@"NitroStorage: Failed to update Keychain item '%@'. Error: %d", nsKey, (int)updateStatus);
|
|
43
|
+
if (status == errSecItemNotFound) {
|
|
44
|
+
NSMutableDictionary* addQuery = [query mutableCopy];
|
|
45
|
+
addQuery[(__bridge id)kSecValueData] = data;
|
|
46
|
+
addQuery[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleWhenUnlocked;
|
|
47
|
+
SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
|
|
74
48
|
}
|
|
75
49
|
}
|
|
76
50
|
|
|
77
51
|
std::optional<std::string> IOSStorageAdapterCpp::getSecure(const std::string& key) {
|
|
78
|
-
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
79
|
-
NSString* service = @"com.nitrostorage.keychain";
|
|
80
|
-
|
|
81
52
|
NSDictionary* query = @{
|
|
82
53
|
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
83
|
-
(__bridge id)kSecAttrService:
|
|
84
|
-
(__bridge id)kSecAttrAccount:
|
|
54
|
+
(__bridge id)kSecAttrService: kKeychainService,
|
|
55
|
+
(__bridge id)kSecAttrAccount: [NSString stringWithUTF8String:key.c_str()],
|
|
85
56
|
(__bridge id)kSecReturnData: @YES,
|
|
86
57
|
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne
|
|
87
58
|
};
|
|
88
|
-
|
|
59
|
+
|
|
89
60
|
CFTypeRef result = NULL;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (status == errSecSuccess && result) {
|
|
61
|
+
if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == errSecSuccess && result) {
|
|
93
62
|
NSData* data = (__bridge_transfer NSData*)result;
|
|
94
|
-
NSString*
|
|
95
|
-
if (
|
|
96
|
-
const char* utf8String = [nsValue UTF8String];
|
|
97
|
-
if (utf8String) {
|
|
98
|
-
std::string value;
|
|
99
|
-
value.reserve([nsValue lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
|
100
|
-
value.assign(utf8String);
|
|
101
|
-
return value;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
} else if (status != errSecItemNotFound) {
|
|
105
|
-
NSLog(@"NitroStorage: Failed to read from Keychain for key '%@'. Error: %d", nsKey, (int)status);
|
|
63
|
+
NSString* str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
64
|
+
if (str) return std::string([str UTF8String]);
|
|
106
65
|
}
|
|
107
|
-
|
|
108
66
|
return std::nullopt;
|
|
109
67
|
}
|
|
110
68
|
|
|
111
69
|
void IOSStorageAdapterCpp::deleteSecure(const std::string& key) {
|
|
112
|
-
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
113
|
-
NSString* service = @"com.nitrostorage.keychain";
|
|
114
|
-
|
|
115
70
|
NSDictionary* query = @{
|
|
116
71
|
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
117
|
-
(__bridge id)kSecAttrService:
|
|
118
|
-
(__bridge id)kSecAttrAccount:
|
|
72
|
+
(__bridge id)kSecAttrService: kKeychainService,
|
|
73
|
+
(__bridge id)kSecAttrAccount: [NSString stringWithUTF8String:key.c_str()]
|
|
119
74
|
};
|
|
120
|
-
|
|
121
|
-
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
|
|
122
|
-
if (status != errSecSuccess && status != errSecItemNotFound) {
|
|
123
|
-
NSLog(@"NitroStorage: Failed to delete from Keychain for key '%@'. Error: %d", nsKey, (int)status);
|
|
124
|
-
}
|
|
75
|
+
SecItemDelete((__bridge CFDictionaryRef)query);
|
|
125
76
|
}
|
|
126
77
|
|
|
127
78
|
} // namespace NitroStorage
|
package/lib/commonjs/index.js
CHANGED
|
@@ -10,6 +10,8 @@ Object.defineProperty(exports, "StorageScope", {
|
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
12
|
exports.createStorageItem = createStorageItem;
|
|
13
|
+
exports.storage = void 0;
|
|
14
|
+
exports.useSetStorage = useSetStorage;
|
|
13
15
|
exports.useStorage = useStorage;
|
|
14
16
|
var _react = require("react");
|
|
15
17
|
var _reactNativeNitroModules = require("react-native-nitro-modules");
|
|
@@ -21,6 +23,19 @@ function getStorageModule() {
|
|
|
21
23
|
}
|
|
22
24
|
return _storageModule;
|
|
23
25
|
}
|
|
26
|
+
const memoryStore = new Map();
|
|
27
|
+
const memoryListeners = new Set();
|
|
28
|
+
function notifyMemoryListeners(key, value) {
|
|
29
|
+
memoryListeners.forEach(listener => listener(key, value));
|
|
30
|
+
}
|
|
31
|
+
const storage = exports.storage = {
|
|
32
|
+
clear: scope => {
|
|
33
|
+
memoryStore.clear();
|
|
34
|
+
},
|
|
35
|
+
clearAll: () => {
|
|
36
|
+
storage.clear(_StorageNitro.StorageScope.Memory);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
24
39
|
function defaultSerialize(value) {
|
|
25
40
|
return JSON.stringify(value);
|
|
26
41
|
}
|
|
@@ -30,21 +45,37 @@ function defaultDeserialize(value) {
|
|
|
30
45
|
function createStorageItem(config) {
|
|
31
46
|
const serialize = config.serialize ?? defaultSerialize;
|
|
32
47
|
const deserialize = config.deserialize ?? defaultDeserialize;
|
|
48
|
+
const isMemory = config.scope === _StorageNitro.StorageScope.Memory;
|
|
33
49
|
const listeners = new Set();
|
|
34
50
|
let unsubscribe = null;
|
|
35
51
|
const ensureSubscription = () => {
|
|
36
52
|
if (!unsubscribe) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
53
|
+
if (isMemory) {
|
|
54
|
+
const listener = key => {
|
|
55
|
+
if (key === config.key) {
|
|
56
|
+
listeners.forEach(l => l());
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
memoryListeners.add(listener);
|
|
60
|
+
unsubscribe = () => memoryListeners.delete(listener);
|
|
61
|
+
} else {
|
|
62
|
+
unsubscribe = getStorageModule().addOnChange(config.scope, key => {
|
|
63
|
+
if (key === config.key) {
|
|
64
|
+
listeners.forEach(listener => listener());
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
42
68
|
}
|
|
43
69
|
};
|
|
44
70
|
let lastRaw;
|
|
45
71
|
let lastValue;
|
|
46
72
|
const get = () => {
|
|
47
|
-
|
|
73
|
+
let raw;
|
|
74
|
+
if (isMemory) {
|
|
75
|
+
raw = memoryStore.get(config.key);
|
|
76
|
+
} else {
|
|
77
|
+
raw = getStorageModule().get(config.key, config.scope);
|
|
78
|
+
}
|
|
48
79
|
if (raw === lastRaw && lastValue !== undefined) {
|
|
49
80
|
return lastValue;
|
|
50
81
|
}
|
|
@@ -52,16 +83,32 @@ function createStorageItem(config) {
|
|
|
52
83
|
if (raw === undefined) {
|
|
53
84
|
lastValue = config.defaultValue;
|
|
54
85
|
} else {
|
|
55
|
-
|
|
86
|
+
if (isMemory) {
|
|
87
|
+
lastValue = raw;
|
|
88
|
+
} else {
|
|
89
|
+
lastValue = deserialize(raw);
|
|
90
|
+
}
|
|
56
91
|
}
|
|
57
92
|
return lastValue;
|
|
58
93
|
};
|
|
59
|
-
const set =
|
|
60
|
-
const
|
|
61
|
-
|
|
94
|
+
const set = valueOrFn => {
|
|
95
|
+
const currentValue = get();
|
|
96
|
+
const newValue = valueOrFn instanceof Function ? valueOrFn(currentValue) : valueOrFn;
|
|
97
|
+
if (isMemory) {
|
|
98
|
+
memoryStore.set(config.key, newValue);
|
|
99
|
+
notifyMemoryListeners(config.key, newValue);
|
|
100
|
+
} else {
|
|
101
|
+
const serialized = serialize(newValue);
|
|
102
|
+
getStorageModule().set(config.key, serialized, config.scope);
|
|
103
|
+
}
|
|
62
104
|
};
|
|
63
105
|
const deleteItem = () => {
|
|
64
|
-
|
|
106
|
+
if (isMemory) {
|
|
107
|
+
memoryStore.delete(config.key);
|
|
108
|
+
notifyMemoryListeners(config.key, undefined);
|
|
109
|
+
} else {
|
|
110
|
+
getStorageModule().remove(config.key, config.scope);
|
|
111
|
+
}
|
|
65
112
|
};
|
|
66
113
|
const subscribe = callback => {
|
|
67
114
|
ensureSubscription();
|
|
@@ -78,11 +125,16 @@ function createStorageItem(config) {
|
|
|
78
125
|
get,
|
|
79
126
|
set,
|
|
80
127
|
delete: deleteItem,
|
|
81
|
-
subscribe
|
|
128
|
+
subscribe,
|
|
129
|
+
scope: config.scope,
|
|
130
|
+
key: config.key
|
|
82
131
|
};
|
|
83
132
|
}
|
|
84
133
|
function useStorage(item) {
|
|
85
134
|
const value = (0, _react.useSyncExternalStore)(item.subscribe, item.get, item.get);
|
|
86
135
|
return [value, item.set];
|
|
87
136
|
}
|
|
137
|
+
function useSetStorage(item) {
|
|
138
|
+
return item.set;
|
|
139
|
+
}
|
|
88
140
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","require","_reactNativeNitroModules","_StorageNitro","_storageModule","getStorageModule","NitroModules","createHybridObject","
|
|
1
|
+
{"version":3,"names":["_react","require","_reactNativeNitroModules","_StorageNitro","_storageModule","getStorageModule","NitroModules","createHybridObject","memoryStore","Map","memoryListeners","Set","notifyMemoryListeners","key","value","forEach","listener","storage","exports","clear","scope","clearAll","StorageScopeEnum","Memory","defaultSerialize","JSON","stringify","defaultDeserialize","parse","createStorageItem","config","serialize","deserialize","isMemory","listeners","unsubscribe","ensureSubscription","l","add","delete","addOnChange","lastRaw","lastValue","get","raw","undefined","defaultValue","set","valueOrFn","currentValue","newValue","Function","serialized","deleteItem","remove","subscribe","callback","size","useStorage","item","useSyncExternalStore","useSetStorage"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.migrateFromMMKV = migrateFromMMKV;
|
|
7
|
+
function migrateFromMMKV(mmkv, item, deleteFromMMKV = false) {
|
|
8
|
+
const key = item.key;
|
|
9
|
+
if (!mmkv.contains(key)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const value = mmkv.getString(key);
|
|
13
|
+
if (value !== undefined) {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(value);
|
|
16
|
+
item.set(parsed);
|
|
17
|
+
} catch {
|
|
18
|
+
item.set(value);
|
|
19
|
+
}
|
|
20
|
+
if (deleteFromMMKV) {
|
|
21
|
+
mmkv.delete(key);
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
const num = mmkv.getNumber(key);
|
|
26
|
+
if (num !== undefined) {
|
|
27
|
+
item.set(num);
|
|
28
|
+
if (deleteFromMMKV) mmkv.delete(key);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
const bool = mmkv.getBoolean(key);
|
|
32
|
+
if (bool !== undefined) {
|
|
33
|
+
item.set(bool);
|
|
34
|
+
if (deleteFromMMKV) mmkv.delete(key);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["migrateFromMMKV","mmkv","item","deleteFromMMKV","key","contains","value","getString","undefined","parsed","JSON","parse","set","delete","num","getNumber","bool","getBoolean"],"sourceRoot":"../../src","sources":["migration.ts"],"mappings":";;;;;;AAWO,SAASA,eAAeA,CAC7BC,IAAc,EACdC,IAAoB,EACpBC,cAAc,GAAG,KAAK,EACb;EACT,MAAMC,GAAG,GAAGF,IAAI,CAACE,GAAG;EACpB,IAAI,CAACH,IAAI,CAACI,QAAQ,CAACD,GAAG,CAAC,EAAE;IACvB,OAAO,KAAK;EACd;EAEA,MAAME,KAAK,GAAGL,IAAI,CAACM,SAAS,CAACH,GAAG,CAAC;EAEjC,IAAIE,KAAK,KAAKE,SAAS,EAAE;IACvB,IAAI;MACF,MAAMC,MAAM,GAAGC,IAAI,CAACC,KAAK,CAACL,KAAK,CAAC;MAChCJ,IAAI,CAACU,GAAG,CAACH,MAAM,CAAC;IAClB,CAAC,CAAC,MAAM;MACNP,IAAI,CAACU,GAAG,CAACN,KAAU,CAAC;IACtB;IAEA,IAAIH,cAAc,EAAE;MAClBF,IAAI,CAACY,MAAM,CAACT,GAAG,CAAC;IAClB;IACA,OAAO,IAAI;EACb;EAEA,MAAMU,GAAG,GAAGb,IAAI,CAACc,SAAS,CAACX,GAAG,CAAC;EAC/B,IAAIU,GAAG,KAAKN,SAAS,EAAE;IACrBN,IAAI,CAACU,GAAG,CAACE,GAAQ,CAAC;IAClB,IAAIX,cAAc,EAAEF,IAAI,CAACY,MAAM,CAACT,GAAG,CAAC;IACpC,OAAO,IAAI;EACb;EAEA,MAAMY,IAAI,GAAGf,IAAI,CAACgB,UAAU,CAACb,GAAG,CAAC;EACjC,IAAIY,IAAI,KAAKR,SAAS,EAAE;IACtBN,IAAI,CAACU,GAAG,CAACI,IAAS,CAAC;IACnB,IAAIb,cAAc,EAAEF,IAAI,CAACY,MAAM,CAACT,GAAG,CAAC;IACpC,OAAO,IAAI;EACb;EAEA,OAAO,KAAK;AACd","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useSyncExternalStore } from "react";
|
|
4
4
|
import { NitroModules } from "react-native-nitro-modules";
|
|
5
|
+
import { StorageScope as StorageScopeEnum } from "./Storage.nitro.js";
|
|
5
6
|
export { StorageScope } from "./Storage.nitro.js";
|
|
6
7
|
let _storageModule = null;
|
|
7
8
|
function getStorageModule() {
|
|
@@ -10,6 +11,19 @@ function getStorageModule() {
|
|
|
10
11
|
}
|
|
11
12
|
return _storageModule;
|
|
12
13
|
}
|
|
14
|
+
const memoryStore = new Map();
|
|
15
|
+
const memoryListeners = new Set();
|
|
16
|
+
function notifyMemoryListeners(key, value) {
|
|
17
|
+
memoryListeners.forEach(listener => listener(key, value));
|
|
18
|
+
}
|
|
19
|
+
export const storage = {
|
|
20
|
+
clear: scope => {
|
|
21
|
+
memoryStore.clear();
|
|
22
|
+
},
|
|
23
|
+
clearAll: () => {
|
|
24
|
+
storage.clear(StorageScopeEnum.Memory);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
13
27
|
function defaultSerialize(value) {
|
|
14
28
|
return JSON.stringify(value);
|
|
15
29
|
}
|
|
@@ -19,21 +33,37 @@ function defaultDeserialize(value) {
|
|
|
19
33
|
export function createStorageItem(config) {
|
|
20
34
|
const serialize = config.serialize ?? defaultSerialize;
|
|
21
35
|
const deserialize = config.deserialize ?? defaultDeserialize;
|
|
36
|
+
const isMemory = config.scope === StorageScopeEnum.Memory;
|
|
22
37
|
const listeners = new Set();
|
|
23
38
|
let unsubscribe = null;
|
|
24
39
|
const ensureSubscription = () => {
|
|
25
40
|
if (!unsubscribe) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
if (isMemory) {
|
|
42
|
+
const listener = key => {
|
|
43
|
+
if (key === config.key) {
|
|
44
|
+
listeners.forEach(l => l());
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
memoryListeners.add(listener);
|
|
48
|
+
unsubscribe = () => memoryListeners.delete(listener);
|
|
49
|
+
} else {
|
|
50
|
+
unsubscribe = getStorageModule().addOnChange(config.scope, key => {
|
|
51
|
+
if (key === config.key) {
|
|
52
|
+
listeners.forEach(listener => listener());
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
31
56
|
}
|
|
32
57
|
};
|
|
33
58
|
let lastRaw;
|
|
34
59
|
let lastValue;
|
|
35
60
|
const get = () => {
|
|
36
|
-
|
|
61
|
+
let raw;
|
|
62
|
+
if (isMemory) {
|
|
63
|
+
raw = memoryStore.get(config.key);
|
|
64
|
+
} else {
|
|
65
|
+
raw = getStorageModule().get(config.key, config.scope);
|
|
66
|
+
}
|
|
37
67
|
if (raw === lastRaw && lastValue !== undefined) {
|
|
38
68
|
return lastValue;
|
|
39
69
|
}
|
|
@@ -41,16 +71,32 @@ export function createStorageItem(config) {
|
|
|
41
71
|
if (raw === undefined) {
|
|
42
72
|
lastValue = config.defaultValue;
|
|
43
73
|
} else {
|
|
44
|
-
|
|
74
|
+
if (isMemory) {
|
|
75
|
+
lastValue = raw;
|
|
76
|
+
} else {
|
|
77
|
+
lastValue = deserialize(raw);
|
|
78
|
+
}
|
|
45
79
|
}
|
|
46
80
|
return lastValue;
|
|
47
81
|
};
|
|
48
|
-
const set =
|
|
49
|
-
const
|
|
50
|
-
|
|
82
|
+
const set = valueOrFn => {
|
|
83
|
+
const currentValue = get();
|
|
84
|
+
const newValue = valueOrFn instanceof Function ? valueOrFn(currentValue) : valueOrFn;
|
|
85
|
+
if (isMemory) {
|
|
86
|
+
memoryStore.set(config.key, newValue);
|
|
87
|
+
notifyMemoryListeners(config.key, newValue);
|
|
88
|
+
} else {
|
|
89
|
+
const serialized = serialize(newValue);
|
|
90
|
+
getStorageModule().set(config.key, serialized, config.scope);
|
|
91
|
+
}
|
|
51
92
|
};
|
|
52
93
|
const deleteItem = () => {
|
|
53
|
-
|
|
94
|
+
if (isMemory) {
|
|
95
|
+
memoryStore.delete(config.key);
|
|
96
|
+
notifyMemoryListeners(config.key, undefined);
|
|
97
|
+
} else {
|
|
98
|
+
getStorageModule().remove(config.key, config.scope);
|
|
99
|
+
}
|
|
54
100
|
};
|
|
55
101
|
const subscribe = callback => {
|
|
56
102
|
ensureSubscription();
|
|
@@ -67,11 +113,16 @@ export function createStorageItem(config) {
|
|
|
67
113
|
get,
|
|
68
114
|
set,
|
|
69
115
|
delete: deleteItem,
|
|
70
|
-
subscribe
|
|
116
|
+
subscribe,
|
|
117
|
+
scope: config.scope,
|
|
118
|
+
key: config.key
|
|
71
119
|
};
|
|
72
120
|
}
|
|
73
121
|
export function useStorage(item) {
|
|
74
122
|
const value = useSyncExternalStore(item.subscribe, item.get, item.get);
|
|
75
123
|
return [value, item.set];
|
|
76
124
|
}
|
|
125
|
+
export function useSetStorage(item) {
|
|
126
|
+
return item.set;
|
|
127
|
+
}
|
|
77
128
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useSyncExternalStore","NitroModules","StorageScope","_storageModule","getStorageModule","createHybridObject","
|
|
1
|
+
{"version":3,"names":["useSyncExternalStore","NitroModules","StorageScope","StorageScopeEnum","_storageModule","getStorageModule","createHybridObject","memoryStore","Map","memoryListeners","Set","notifyMemoryListeners","key","value","forEach","listener","storage","clear","scope","clearAll","Memory","defaultSerialize","JSON","stringify","defaultDeserialize","parse","createStorageItem","config","serialize","deserialize","isMemory","listeners","unsubscribe","ensureSubscription","l","add","delete","addOnChange","lastRaw","lastValue","get","raw","undefined","defaultValue","set","valueOrFn","currentValue","newValue","Function","serialized","deleteItem","remove","subscribe","callback","size","useStorage","item","useSetStorage"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
export function migrateFromMMKV(mmkv, item, deleteFromMMKV = false) {
|
|
4
|
+
const key = item.key;
|
|
5
|
+
if (!mmkv.contains(key)) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const value = mmkv.getString(key);
|
|
9
|
+
if (value !== undefined) {
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(value);
|
|
12
|
+
item.set(parsed);
|
|
13
|
+
} catch {
|
|
14
|
+
item.set(value);
|
|
15
|
+
}
|
|
16
|
+
if (deleteFromMMKV) {
|
|
17
|
+
mmkv.delete(key);
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
const num = mmkv.getNumber(key);
|
|
22
|
+
if (num !== undefined) {
|
|
23
|
+
item.set(num);
|
|
24
|
+
if (deleteFromMMKV) mmkv.delete(key);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
const bool = mmkv.getBoolean(key);
|
|
28
|
+
if (bool !== undefined) {
|
|
29
|
+
item.set(bool);
|
|
30
|
+
if (deleteFromMMKV) mmkv.delete(key);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["migrateFromMMKV","mmkv","item","deleteFromMMKV","key","contains","value","getString","undefined","parsed","JSON","parse","set","delete","num","getNumber","bool","getBoolean"],"sourceRoot":"../../src","sources":["migration.ts"],"mappings":";;AAWA,OAAO,SAASA,eAAeA,CAC7BC,IAAc,EACdC,IAAoB,EACpBC,cAAc,GAAG,KAAK,EACb;EACT,MAAMC,GAAG,GAAGF,IAAI,CAACE,GAAG;EACpB,IAAI,CAACH,IAAI,CAACI,QAAQ,CAACD,GAAG,CAAC,EAAE;IACvB,OAAO,KAAK;EACd;EAEA,MAAME,KAAK,GAAGL,IAAI,CAACM,SAAS,CAACH,GAAG,CAAC;EAEjC,IAAIE,KAAK,KAAKE,SAAS,EAAE;IACvB,IAAI;MACF,MAAMC,MAAM,GAAGC,IAAI,CAACC,KAAK,CAACL,KAAK,CAAC;MAChCJ,IAAI,CAACU,GAAG,CAACH,MAAM,CAAC;IAClB,CAAC,CAAC,MAAM;MACNP,IAAI,CAACU,GAAG,CAACN,KAAU,CAAC;IACtB;IAEA,IAAIH,cAAc,EAAE;MAClBF,IAAI,CAACY,MAAM,CAACT,GAAG,CAAC;IAClB;IACA,OAAO,IAAI;EACb;EAEA,MAAMU,GAAG,GAAGb,IAAI,CAACc,SAAS,CAACX,GAAG,CAAC;EAC/B,IAAIU,GAAG,KAAKN,SAAS,EAAE;IACrBN,IAAI,CAACU,GAAG,CAACE,GAAQ,CAAC;IAClB,IAAIX,cAAc,EAAEF,IAAI,CAACY,MAAM,CAACT,GAAG,CAAC;IACpC,OAAO,IAAI;EACb;EAEA,MAAMY,IAAI,GAAGf,IAAI,CAACgB,UAAU,CAACb,GAAG,CAAC;EACjC,IAAIY,IAAI,KAAKR,SAAS,EAAE;IACtBN,IAAI,CAACU,GAAG,CAACI,IAAS,CAAC;IACnB,IAAIb,cAAc,EAAEF,IAAI,CAACY,MAAM,CAACT,GAAG,CAAC;IACpC,OAAO,IAAI;EACb;EAEA,OAAO,KAAK;AACd","ignoreList":[]}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { StorageScope } from "./Storage.nitro";
|
|
2
2
|
export { StorageScope } from "./Storage.nitro";
|
|
3
3
|
export type { Storage } from "./Storage.nitro";
|
|
4
|
+
export declare const storage: {
|
|
5
|
+
clear: (scope: StorageScope.Memory) => void;
|
|
6
|
+
clearAll: () => void;
|
|
7
|
+
};
|
|
4
8
|
export interface StorageItemConfig<T> {
|
|
5
9
|
key: string;
|
|
6
10
|
scope: StorageScope;
|
|
@@ -10,10 +14,13 @@ export interface StorageItemConfig<T> {
|
|
|
10
14
|
}
|
|
11
15
|
export interface StorageItem<T> {
|
|
12
16
|
get: () => T;
|
|
13
|
-
set: (value: T) => void;
|
|
17
|
+
set: (value: T | ((prev: T) => T)) => void;
|
|
14
18
|
delete: () => void;
|
|
15
19
|
subscribe: (callback: () => void) => () => void;
|
|
20
|
+
scope: StorageScope;
|
|
21
|
+
key: string;
|
|
16
22
|
}
|
|
17
23
|
export declare function createStorageItem<T = undefined>(config: StorageItemConfig<T>): StorageItem<T>;
|
|
18
|
-
export declare function useStorage<T>(item: StorageItem<T>): [T, (value: T) => void];
|
|
24
|
+
export declare function useStorage<T>(item: StorageItem<T>): [T, (value: T | ((prev: T) => T)) => void];
|
|
25
|
+
export declare function useSetStorage<T>(item: StorageItem<T>): (value: T | ((prev: T) => T)) => void;
|
|
19
26
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAkB/C,eAAO,MAAM,OAAO;mBACH,YAAY,CAAC,MAAM;;CAMnC,CAAC;AAEF,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,YAAY,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC;IACjC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC,CAAC;IACb,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC;IAC3C,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IAChD,KAAK,EAAE,YAAY,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAUD,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,SAAS,EAC7C,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAC3B,WAAW,CAAC,CAAC,CAAC,CAwGhB;AAED,wBAAgB,UAAU,CAAC,CAAC,EAC1B,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,GACnB,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAG5C;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,oCAlIb,IAAI,CAoI3C"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StorageItem } from "./index";
|
|
2
|
+
interface MMKVLike {
|
|
3
|
+
getString: (key: string) => string | undefined;
|
|
4
|
+
getNumber: (key: string) => number | undefined;
|
|
5
|
+
getBoolean: (key: string) => boolean | undefined;
|
|
6
|
+
contains: (key: string) => boolean;
|
|
7
|
+
delete: (key: string) => void;
|
|
8
|
+
getAllKeys: () => string[];
|
|
9
|
+
}
|
|
10
|
+
export declare function migrateFromMMKV<T>(mmkv: MMKVLike, item: StorageItem<T>, deleteFromMMKV?: boolean): boolean;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../src/migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,UAAU,QAAQ;IAChB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC/C,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC/C,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IACjD,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAgB,eAAe,CAAC,CAAC,EAC/B,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,cAAc,UAAQ,GACrB,OAAO,CAqCT"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-storage",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "The fastest, most complete storage solution for React Native. Synchronous Memory, Disk, and Secure storage in one unified API. Built with Nitro Modules.",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
7
7
|
"types": "lib/typescript/index.d.ts",
|
|
8
8
|
"react-native": "src/index.ts",
|
|
9
9
|
"source": "src/index.ts",
|
|
10
|
+
"app": "app.plugin.js",
|
|
10
11
|
"files": [
|
|
11
12
|
"src",
|
|
12
13
|
"lib",
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { useSyncExternalStore } from "react";
|
|
1
|
+
import { useSyncExternalStore, useMemo } from "react";
|
|
2
2
|
import { NitroModules } from "react-native-nitro-modules";
|
|
3
3
|
import type { Storage, StorageScope } from "./Storage.nitro";
|
|
4
|
+
import { StorageScope as StorageScopeEnum } from "./Storage.nitro";
|
|
4
5
|
|
|
5
6
|
export { StorageScope } from "./Storage.nitro";
|
|
6
7
|
export type { Storage } from "./Storage.nitro";
|
|
@@ -14,6 +15,22 @@ function getStorageModule(): Storage {
|
|
|
14
15
|
return _storageModule!;
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
const memoryStore = new Map<string, any>();
|
|
19
|
+
const memoryListeners = new Set<(key: string, value: any) => void>();
|
|
20
|
+
|
|
21
|
+
function notifyMemoryListeners(key: string, value: any) {
|
|
22
|
+
memoryListeners.forEach((listener) => listener(key, value));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const storage = {
|
|
26
|
+
clear: (scope: StorageScope.Memory) => {
|
|
27
|
+
memoryStore.clear();
|
|
28
|
+
},
|
|
29
|
+
clearAll: () => {
|
|
30
|
+
storage.clear(StorageScopeEnum.Memory);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
17
34
|
export interface StorageItemConfig<T> {
|
|
18
35
|
key: string;
|
|
19
36
|
scope: StorageScope;
|
|
@@ -24,9 +41,11 @@ export interface StorageItemConfig<T> {
|
|
|
24
41
|
|
|
25
42
|
export interface StorageItem<T> {
|
|
26
43
|
get: () => T;
|
|
27
|
-
set: (value: T) => void;
|
|
44
|
+
set: (value: T | ((prev: T) => T)) => void;
|
|
28
45
|
delete: () => void;
|
|
29
46
|
subscribe: (callback: () => void) => () => void;
|
|
47
|
+
scope: StorageScope;
|
|
48
|
+
key: string;
|
|
30
49
|
}
|
|
31
50
|
|
|
32
51
|
function defaultSerialize<T>(value: T): string {
|
|
@@ -42,26 +61,42 @@ export function createStorageItem<T = undefined>(
|
|
|
42
61
|
): StorageItem<T> {
|
|
43
62
|
const serialize = config.serialize ?? defaultSerialize;
|
|
44
63
|
const deserialize = config.deserialize ?? defaultDeserialize;
|
|
64
|
+
const isMemory = config.scope === StorageScopeEnum.Memory;
|
|
45
65
|
|
|
46
66
|
const listeners = new Set<() => void>();
|
|
47
|
-
|
|
48
67
|
let unsubscribe: (() => void) | null = null;
|
|
49
68
|
|
|
50
69
|
const ensureSubscription = () => {
|
|
51
70
|
if (!unsubscribe) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
71
|
+
if (isMemory) {
|
|
72
|
+
const listener = (key: string) => {
|
|
73
|
+
if (key === config.key) {
|
|
74
|
+
listeners.forEach((l) => l());
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
memoryListeners.add(listener);
|
|
78
|
+
unsubscribe = () => memoryListeners.delete(listener);
|
|
79
|
+
} else {
|
|
80
|
+
unsubscribe = getStorageModule().addOnChange(config.scope, (key) => {
|
|
81
|
+
if (key === config.key) {
|
|
82
|
+
listeners.forEach((listener) => listener());
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
57
86
|
}
|
|
58
87
|
};
|
|
59
88
|
|
|
60
|
-
let lastRaw: string | undefined;
|
|
89
|
+
let lastRaw: string | any | undefined;
|
|
61
90
|
let lastValue: T | undefined;
|
|
62
91
|
|
|
63
92
|
const get = (): T => {
|
|
64
|
-
|
|
93
|
+
let raw: string | any;
|
|
94
|
+
|
|
95
|
+
if (isMemory) {
|
|
96
|
+
raw = memoryStore.get(config.key);
|
|
97
|
+
} else {
|
|
98
|
+
raw = getStorageModule().get(config.key, config.scope);
|
|
99
|
+
}
|
|
65
100
|
|
|
66
101
|
if (raw === lastRaw && lastValue !== undefined) {
|
|
67
102
|
return lastValue;
|
|
@@ -72,19 +107,39 @@ export function createStorageItem<T = undefined>(
|
|
|
72
107
|
if (raw === undefined) {
|
|
73
108
|
lastValue = config.defaultValue as T;
|
|
74
109
|
} else {
|
|
75
|
-
|
|
110
|
+
if (isMemory) {
|
|
111
|
+
lastValue = raw as T;
|
|
112
|
+
} else {
|
|
113
|
+
lastValue = deserialize(raw);
|
|
114
|
+
}
|
|
76
115
|
}
|
|
77
116
|
|
|
78
117
|
return lastValue;
|
|
79
118
|
};
|
|
80
119
|
|
|
81
|
-
const set = (
|
|
82
|
-
const
|
|
83
|
-
|
|
120
|
+
const set = (valueOrFn: T | ((prev: T) => T)): void => {
|
|
121
|
+
const currentValue = get();
|
|
122
|
+
const newValue =
|
|
123
|
+
valueOrFn instanceof Function
|
|
124
|
+
? (valueOrFn as Function)(currentValue)
|
|
125
|
+
: valueOrFn;
|
|
126
|
+
|
|
127
|
+
if (isMemory) {
|
|
128
|
+
memoryStore.set(config.key, newValue);
|
|
129
|
+
notifyMemoryListeners(config.key, newValue);
|
|
130
|
+
} else {
|
|
131
|
+
const serialized = serialize(newValue);
|
|
132
|
+
getStorageModule().set(config.key, serialized, config.scope);
|
|
133
|
+
}
|
|
84
134
|
};
|
|
85
135
|
|
|
86
136
|
const deleteItem = (): void => {
|
|
87
|
-
|
|
137
|
+
if (isMemory) {
|
|
138
|
+
memoryStore.delete(config.key);
|
|
139
|
+
notifyMemoryListeners(config.key, undefined);
|
|
140
|
+
} else {
|
|
141
|
+
getStorageModule().remove(config.key, config.scope);
|
|
142
|
+
}
|
|
88
143
|
};
|
|
89
144
|
|
|
90
145
|
const subscribe = (callback: () => void): (() => void) => {
|
|
@@ -104,10 +159,18 @@ export function createStorageItem<T = undefined>(
|
|
|
104
159
|
set,
|
|
105
160
|
delete: deleteItem,
|
|
106
161
|
subscribe,
|
|
162
|
+
scope: config.scope,
|
|
163
|
+
key: config.key,
|
|
107
164
|
};
|
|
108
165
|
}
|
|
109
166
|
|
|
110
|
-
export function useStorage<T>(
|
|
167
|
+
export function useStorage<T>(
|
|
168
|
+
item: StorageItem<T>
|
|
169
|
+
): [T, (value: T | ((prev: T) => T)) => void] {
|
|
111
170
|
const value = useSyncExternalStore(item.subscribe, item.get, item.get);
|
|
112
171
|
return [value, item.set];
|
|
113
172
|
}
|
|
173
|
+
|
|
174
|
+
export function useSetStorage<T>(item: StorageItem<T>) {
|
|
175
|
+
return item.set;
|
|
176
|
+
}
|
package/src/migration.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { StorageItem } from "./index";
|
|
2
|
+
|
|
3
|
+
interface MMKVLike {
|
|
4
|
+
getString: (key: string) => string | undefined;
|
|
5
|
+
getNumber: (key: string) => number | undefined;
|
|
6
|
+
getBoolean: (key: string) => boolean | undefined;
|
|
7
|
+
contains: (key: string) => boolean;
|
|
8
|
+
delete: (key: string) => void;
|
|
9
|
+
getAllKeys: () => string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function migrateFromMMKV<T>(
|
|
13
|
+
mmkv: MMKVLike,
|
|
14
|
+
item: StorageItem<T>,
|
|
15
|
+
deleteFromMMKV = false
|
|
16
|
+
): boolean {
|
|
17
|
+
const key = item.key;
|
|
18
|
+
if (!mmkv.contains(key)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const value = mmkv.getString(key);
|
|
23
|
+
|
|
24
|
+
if (value !== undefined) {
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(value);
|
|
27
|
+
item.set(parsed);
|
|
28
|
+
} catch {
|
|
29
|
+
item.set(value as T);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (deleteFromMMKV) {
|
|
33
|
+
mmkv.delete(key);
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const num = mmkv.getNumber(key);
|
|
39
|
+
if (num !== undefined) {
|
|
40
|
+
item.set(num as T);
|
|
41
|
+
if (deleteFromMMKV) mmkv.delete(key);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const bool = mmkv.getBoolean(key);
|
|
46
|
+
if (bool !== undefined) {
|
|
47
|
+
item.set(bool as T);
|
|
48
|
+
if (deleteFromMMKV) mmkv.delete(key);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return false;
|
|
53
|
+
}
|