react-native-nitro-storage 0.3.0 → 0.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 +594 -247
- package/android/CMakeLists.txt +2 -0
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +102 -11
- package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +16 -0
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +154 -34
- package/android/src/main/java/com/nitrostorage/NitroStoragePackage.kt +2 -2
- package/cpp/bindings/HybridStorage.cpp +176 -21
- package/cpp/bindings/HybridStorage.hpp +29 -2
- package/cpp/core/NativeStorageAdapter.hpp +16 -0
- package/ios/IOSStorageAdapterCpp.hpp +20 -0
- package/ios/IOSStorageAdapterCpp.mm +239 -32
- package/lib/commonjs/Storage.types.js +23 -1
- package/lib/commonjs/Storage.types.js.map +1 -1
- package/lib/commonjs/index.js +292 -75
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +473 -86
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/internal.js +10 -0
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/commonjs/storage-hooks.js +36 -0
- package/lib/commonjs/storage-hooks.js.map +1 -0
- package/lib/module/Storage.types.js +22 -0
- package/lib/module/Storage.types.js.map +1 -1
- package/lib/module/index.js +264 -75
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +445 -86
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/internal.js +8 -0
- package/lib/module/internal.js.map +1 -1
- package/lib/module/storage-hooks.js +30 -0
- package/lib/module/storage-hooks.js.map +1 -0
- package/lib/typescript/Storage.nitro.d.ts +12 -0
- package/lib/typescript/Storage.nitro.d.ts.map +1 -1
- package/lib/typescript/Storage.types.d.ts +20 -0
- package/lib/typescript/Storage.types.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +33 -10
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +45 -10
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/internal.d.ts +2 -0
- package/lib/typescript/internal.d.ts.map +1 -1
- package/lib/typescript/storage-hooks.d.ts +10 -0
- package/lib/typescript/storage-hooks.d.ts.map +1 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +12 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +12 -0
- package/package.json +8 -3
- package/src/Storage.nitro.ts +13 -2
- package/src/Storage.types.ts +22 -0
- package/src/index.ts +382 -123
- package/src/index.web.ts +618 -134
- package/src/internal.ts +14 -4
- package/src/migration.ts +1 -1
- package/src/storage-hooks.ts +48 -0
package/README.md
CHANGED
|
@@ -1,12 +1,49 @@
|
|
|
1
1
|
# react-native-nitro-storage
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The fastest, most complete storage solution for React Native.
|
|
4
|
+
Synchronous Memory, Disk, and Secure storage in one unified API — powered by [Nitro Modules](https://github.com/mrousavy/nitro) and JSI.
|
|
5
|
+
|
|
6
|
+
## Highlights
|
|
7
|
+
|
|
8
|
+
- **Three storage scopes** — in-memory, persistent disk, and hardware-encrypted secure storage
|
|
9
|
+
- **Synchronous reads & writes** — no `async/await`, no bridge, zero serialization overhead for primitives
|
|
10
|
+
- **React hooks** — `useStorage`, `useStorageSelector`, `useSetStorage` with automatic re-renders
|
|
11
|
+
- **Type-safe** — full TypeScript generics, custom serializers, schema validation with fallback
|
|
12
|
+
- **Namespaces** — isolate keys by feature, user, or tenant with automatic prefixing
|
|
13
|
+
- **TTL expiration** — time-based auto-expiry with optional `onExpired` callback
|
|
14
|
+
- **Biometric storage** — hardware-backed biometric protection on iOS & Android
|
|
15
|
+
- **Auth storage factory** — `createSecureAuthStorage` for multi-token auth flows
|
|
16
|
+
- **Batch operations** — atomic multi-key get/set/remove via native batch APIs
|
|
17
|
+
- **Transactions** — grouped writes with automatic rollback on error
|
|
18
|
+
- **Migrations** — versioned data migrations with `registerMigration` / `migrateToLatest`
|
|
19
|
+
- **MMKV migration** — drop-in `migrateFromMMKV` for painless migration from MMKV
|
|
20
|
+
- **Cross-platform** — iOS, Android, and web (`localStorage` fallback)
|
|
21
|
+
|
|
22
|
+
## Feature Coverage
|
|
23
|
+
|
|
24
|
+
Every feature in this package is documented with at least one runnable example in this README:
|
|
25
|
+
|
|
26
|
+
- Core item API (`createStorageItem`, `get/set/delete/has/subscribe`) — see Quick Start and Low-level subscription use case
|
|
27
|
+
- Hooks (`useStorage`, `useStorageSelector`, `useSetStorage`) — see Quick Start and Persisted User Preferences
|
|
28
|
+
- Scopes (`Memory`, `Disk`, `Secure`) — see Storage Scopes and multiple use cases
|
|
29
|
+
- Namespaces — see Multi-Tenant / Namespaced Storage
|
|
30
|
+
- TTL expiration + callbacks — see OTP / Temporary Codes
|
|
31
|
+
- Validation + recovery — see Feature Flags with Validation
|
|
32
|
+
- Biometric + access control — see Biometric-protected Secrets
|
|
33
|
+
- Global storage utilities (`clear*`, `has`, `getAll*`, `size`, secure write settings) — see Global utility examples and Storage Snapshots and Cleanup
|
|
34
|
+
- Batch APIs (`getBatch`, `setBatch`, `removeBatch`) — see Batch Operations and Bulk Bootstrap with Batch APIs
|
|
35
|
+
- Transactions — see Transactions and Atomic Balance Transfer
|
|
36
|
+
- Migrations (`registerMigration`, `migrateToLatest`) — see Migrations
|
|
37
|
+
- MMKV migration (`migrateFromMMKV`) — see MMKV Migration and Migrating From MMKV
|
|
38
|
+
- Auth storage factory (`createSecureAuthStorage`) — see Auth Token Management
|
|
4
39
|
|
|
5
40
|
## Requirements
|
|
6
41
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
42
|
+
| Dependency | Version |
|
|
43
|
+
| ---------------------------- | ----------- |
|
|
44
|
+
| `react-native` | `>= 0.75.0` |
|
|
45
|
+
| `react-native-nitro-modules` | `>= 0.33.9` |
|
|
46
|
+
| `react` | `>= 18.2.0` |
|
|
10
47
|
|
|
11
48
|
## Installation
|
|
12
49
|
|
|
@@ -14,13 +51,19 @@ Synchronous storage for React Native with a unified API for memory, disk, and se
|
|
|
14
51
|
bun add react-native-nitro-storage react-native-nitro-modules
|
|
15
52
|
```
|
|
16
53
|
|
|
54
|
+
or:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm install react-native-nitro-storage react-native-nitro-modules
|
|
58
|
+
```
|
|
59
|
+
|
|
17
60
|
### Expo
|
|
18
61
|
|
|
19
62
|
```bash
|
|
20
63
|
bunx expo install react-native-nitro-storage react-native-nitro-modules
|
|
21
64
|
```
|
|
22
65
|
|
|
23
|
-
`app.json`:
|
|
66
|
+
Add the config plugin to `app.json`:
|
|
24
67
|
|
|
25
68
|
```json
|
|
26
69
|
{
|
|
@@ -38,12 +81,9 @@ bunx expo install react-native-nitro-storage react-native-nitro-modules
|
|
|
38
81
|
}
|
|
39
82
|
```
|
|
40
83
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- If `faceIDPermission` is omitted, the plugin sets a default only when `NSFaceIDUsageDescription` is missing.
|
|
44
|
-
- Android biometric permissions are opt-in via `addBiometricPermissions: true`.
|
|
84
|
+
> `faceIDPermission` sets `NSFaceIDUsageDescription` only when missing. Android biometric permissions are opt-in via `addBiometricPermissions: true`.
|
|
45
85
|
|
|
46
|
-
Then:
|
|
86
|
+
Then run:
|
|
47
87
|
|
|
48
88
|
```bash
|
|
49
89
|
bunx expo prebuild
|
|
@@ -51,13 +91,13 @@ bunx expo prebuild
|
|
|
51
91
|
|
|
52
92
|
### Bare React Native
|
|
53
93
|
|
|
54
|
-
iOS
|
|
94
|
+
**iOS:**
|
|
55
95
|
|
|
56
96
|
```bash
|
|
57
97
|
cd ios && pod install
|
|
58
98
|
```
|
|
59
99
|
|
|
60
|
-
Android
|
|
100
|
+
**Android** — initialize the native adapter in `MainApplication.kt`:
|
|
61
101
|
|
|
62
102
|
```kotlin
|
|
63
103
|
import com.nitrostorage.AndroidStorageAdapter
|
|
@@ -70,11 +110,14 @@ class MainApplication : Application() {
|
|
|
70
110
|
}
|
|
71
111
|
```
|
|
72
112
|
|
|
113
|
+
---
|
|
114
|
+
|
|
73
115
|
## Quick Start
|
|
74
116
|
|
|
75
117
|
```ts
|
|
76
118
|
import { createStorageItem, StorageScope, useStorage } from "react-native-nitro-storage";
|
|
77
119
|
|
|
120
|
+
// define a storage item outside of components
|
|
78
121
|
const counterItem = createStorageItem({
|
|
79
122
|
key: "counter",
|
|
80
123
|
scope: StorageScope.Memory,
|
|
@@ -93,369 +136,673 @@ export function Counter() {
|
|
|
93
136
|
}
|
|
94
137
|
```
|
|
95
138
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
- `
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
- `StorageItemConfig<T>`
|
|
116
|
-
- `StorageItem<T>`
|
|
117
|
-
- `StorageBatchSetItem<T>`
|
|
118
|
-
- `Validator<T>`
|
|
119
|
-
- `ExpirationConfig`
|
|
120
|
-
- `MigrationContext`
|
|
121
|
-
- `Migration`
|
|
122
|
-
- `TransactionContext`
|
|
123
|
-
- `MMKVLike`
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Storage Scopes
|
|
142
|
+
|
|
143
|
+
| Scope | Backend (iOS) | Backend (Android) | Backend (Web) | Persisted |
|
|
144
|
+
| -------- | ------------------------ | -------------------------- | ------------------------------------------------ | --------- |
|
|
145
|
+
| `Memory` | In-process JS Map | In-process JS Map | In-process JS Map | No |
|
|
146
|
+
| `Disk` | UserDefaults (app suite) | SharedPreferences | `localStorage` | Yes |
|
|
147
|
+
| `Secure` | Keychain (AES-256 GCM) | EncryptedSharedPreferences | `localStorage` (`__secure_` + `__bio_` prefixes) | Yes |
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { StorageScope } from "react-native-nitro-storage";
|
|
151
|
+
|
|
152
|
+
StorageScope.Memory; // 0 — ephemeral, fastest
|
|
153
|
+
StorageScope.Disk; // 1 — persistent, fast
|
|
154
|
+
StorageScope.Secure; // 2 — encrypted, slightly slower
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
124
158
|
|
|
125
159
|
## API Reference
|
|
126
160
|
|
|
127
|
-
### `
|
|
161
|
+
### `createStorageItem<T>(config)`
|
|
162
|
+
|
|
163
|
+
The core factory. Creates a reactive storage item that can be used standalone or with hooks.
|
|
128
164
|
|
|
129
165
|
```ts
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
Secure = 2,
|
|
134
|
-
}
|
|
166
|
+
function createStorageItem<T = undefined>(
|
|
167
|
+
config: StorageItemConfig<T>,
|
|
168
|
+
): StorageItem<T>;
|
|
135
169
|
```
|
|
136
170
|
|
|
137
|
-
|
|
171
|
+
**Config options:**
|
|
172
|
+
|
|
173
|
+
| Property | Type | Default | Description |
|
|
174
|
+
| ---------------------- | -------------------------------- | -------------- | -------------------------------------------------------------- |
|
|
175
|
+
| `key` | `string` | _required_ | Storage key identifier |
|
|
176
|
+
| `scope` | `StorageScope` | _required_ | Where to store the data |
|
|
177
|
+
| `defaultValue` | `T` | `undefined` | Value returned when no data exists |
|
|
178
|
+
| `serialize` | `(value: T) => string` | JSON fast path | Custom serialization |
|
|
179
|
+
| `deserialize` | `(value: string) => T` | JSON fast path | Custom deserialization |
|
|
180
|
+
| `validate` | `(value: unknown) => value is T` | — | Type guard run on every read |
|
|
181
|
+
| `onValidationError` | `(invalidValue: unknown) => T` | — | Recovery function when validation fails |
|
|
182
|
+
| `expiration` | `{ ttlMs: number }` | — | Time-to-live in milliseconds |
|
|
183
|
+
| `onExpired` | `(key: string) => void` | — | Callback fired when a TTL value expires on read |
|
|
184
|
+
| `readCache` | `boolean` | `false` | Cache deserialized values in JS (avoids repeated native reads) |
|
|
185
|
+
| `coalesceSecureWrites` | `boolean` | `false` | Batch same-tick Secure writes per key |
|
|
186
|
+
| `namespace` | `string` | — | Prefix key as `namespace:key` for isolation |
|
|
187
|
+
| `biometric` | `boolean` | `false` | Require biometric auth (Secure scope only) |
|
|
188
|
+
| `accessControl` | `AccessControl` | — | Keychain access control level (native only) |
|
|
189
|
+
|
|
190
|
+
**Returned `StorageItem<T>`:**
|
|
191
|
+
|
|
192
|
+
| Method / Property | Type | Description |
|
|
193
|
+
| ----------------- | ---------------------------------------- | -------------------------------------------------- |
|
|
194
|
+
| `get()` | `() => T` | Read current value (synchronous) |
|
|
195
|
+
| `set(value)` | `(value: T \| ((prev: T) => T)) => void` | Write a value or updater function |
|
|
196
|
+
| `delete()` | `() => void` | Remove the stored value (resets to `defaultValue`) |
|
|
197
|
+
| `has()` | `() => boolean` | Check if a value exists in storage |
|
|
198
|
+
| `subscribe(cb)` | `(cb: () => void) => () => void` | Listen for changes, returns unsubscribe |
|
|
199
|
+
| `serialize` | `(v: T) => string` | The item's serializer |
|
|
200
|
+
| `deserialize` | `(v: string) => T` | The item's deserializer |
|
|
201
|
+
| `scope` | `StorageScope` | The item's scope |
|
|
202
|
+
| `key` | `string` | The resolved key (includes namespace prefix) |
|
|
203
|
+
|
|
204
|
+
**Non-React subscription example:**
|
|
138
205
|
|
|
139
206
|
```ts
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
getBatch(keys: string[], scope: number): (string | undefined)[];
|
|
147
|
-
removeBatch(keys: string[], scope: number): void;
|
|
148
|
-
addOnChange(
|
|
149
|
-
scope: number,
|
|
150
|
-
callback: (key: string, value: string | undefined) => void
|
|
151
|
-
): () => void;
|
|
152
|
-
};
|
|
207
|
+
const unsubscribe = sessionItem.subscribe(() => {
|
|
208
|
+
console.log("session changed:", sessionItem.get());
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
sessionItem.set("next-session");
|
|
212
|
+
unsubscribe();
|
|
153
213
|
```
|
|
154
214
|
|
|
155
|
-
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### React Hooks
|
|
156
218
|
|
|
157
|
-
|
|
158
|
-
- Most app code should use `createStorageItem` + hooks instead of this low-level API.
|
|
219
|
+
#### `useStorage(item)`
|
|
159
220
|
|
|
160
|
-
|
|
221
|
+
Full reactive binding. Re-renders when the value changes.
|
|
161
222
|
|
|
162
223
|
```ts
|
|
163
|
-
|
|
164
|
-
key: string;
|
|
165
|
-
scope: StorageScope;
|
|
166
|
-
defaultValue?: T;
|
|
167
|
-
serialize?: (value: T) => string;
|
|
168
|
-
deserialize?: (value: string) => T;
|
|
169
|
-
validate?: Validator<T>;
|
|
170
|
-
onValidationError?: (invalidValue: unknown) => T;
|
|
171
|
-
expiration?: ExpirationConfig;
|
|
172
|
-
readCache?: boolean;
|
|
173
|
-
coalesceSecureWrites?: boolean;
|
|
174
|
-
};
|
|
224
|
+
const [value, setValue] = useStorage(item);
|
|
175
225
|
```
|
|
176
226
|
|
|
177
|
-
|
|
227
|
+
#### `useStorageSelector(item, selector, isEqual?)`
|
|
228
|
+
|
|
229
|
+
Subscribe to a derived slice. Only re-renders when the selected value changes.
|
|
178
230
|
|
|
179
231
|
```ts
|
|
180
|
-
|
|
181
|
-
get: () => T;
|
|
182
|
-
set: (value: T | ((prev: T) => T)) => void;
|
|
183
|
-
delete: () => void;
|
|
184
|
-
subscribe: (callback: () => void) => () => void;
|
|
185
|
-
serialize: (value: T) => string;
|
|
186
|
-
deserialize: (value: string) => T;
|
|
187
|
-
_triggerListeners: () => void;
|
|
188
|
-
scope: StorageScope;
|
|
189
|
-
key: string;
|
|
190
|
-
};
|
|
232
|
+
const [theme, setSettings] = useStorageSelector(settingsItem, (s) => s.theme);
|
|
191
233
|
```
|
|
192
234
|
|
|
193
|
-
|
|
235
|
+
#### `useSetStorage(item)`
|
|
236
|
+
|
|
237
|
+
Write-only hook. Useful when a component needs to update a value but doesn't depend on it.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
const setToken = useSetStorage(tokenItem);
|
|
241
|
+
setToken("new-token");
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
### `storage` — Global Utilities
|
|
194
247
|
|
|
195
248
|
```ts
|
|
196
|
-
|
|
249
|
+
import { storage, StorageScope } from "react-native-nitro-storage";
|
|
197
250
|
```
|
|
198
251
|
|
|
199
|
-
|
|
252
|
+
| Method | Description |
|
|
253
|
+
| --------------------------------------- | ---------------------------------------------------------------------------- |
|
|
254
|
+
| `storage.clear(scope)` | Clear all keys in a scope (`Secure` also clears biometric entries) |
|
|
255
|
+
| `storage.clearAll()` | Clear Memory + Disk + Secure |
|
|
256
|
+
| `storage.clearNamespace(ns, scope)` | Remove only keys matching a namespace |
|
|
257
|
+
| `storage.clearBiometric()` | Remove all biometric-prefixed keys |
|
|
258
|
+
| `storage.has(key, scope)` | Check if a key exists |
|
|
259
|
+
| `storage.getAllKeys(scope)` | Get all key names |
|
|
260
|
+
| `storage.getAll(scope)` | Get all key-value pairs as `Record<string, string>` |
|
|
261
|
+
| `storage.size(scope)` | Number of stored keys |
|
|
262
|
+
| `storage.setAccessControl(level)` | Set default secure access control for subsequent secure writes (native only) |
|
|
263
|
+
| `storage.setSecureWritesAsync(enabled)` | Toggle async secure writes on Android (`false` by default) |
|
|
264
|
+
| `storage.flushSecureWrites()` | Force flush of queued secure writes when coalescing is enabled |
|
|
265
|
+
| `storage.setKeychainAccessGroup(group)` | Set keychain access group for app sharing (native only) |
|
|
266
|
+
|
|
267
|
+
> `storage.getAll(StorageScope.Secure)` returns regular secure entries. Biometric-protected values are not included in this snapshot API.
|
|
268
|
+
|
|
269
|
+
#### Global utility examples
|
|
200
270
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
271
|
+
```ts
|
|
272
|
+
import { AccessControl, storage, StorageScope } from "react-native-nitro-storage";
|
|
273
|
+
|
|
274
|
+
storage.has("session", StorageScope.Disk);
|
|
275
|
+
storage.getAllKeys(StorageScope.Disk);
|
|
276
|
+
storage.getAll(StorageScope.Disk);
|
|
277
|
+
storage.size(StorageScope.Disk);
|
|
278
|
+
|
|
279
|
+
storage.clearNamespace("user-42", StorageScope.Disk);
|
|
280
|
+
storage.clearBiometric();
|
|
281
|
+
|
|
282
|
+
storage.setAccessControl(AccessControl.WhenUnlockedThisDeviceOnly);
|
|
283
|
+
storage.setKeychainAccessGroup("group.com.example.shared");
|
|
284
|
+
|
|
285
|
+
storage.clear(StorageScope.Memory);
|
|
286
|
+
storage.clearAll();
|
|
287
|
+
```
|
|
211
288
|
|
|
212
|
-
|
|
289
|
+
#### Android secure write mode
|
|
213
290
|
|
|
214
|
-
|
|
291
|
+
`storage.setSecureWritesAsync(true)` switches secure writes from synchronous `commit()` to asynchronous `apply()` on Android.
|
|
292
|
+
Use this for non-critical secure writes when lower latency matters more than immediate durability.
|
|
215
293
|
|
|
216
|
-
|
|
294
|
+
Call `storage.flushSecureWrites()` when you need deterministic persistence boundaries (for example before namespace clears, process handoff, or strict test assertions).
|
|
217
295
|
|
|
218
296
|
```ts
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
297
|
+
import { storage } from "react-native-nitro-storage";
|
|
298
|
+
|
|
299
|
+
storage.setSecureWritesAsync(true);
|
|
300
|
+
|
|
301
|
+
// ...multiple secure writes happen (including coalesced item writes)
|
|
302
|
+
|
|
303
|
+
storage.flushSecureWrites(); // deterministic durability boundary
|
|
222
304
|
```
|
|
223
305
|
|
|
224
|
-
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
### `createSecureAuthStorage<K>(config, options?)`
|
|
309
|
+
|
|
310
|
+
One-liner factory for authentication flows. Creates multiple `StorageItem<string>` entries in Secure scope.
|
|
225
311
|
|
|
226
312
|
```ts
|
|
227
|
-
function
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
): [TSelected, (value: T | ((prev: T) => T)) => void]
|
|
313
|
+
function createSecureAuthStorage<K extends string>(
|
|
314
|
+
config: SecureAuthStorageConfig<K>,
|
|
315
|
+
options?: { namespace?: string },
|
|
316
|
+
): Record<K, StorageItem<string>>;
|
|
232
317
|
```
|
|
233
318
|
|
|
234
|
-
|
|
319
|
+
- Default namespace: `"auth"`
|
|
320
|
+
- Each key is a separate `StorageItem<string>` with `StorageScope.Secure`
|
|
321
|
+
- Supports per-key TTL, biometric, and access control
|
|
322
|
+
|
|
323
|
+
---
|
|
235
324
|
|
|
236
|
-
###
|
|
325
|
+
### Batch Operations
|
|
326
|
+
|
|
327
|
+
Atomic multi-key operations. Uses native batch APIs for best performance.
|
|
237
328
|
|
|
238
329
|
```ts
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
330
|
+
import { getBatch, setBatch, removeBatch } from "react-native-nitro-storage";
|
|
331
|
+
|
|
332
|
+
// Read multiple items at once
|
|
333
|
+
const [a, b, c] = getBatch([itemA, itemB, itemC], StorageScope.Disk);
|
|
334
|
+
|
|
335
|
+
// Write multiple items atomically
|
|
336
|
+
setBatch(
|
|
337
|
+
[
|
|
338
|
+
{ item: itemA, value: "hello" },
|
|
339
|
+
{ item: itemB, value: "world" },
|
|
340
|
+
],
|
|
341
|
+
StorageScope.Disk,
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
// Remove multiple items
|
|
345
|
+
removeBatch([itemA, itemB], StorageScope.Disk);
|
|
242
346
|
```
|
|
243
347
|
|
|
244
|
-
|
|
348
|
+
> All items in a batch must share the same scope. Items with `validate` or `expiration` automatically use per-item paths to preserve semantics.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
### Transactions
|
|
353
|
+
|
|
354
|
+
Grouped writes with automatic rollback on error.
|
|
245
355
|
|
|
246
356
|
```ts
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
357
|
+
import { runTransaction, StorageScope } from "react-native-nitro-storage";
|
|
358
|
+
|
|
359
|
+
runTransaction(StorageScope.Disk, (tx) => {
|
|
360
|
+
const balance = tx.getItem(balanceItem);
|
|
361
|
+
tx.setItem(balanceItem, balance - 50);
|
|
362
|
+
tx.setItem(logItem, `Deducted 50 at ${new Date().toISOString()}`);
|
|
363
|
+
|
|
364
|
+
if (balance - 50 < 0) throw new Error("Insufficient funds");
|
|
365
|
+
// if this throws, both writes are rolled back
|
|
366
|
+
});
|
|
251
367
|
```
|
|
252
368
|
|
|
253
|
-
|
|
369
|
+
**TransactionContext methods:**
|
|
254
370
|
|
|
255
|
-
|
|
256
|
-
|
|
371
|
+
| Method | Description |
|
|
372
|
+
| ---------------------- | --------------------------- |
|
|
373
|
+
| `getItem(item)` | Read a StorageItem's value |
|
|
374
|
+
| `setItem(item, value)` | Write a StorageItem's value |
|
|
375
|
+
| `removeItem(item)` | Delete a StorageItem |
|
|
376
|
+
| `getRaw(key)` | Read raw string by key |
|
|
377
|
+
| `setRaw(key, value)` | Write raw string by key |
|
|
378
|
+
| `removeRaw(key)` | Delete raw key |
|
|
257
379
|
|
|
258
|
-
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
### Migrations
|
|
383
|
+
|
|
384
|
+
Versioned, sequential data migrations.
|
|
259
385
|
|
|
260
386
|
```ts
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
387
|
+
import {
|
|
388
|
+
registerMigration,
|
|
389
|
+
migrateToLatest,
|
|
390
|
+
StorageScope,
|
|
391
|
+
} from "react-native-nitro-storage";
|
|
265
392
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
): unknown[];
|
|
393
|
+
registerMigration(1, ({ setRaw }) => {
|
|
394
|
+
setRaw("onboarding-complete", "false");
|
|
395
|
+
});
|
|
270
396
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
)
|
|
397
|
+
registerMigration(2, ({ getRaw, setRaw, removeRaw }) => {
|
|
398
|
+
const raw = getRaw("legacy-key");
|
|
399
|
+
if (raw) {
|
|
400
|
+
setRaw("new-key", raw);
|
|
401
|
+
removeRaw("legacy-key");
|
|
402
|
+
}
|
|
403
|
+
});
|
|
275
404
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
scope: StorageScope
|
|
279
|
-
): void;
|
|
405
|
+
// apply all pending migrations (runs once per scope)
|
|
406
|
+
migrateToLatest(StorageScope.Disk);
|
|
280
407
|
```
|
|
281
408
|
|
|
282
|
-
|
|
409
|
+
- Versions must be positive integers, registered in any order, applied ascending
|
|
410
|
+
- Version state is tracked per scope via `__nitro_storage_migration_version__`
|
|
411
|
+
- Duplicate versions throw at registration time
|
|
283
412
|
|
|
284
|
-
|
|
285
|
-
- Items using `validate` or `expiration` automatically run via per-item `get()`/`set()` paths to preserve validation and TTL behavior.
|
|
286
|
-
- Mixed-scope calls throw:
|
|
287
|
-
- `Batch scope mismatch for "<key>": expected <Scope>, received <Scope>.`
|
|
413
|
+
---
|
|
288
414
|
|
|
289
|
-
###
|
|
415
|
+
### MMKV Migration
|
|
416
|
+
|
|
417
|
+
Drop-in helper for migrating from `react-native-mmkv`.
|
|
290
418
|
|
|
291
419
|
```ts
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
getRaw: (key: string) => string | undefined;
|
|
295
|
-
setRaw: (key: string, value: string) => void;
|
|
296
|
-
removeRaw: (key: string) => void;
|
|
297
|
-
};
|
|
420
|
+
import { migrateFromMMKV } from "react-native-nitro-storage";
|
|
421
|
+
import { MMKV } from "react-native-mmkv";
|
|
298
422
|
|
|
299
|
-
|
|
423
|
+
const mmkv = new MMKV();
|
|
300
424
|
|
|
301
|
-
|
|
302
|
-
|
|
425
|
+
const migrated = migrateFromMMKV(mmkv, myStorageItem, true);
|
|
426
|
+
// true → value found and copied, original deleted from MMKV
|
|
427
|
+
// false → no matching key in MMKV
|
|
303
428
|
```
|
|
304
429
|
|
|
305
|
-
|
|
430
|
+
- Read priority: `getString` → `getNumber` → `getBoolean`
|
|
431
|
+
- Uses `item.set()` so validation still applies
|
|
432
|
+
- Only deletes from MMKV when migration succeeds
|
|
306
433
|
|
|
307
|
-
|
|
308
|
-
- Duplicate versions throw.
|
|
309
|
-
- Migration version is tracked per scope using key `__nitro_storage_migration_version__`.
|
|
310
|
-
- `migrateToLatest` applies pending migrations in ascending version order and returns applied/latest version.
|
|
434
|
+
---
|
|
311
435
|
|
|
312
|
-
|
|
436
|
+
### Enums
|
|
313
437
|
|
|
314
|
-
|
|
315
|
-
- `registerMigration`: throws when version is already registered.
|
|
316
|
-
- `migrateToLatest`: throws on invalid scope.
|
|
438
|
+
#### `AccessControl`
|
|
317
439
|
|
|
318
|
-
|
|
440
|
+
Controls keychain item access requirements (iOS Keychain / Android Keystore). No-op on web.
|
|
319
441
|
|
|
320
442
|
```ts
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
443
|
+
enum AccessControl {
|
|
444
|
+
WhenUnlocked = 0,
|
|
445
|
+
AfterFirstUnlock = 1,
|
|
446
|
+
WhenPasscodeSetThisDeviceOnly = 2,
|
|
447
|
+
WhenUnlockedThisDeviceOnly = 3,
|
|
448
|
+
AfterFirstUnlockThisDeviceOnly = 4,
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### `BiometricLevel`
|
|
330
453
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
454
|
+
```ts
|
|
455
|
+
enum BiometricLevel {
|
|
456
|
+
None = 0,
|
|
457
|
+
BiometryOrPasscode = 1,
|
|
458
|
+
BiometryOnly = 2,
|
|
459
|
+
}
|
|
335
460
|
```
|
|
336
461
|
|
|
337
|
-
|
|
462
|
+
---
|
|
338
463
|
|
|
339
|
-
|
|
340
|
-
- Rollback is best-effort within process lifetime.
|
|
341
|
-
- `setItem`/`removeItem` prefer item methods when available, so validation/TTL/cache semantics stay consistent.
|
|
464
|
+
## Use Cases
|
|
342
465
|
|
|
343
|
-
|
|
466
|
+
### Persisted User Preferences
|
|
344
467
|
|
|
345
|
-
|
|
346
|
-
|
|
468
|
+
```ts
|
|
469
|
+
type UserPreferences = {
|
|
470
|
+
theme: "light" | "dark" | "system";
|
|
471
|
+
language: string;
|
|
472
|
+
notifications: boolean;
|
|
473
|
+
};
|
|
347
474
|
|
|
348
|
-
|
|
475
|
+
const prefsItem = createStorageItem<UserPreferences>({
|
|
476
|
+
key: "prefs",
|
|
477
|
+
scope: StorageScope.Disk,
|
|
478
|
+
defaultValue: { theme: "system", language: "en", notifications: true },
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// in a component — only re-renders when theme changes
|
|
482
|
+
const [theme, setPrefs] = useStorageSelector(prefsItem, (p) => p.theme);
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Auth Token Management
|
|
349
486
|
|
|
350
487
|
```ts
|
|
351
|
-
|
|
488
|
+
const auth = createSecureAuthStorage(
|
|
489
|
+
{
|
|
490
|
+
accessToken: { ttlMs: 15 * 60_000, biometric: true },
|
|
491
|
+
refreshToken: { ttlMs: 7 * 24 * 60 * 60_000 },
|
|
492
|
+
idToken: {},
|
|
493
|
+
},
|
|
494
|
+
{ namespace: "myapp-auth" },
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
// after login
|
|
498
|
+
auth.accessToken.set(response.accessToken);
|
|
499
|
+
auth.refreshToken.set(response.refreshToken);
|
|
500
|
+
auth.idToken.set(response.idToken);
|
|
501
|
+
|
|
502
|
+
// check if token exists and hasn't expired
|
|
503
|
+
if (auth.accessToken.has()) {
|
|
504
|
+
const token = auth.accessToken.get();
|
|
505
|
+
// use token
|
|
506
|
+
} else {
|
|
507
|
+
// refresh or re-login
|
|
508
|
+
}
|
|
352
509
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
};
|
|
510
|
+
// logout
|
|
511
|
+
storage.clearNamespace("myapp-auth", StorageScope.Secure);
|
|
356
512
|
```
|
|
357
513
|
|
|
358
|
-
###
|
|
514
|
+
### Feature Flags with Validation
|
|
359
515
|
|
|
360
516
|
```ts
|
|
361
|
-
type
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
517
|
+
type FeatureFlags = {
|
|
518
|
+
darkMode: boolean;
|
|
519
|
+
betaFeature: boolean;
|
|
520
|
+
maxUploadMb: number;
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
524
|
+
typeof value === "object" && value !== null;
|
|
525
|
+
|
|
526
|
+
const isFeatureFlags = (value: unknown): value is FeatureFlags => {
|
|
527
|
+
if (!isRecord(value)) return false;
|
|
528
|
+
return (
|
|
529
|
+
typeof value.darkMode === "boolean" &&
|
|
530
|
+
typeof value.betaFeature === "boolean" &&
|
|
531
|
+
typeof value.maxUploadMb === "number"
|
|
532
|
+
);
|
|
368
533
|
};
|
|
369
534
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
535
|
+
const flagsItem = createStorageItem<FeatureFlags>({
|
|
536
|
+
key: "feature-flags",
|
|
537
|
+
scope: StorageScope.Disk,
|
|
538
|
+
defaultValue: { darkMode: false, betaFeature: false, maxUploadMb: 10 },
|
|
539
|
+
validate: isFeatureFlags,
|
|
540
|
+
onValidationError: () => ({
|
|
541
|
+
darkMode: false,
|
|
542
|
+
betaFeature: false,
|
|
543
|
+
maxUploadMb: 10,
|
|
544
|
+
}),
|
|
545
|
+
expiration: { ttlMs: 60 * 60_000 }, // refresh from server every hour
|
|
546
|
+
onExpired: () => fetchAndStoreFlags(),
|
|
547
|
+
});
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Biometric-protected Secrets
|
|
551
|
+
|
|
552
|
+
```ts
|
|
553
|
+
import { AccessControl, createStorageItem, StorageScope } from "react-native-nitro-storage";
|
|
554
|
+
|
|
555
|
+
const paymentPin = createStorageItem<string>({
|
|
556
|
+
key: "payment-pin",
|
|
557
|
+
scope: StorageScope.Secure,
|
|
558
|
+
defaultValue: "",
|
|
559
|
+
biometric: true,
|
|
560
|
+
accessControl: AccessControl.WhenPasscodeSetThisDeviceOnly,
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
paymentPin.set("4829");
|
|
564
|
+
const pin = paymentPin.get();
|
|
565
|
+
paymentPin.delete();
|
|
375
566
|
```
|
|
376
567
|
|
|
377
|
-
|
|
568
|
+
### Multi-Tenant / Namespaced Storage
|
|
569
|
+
|
|
570
|
+
```ts
|
|
571
|
+
function createUserStorage(userId: string) {
|
|
572
|
+
return {
|
|
573
|
+
cart: createStorageItem<string[]>({
|
|
574
|
+
key: "cart",
|
|
575
|
+
scope: StorageScope.Disk,
|
|
576
|
+
defaultValue: [],
|
|
577
|
+
namespace: `user-${userId}`,
|
|
578
|
+
}),
|
|
579
|
+
draft: createStorageItem<string>({
|
|
580
|
+
key: "draft",
|
|
581
|
+
scope: StorageScope.Disk,
|
|
582
|
+
defaultValue: "",
|
|
583
|
+
namespace: `user-${userId}`,
|
|
584
|
+
}),
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// clear all data for a specific user
|
|
589
|
+
storage.clearNamespace("user-123", StorageScope.Disk);
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### OTP / Temporary Codes
|
|
593
|
+
|
|
594
|
+
```ts
|
|
595
|
+
const otpItem = createStorageItem<string>({
|
|
596
|
+
key: "otp-code",
|
|
597
|
+
scope: StorageScope.Secure,
|
|
598
|
+
defaultValue: "",
|
|
599
|
+
expiration: { ttlMs: 5 * 60_000 }, // 5 minutes
|
|
600
|
+
onExpired: (key) => {
|
|
601
|
+
console.log(`${key} expired — prompt user to request a new code`);
|
|
602
|
+
},
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// store the code
|
|
606
|
+
otpItem.set("482917");
|
|
607
|
+
|
|
608
|
+
// later — returns "" if expired
|
|
609
|
+
const code = otpItem.get();
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### Bulk Bootstrap with Batch APIs
|
|
613
|
+
|
|
614
|
+
```ts
|
|
615
|
+
import {
|
|
616
|
+
createStorageItem,
|
|
617
|
+
getBatch,
|
|
618
|
+
removeBatch,
|
|
619
|
+
setBatch,
|
|
620
|
+
StorageScope,
|
|
621
|
+
} from "react-native-nitro-storage";
|
|
622
|
+
|
|
623
|
+
const firstName = createStorageItem({
|
|
624
|
+
key: "first-name",
|
|
625
|
+
scope: StorageScope.Disk,
|
|
626
|
+
defaultValue: "",
|
|
627
|
+
});
|
|
628
|
+
const lastName = createStorageItem({
|
|
629
|
+
key: "last-name",
|
|
630
|
+
scope: StorageScope.Disk,
|
|
631
|
+
defaultValue: "",
|
|
632
|
+
});
|
|
378
633
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
634
|
+
setBatch(
|
|
635
|
+
[
|
|
636
|
+
{ item: firstName, value: "Ada" },
|
|
637
|
+
{ item: lastName, value: "Lovelace" },
|
|
638
|
+
],
|
|
639
|
+
StorageScope.Disk,
|
|
640
|
+
);
|
|
383
641
|
|
|
384
|
-
|
|
642
|
+
const [first, last] = getBatch([firstName, lastName], StorageScope.Disk);
|
|
643
|
+
removeBatch([firstName, lastName], StorageScope.Disk);
|
|
644
|
+
```
|
|
385
645
|
|
|
386
|
-
###
|
|
646
|
+
### Atomic Balance Transfer
|
|
387
647
|
|
|
388
648
|
```ts
|
|
389
|
-
const
|
|
390
|
-
key: "
|
|
649
|
+
const fromBalance = createStorageItem({
|
|
650
|
+
key: "from",
|
|
651
|
+
scope: StorageScope.Disk,
|
|
652
|
+
defaultValue: 100,
|
|
653
|
+
});
|
|
654
|
+
const toBalance = createStorageItem({
|
|
655
|
+
key: "to",
|
|
391
656
|
scope: StorageScope.Disk,
|
|
392
657
|
defaultValue: 0,
|
|
393
|
-
validate: (v): v is number => typeof v === "number" && v > 0,
|
|
394
|
-
onValidationError: () => 1,
|
|
395
658
|
});
|
|
659
|
+
|
|
660
|
+
function transfer(amount: number) {
|
|
661
|
+
runTransaction(StorageScope.Disk, (tx) => {
|
|
662
|
+
const from = tx.getItem(fromBalance);
|
|
663
|
+
if (from < amount) throw new Error("Insufficient funds");
|
|
664
|
+
|
|
665
|
+
tx.setItem(fromBalance, from - amount);
|
|
666
|
+
tx.setItem(toBalance, tx.getItem(toBalance) + amount);
|
|
667
|
+
});
|
|
668
|
+
}
|
|
396
669
|
```
|
|
397
670
|
|
|
398
|
-
###
|
|
671
|
+
### Custom Binary Codec
|
|
399
672
|
|
|
400
673
|
```ts
|
|
401
|
-
const
|
|
402
|
-
key: "
|
|
403
|
-
scope: StorageScope.
|
|
404
|
-
|
|
674
|
+
const compactItem = createStorageItem<{ id: number; active: boolean }>({
|
|
675
|
+
key: "compact",
|
|
676
|
+
scope: StorageScope.Disk,
|
|
677
|
+
defaultValue: { id: 0, active: false },
|
|
678
|
+
serialize: (v) => `${v.id}|${v.active ? "1" : "0"}`,
|
|
679
|
+
deserialize: (v) => {
|
|
680
|
+
const [id, flag] = v.split("|");
|
|
681
|
+
return { id: Number(id), active: flag === "1" };
|
|
682
|
+
},
|
|
405
683
|
});
|
|
406
684
|
```
|
|
407
685
|
|
|
408
|
-
###
|
|
686
|
+
### Coalesced Secure Writes with Deterministic Flush
|
|
409
687
|
|
|
410
688
|
```ts
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
689
|
+
import { createStorageItem, storage, StorageScope } from "react-native-nitro-storage";
|
|
690
|
+
|
|
691
|
+
const sessionToken = createStorageItem<string>({
|
|
692
|
+
key: "session-token",
|
|
693
|
+
scope: StorageScope.Secure,
|
|
694
|
+
defaultValue: "",
|
|
695
|
+
coalesceSecureWrites: true,
|
|
414
696
|
});
|
|
697
|
+
|
|
698
|
+
sessionToken.set("token-v1");
|
|
699
|
+
sessionToken.set("token-v2");
|
|
700
|
+
|
|
701
|
+
// force pending secure writes to native persistence
|
|
702
|
+
storage.flushSecureWrites();
|
|
415
703
|
```
|
|
416
704
|
|
|
417
|
-
###
|
|
705
|
+
### Storage Snapshots and Cleanup
|
|
418
706
|
|
|
419
707
|
```ts
|
|
420
|
-
|
|
421
|
-
|
|
708
|
+
import { storage, StorageScope } from "react-native-nitro-storage";
|
|
709
|
+
|
|
710
|
+
const diskKeys = storage.getAllKeys(StorageScope.Disk);
|
|
711
|
+
const diskValues = storage.getAll(StorageScope.Disk);
|
|
712
|
+
const secureCount = storage.size(StorageScope.Secure);
|
|
713
|
+
|
|
714
|
+
if (storage.has("legacy-flag", StorageScope.Disk)) {
|
|
715
|
+
storage.clearNamespace("legacy", StorageScope.Disk);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
storage.clearBiometric();
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### Low-level Subscription (outside React)
|
|
722
|
+
|
|
723
|
+
```ts
|
|
724
|
+
import { createStorageItem, StorageScope } from "react-native-nitro-storage";
|
|
725
|
+
|
|
726
|
+
const notificationsItem = createStorageItem<boolean>({
|
|
727
|
+
key: "notifications-enabled",
|
|
728
|
+
scope: StorageScope.Disk,
|
|
729
|
+
defaultValue: true,
|
|
422
730
|
});
|
|
423
731
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
if (!raw) return;
|
|
427
|
-
const value = JSON.parse(raw) as { ready: boolean };
|
|
428
|
-
setRaw("seed", JSON.stringify({ ...value, migrated: true }));
|
|
732
|
+
const unsubscribe = notificationsItem.subscribe(() => {
|
|
733
|
+
console.log("notifications changed:", notificationsItem.get());
|
|
429
734
|
});
|
|
430
735
|
|
|
431
|
-
|
|
736
|
+
notificationsItem.set(false);
|
|
737
|
+
unsubscribe();
|
|
432
738
|
```
|
|
433
739
|
|
|
434
|
-
|
|
740
|
+
### Migrating From MMKV
|
|
741
|
+
|
|
742
|
+
```ts
|
|
743
|
+
import { MMKV } from "react-native-mmkv";
|
|
744
|
+
|
|
745
|
+
const mmkv = new MMKV();
|
|
746
|
+
|
|
747
|
+
const usernameItem = createStorageItem({
|
|
748
|
+
key: "username",
|
|
749
|
+
scope: StorageScope.Disk,
|
|
750
|
+
defaultValue: "",
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
// run once at app startup
|
|
754
|
+
migrateFromMMKV(mmkv, usernameItem, true); // true = delete from MMKV after
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
## Exported Types
|
|
760
|
+
|
|
761
|
+
```ts
|
|
762
|
+
import type {
|
|
763
|
+
Storage,
|
|
764
|
+
StorageItemConfig,
|
|
765
|
+
StorageItem,
|
|
766
|
+
StorageBatchSetItem,
|
|
767
|
+
Validator,
|
|
768
|
+
ExpirationConfig,
|
|
769
|
+
MigrationContext,
|
|
770
|
+
Migration,
|
|
771
|
+
TransactionContext,
|
|
772
|
+
MMKVLike,
|
|
773
|
+
SecureAuthStorageConfig,
|
|
774
|
+
} from "react-native-nitro-storage";
|
|
775
|
+
```
|
|
435
776
|
|
|
436
|
-
|
|
437
|
-
- `Disk`: App-scoped UserDefaults suite (iOS), SharedPreferences (Android), `localStorage` (web).
|
|
438
|
-
- `Secure`: Keychain (iOS), EncryptedSharedPreferences (Android), `sessionStorage` fallback (web).
|
|
777
|
+
---
|
|
439
778
|
|
|
440
779
|
## Dev Commands
|
|
441
780
|
|
|
442
|
-
From
|
|
781
|
+
From repository root:
|
|
443
782
|
|
|
444
783
|
```bash
|
|
445
784
|
bun run test -- --filter=react-native-nitro-storage
|
|
785
|
+
bun run lint -- --filter=react-native-nitro-storage
|
|
786
|
+
bun run format:check -- --filter=react-native-nitro-storage
|
|
446
787
|
bun run typecheck -- --filter=react-native-nitro-storage
|
|
788
|
+
bun run test:types -- --filter=react-native-nitro-storage
|
|
789
|
+
bun run test:cpp -- --filter=react-native-nitro-storage
|
|
447
790
|
bun run build -- --filter=react-native-nitro-storage
|
|
448
|
-
bun run benchmark
|
|
449
791
|
```
|
|
450
792
|
|
|
451
|
-
Inside
|
|
793
|
+
Inside `packages/react-native-nitro-storage`:
|
|
452
794
|
|
|
453
795
|
```bash
|
|
454
|
-
bun run test
|
|
455
|
-
bun run test:coverage
|
|
456
|
-
bun run
|
|
457
|
-
bun run
|
|
458
|
-
bun run
|
|
796
|
+
bun run test # run tests
|
|
797
|
+
bun run test:coverage # run tests with coverage
|
|
798
|
+
bun run lint # eslint (expo-magic rules)
|
|
799
|
+
bun run format:check # prettier check
|
|
800
|
+
bun run typecheck # tsc --noEmit
|
|
801
|
+
bun run test:types # public type-level API tests
|
|
802
|
+
bun run test:cpp # C++ binding/core tests
|
|
803
|
+
bun run check:pack # npm pack content guard
|
|
804
|
+
bun run build # bob build
|
|
805
|
+
bun run benchmark # performance benchmarks
|
|
459
806
|
```
|
|
460
807
|
|
|
461
808
|
## License
|