react-native-nitro-storage 0.10.4 → 0.11.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 (87) hide show
  1. package/CHANGELOG.md +71 -14
  2. package/README.md +127 -41
  3. package/SECURITY.md +16 -6
  4. package/android/build.gradle +0 -1
  5. package/android/consumer-rules.pro +0 -3
  6. package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +1 -4
  7. package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +3 -3
  8. package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +73 -23
  9. package/android/src/main/java/com/nitrostorage/DiskSqliteStore.kt +15 -0
  10. package/app.plugin.js +51 -51
  11. package/cpp/bindings/HybridStorage.cpp +25 -6
  12. package/cpp/core/NativeStorageAdapter.hpp +14 -1
  13. package/cpp/core/SqliteDiskStore.cpp +84 -11
  14. package/cpp/core/SqliteDiskStore.hpp +10 -0
  15. package/docs/api-reference.md +73 -28
  16. package/docs/benchmarks.md +4 -12
  17. package/docs/mmkv-migration.md +3 -1
  18. package/docs/native-libraries.md +10 -4
  19. package/docs/secure-storage.md +26 -12
  20. package/docs/web-backends.md +6 -2
  21. package/indexeddb-backend/package.json +6 -0
  22. package/ios/IOSStorageAdapterCpp.hpp +4 -0
  23. package/ios/IOSStorageAdapterCpp.mm +96 -18
  24. package/lib/commonjs/capabilities.js +3 -8
  25. package/lib/commonjs/capabilities.js.map +1 -1
  26. package/lib/commonjs/index.js +23 -8
  27. package/lib/commonjs/index.js.map +1 -1
  28. package/lib/commonjs/index.web.js +61 -19
  29. package/lib/commonjs/index.web.js.map +1 -1
  30. package/lib/commonjs/indexeddb-backend.js +21 -37
  31. package/lib/commonjs/indexeddb-backend.js.map +1 -1
  32. package/lib/commonjs/internal.js +6 -0
  33. package/lib/commonjs/internal.js.map +1 -1
  34. package/lib/commonjs/storage-core.js +35 -11
  35. package/lib/commonjs/storage-core.js.map +1 -1
  36. package/lib/commonjs/storage-runtime.js +1 -1
  37. package/lib/commonjs/storage-runtime.js.map +1 -1
  38. package/lib/commonjs/testing.js +68 -23
  39. package/lib/commonjs/testing.js.map +1 -1
  40. package/lib/commonjs/web-backend-contract.js +6 -2
  41. package/lib/commonjs/web-backend-contract.js.map +1 -1
  42. package/lib/module/capabilities.js +3 -8
  43. package/lib/module/capabilities.js.map +1 -1
  44. package/lib/module/index.js +14 -5
  45. package/lib/module/index.js.map +1 -1
  46. package/lib/module/index.web.js +61 -12
  47. package/lib/module/index.web.js.map +1 -1
  48. package/lib/module/indexeddb-backend.js +21 -37
  49. package/lib/module/indexeddb-backend.js.map +1 -1
  50. package/lib/module/internal.js +5 -0
  51. package/lib/module/internal.js.map +1 -1
  52. package/lib/module/storage-core.js +36 -12
  53. package/lib/module/storage-core.js.map +1 -1
  54. package/lib/module/storage-runtime.js +1 -1
  55. package/lib/module/storage-runtime.js.map +1 -1
  56. package/lib/module/testing.js +59 -16
  57. package/lib/module/testing.js.map +1 -1
  58. package/lib/module/web-backend-contract.js +5 -2
  59. package/lib/module/web-backend-contract.js.map +1 -1
  60. package/lib/typescript/capabilities.d.ts.map +1 -1
  61. package/lib/typescript/index.d.ts +8 -1
  62. package/lib/typescript/index.d.ts.map +1 -1
  63. package/lib/typescript/index.web.d.ts +7 -1
  64. package/lib/typescript/index.web.d.ts.map +1 -1
  65. package/lib/typescript/indexeddb-backend.d.ts.map +1 -1
  66. package/lib/typescript/internal.d.ts +1 -0
  67. package/lib/typescript/internal.d.ts.map +1 -1
  68. package/lib/typescript/storage-core.d.ts +1 -1
  69. package/lib/typescript/storage-core.d.ts.map +1 -1
  70. package/lib/typescript/storage-runtime.d.ts +1 -1
  71. package/lib/typescript/storage-runtime.d.ts.map +1 -1
  72. package/lib/typescript/testing.d.ts +15 -2
  73. package/lib/typescript/testing.d.ts.map +1 -1
  74. package/lib/typescript/web-backend-contract.d.ts +1 -0
  75. package/lib/typescript/web-backend-contract.d.ts.map +1 -1
  76. package/package.json +14 -5
  77. package/react-native-nitro-storage.podspec +4 -2
  78. package/src/capabilities.ts +4 -9
  79. package/src/index.ts +19 -5
  80. package/src/index.web.ts +86 -19
  81. package/src/indexeddb-backend.ts +20 -42
  82. package/src/internal.ts +8 -0
  83. package/src/storage-core.ts +52 -19
  84. package/src/storage-runtime.ts +3 -1
  85. package/src/testing.ts +91 -18
  86. package/src/web-backend-contract.ts +6 -2
  87. package/testing/package.json +8 -0
package/src/testing.ts CHANGED
@@ -1,4 +1,9 @@
1
- import { assertAccessControlLevel } from "./shared";
1
+ import { createIndexedDBBackend as createIndexedDBBackendFromSubpath } from "./indexeddb-backend";
2
+ import {
3
+ assertAccessControlLevel,
4
+ notifyAllListeners,
5
+ notifyKeyListeners,
6
+ } from "./shared";
2
7
  import type { NonMemoryScope } from "./shared";
3
8
  import {
4
9
  createStorageCore,
@@ -11,12 +16,15 @@ import type {
11
16
  StorageCapabilities,
12
17
  } from "./storage-runtime";
13
18
  import { StorageScope, AccessControl } from "./Storage.types";
19
+ import type {
20
+ WebDiskStorageBackend,
21
+ WebSecureStorageBackend,
22
+ } from "./web-storage-backend";
14
23
 
15
24
  export { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
16
25
  export { isKeychainLockedError } from "./shared";
17
26
  export { migrateFromMMKV } from "./migration";
18
27
  export { getStorageErrorCode, isStorageError } from "./storage-runtime";
19
- export { createIndexedDBBackend } from "./indexeddb-backend";
20
28
  export {
21
29
  describeWebBackendCapabilities,
22
30
  isIndexedDBWebBackend,
@@ -43,6 +51,8 @@ export type {
43
51
  StorageMetricsObserver,
44
52
  StorageSelectorListener,
45
53
  StorageSelectorSubscribeOptions,
54
+ StorageCompositeError,
55
+ StorageCompensationError,
46
56
  StorageVersion,
47
57
  Validator,
48
58
  VersionedValue,
@@ -62,6 +72,13 @@ export type {
62
72
  StorageKeyChangeEvent,
63
73
  } from "./storage-events";
64
74
  export type { StorageActions, StorageSetter } from "./storage-hooks";
75
+ export type {
76
+ WebDiskStorageBackend,
77
+ WebSecureStorageBackend,
78
+ WebStorageBackend,
79
+ WebStorageChangeEvent,
80
+ WebStorageScope,
81
+ } from "./web-storage-backend";
65
82
  export type {
66
83
  SetItemConfig,
67
84
  SetStorageItem,
@@ -80,7 +97,9 @@ type InMemoryBackend = StorageCoreBackend & {
80
97
  resetState: () => void;
81
98
  };
82
99
 
83
- function createInMemoryBackend(): InMemoryBackend {
100
+ type ChangeNotifier = (scope: NonMemoryScope, key: string | null) => void;
101
+
102
+ function createInMemoryBackend(notify: ChangeNotifier): InMemoryBackend {
84
103
  const stores = new Map<number, Map<string, string>>([
85
104
  [StorageScope.Disk, new Map()],
86
105
  [StorageScope.Secure, new Map()],
@@ -99,15 +118,18 @@ function createInMemoryBackend(): InMemoryBackend {
99
118
  get: (key, scope) => storeFor(scope).get(key),
100
119
  set: (key, value, scope) => {
101
120
  storeFor(scope).set(key, value);
121
+ notify(scope as NonMemoryScope, key);
102
122
  },
103
123
  remove: (key, scope) => {
104
124
  storeFor(scope).delete(key);
105
125
  if (scope === StorageScope.Secure) {
106
126
  biometricStore.delete(key);
107
127
  }
128
+ notify(scope as NonMemoryScope, key);
108
129
  },
109
130
  clear: (scope) => {
110
131
  storeFor(scope).clear();
132
+ notify(scope as NonMemoryScope, null);
111
133
  },
112
134
  has: (key, scope) => storeFor(scope).has(key),
113
135
  getAllKeys: (scope) => Array.from(storeFor(scope).keys()),
@@ -124,6 +146,9 @@ function createInMemoryBackend(): InMemoryBackend {
124
146
  store.set(key, value);
125
147
  }
126
148
  });
149
+ keys.forEach((key) => {
150
+ notify(scope as NonMemoryScope, key);
151
+ });
127
152
  },
128
153
  getBatch: (keys, scope) => {
129
154
  const store = storeFor(scope);
@@ -135,12 +160,16 @@ function createInMemoryBackend(): InMemoryBackend {
135
160
  if (scope === StorageScope.Secure) {
136
161
  keys.forEach((key) => biometricStore.delete(key));
137
162
  }
163
+ keys.forEach((key) => {
164
+ notify(scope as NonMemoryScope, key);
165
+ });
138
166
  },
139
167
  removeByPrefix: (prefix, scope) => {
140
168
  const store = storeFor(scope);
141
169
  for (const key of Array.from(store.keys())) {
142
170
  if (key.startsWith(prefix)) {
143
171
  store.delete(key);
172
+ notify(scope as NonMemoryScope, key);
144
173
  }
145
174
  }
146
175
  },
@@ -149,13 +178,16 @@ function createInMemoryBackend(): InMemoryBackend {
149
178
  setSecureBiometricWithLevel: (key, value) => {
150
179
  biometricStore.set(key, value);
151
180
  storeFor(StorageScope.Secure).delete(key);
181
+ notify(StorageScope.Secure, key);
152
182
  },
153
183
  deleteSecureBiometric: (key) => {
154
184
  biometricStore.delete(key);
185
+ notify(StorageScope.Secure, key);
155
186
  },
156
187
  hasSecureBiometric: (key) => biometricStore.has(key),
157
188
  clearSecureBiometric: () => {
158
189
  biometricStore.clear();
190
+ notify(StorageScope.Secure, null);
159
191
  },
160
192
  resetState: () => {
161
193
  stores.forEach((store) => {
@@ -167,23 +199,37 @@ function createInMemoryBackend(): InMemoryBackend {
167
199
  }
168
200
 
169
201
  function buildTestingModule() {
170
- const backend = createInMemoryBackend();
202
+ let coreInternals: StorageCoreInternals | undefined;
203
+ const backend = createInMemoryBackend((scope, key) => {
204
+ if (!coreInternals) {
205
+ return;
206
+ }
207
+ const listeners = coreInternals.getScopedListeners(scope);
208
+ if (key === null) {
209
+ notifyAllListeners(listeners);
210
+ return;
211
+ }
212
+ notifyKeyListeners(listeners, key);
213
+ });
171
214
 
172
215
  const buildAdapter = (
173
- _internals: StorageCoreInternals,
174
- ): StorageCoreAdapter => ({
175
- backend,
176
- changeSource: "native",
177
- applyAccessControlOnSecureRawWrite: true,
178
- ensureScopeSubscription: (_scope: NonMemoryScope) => {},
179
- maybeCleanupScopeSubscription: (_scope: NonMemoryScope) => {},
180
- onWillEmitChanges: () => {},
181
- getSecureMetadataProfile: () => ({
182
- backend: TEST_SECURE_BACKEND,
183
- encrypted: "unavailable",
184
- hardwareBacked: "unavailable",
185
- }),
186
- });
216
+ adapterInternals: StorageCoreInternals,
217
+ ): StorageCoreAdapter => {
218
+ coreInternals = adapterInternals;
219
+ return {
220
+ backend,
221
+ changeSource: "native",
222
+ applyAccessControlOnSecureRawWrite: true,
223
+ ensureScopeSubscription: (_scope: NonMemoryScope) => {},
224
+ maybeCleanupScopeSubscription: (_scope: NonMemoryScope) => {},
225
+ onWillEmitChanges: () => {},
226
+ getSecureMetadataProfile: () => ({
227
+ backend: TEST_SECURE_BACKEND,
228
+ encrypted: "unavailable",
229
+ hardwareBacked: "unavailable",
230
+ }),
231
+ };
232
+ };
187
233
 
188
234
  const core = createStorageCore(buildAdapter);
189
235
  const { internals } = core;
@@ -276,6 +322,25 @@ export const migrateToLatest = defaultModule.migrateToLatest;
276
322
  export const runTransaction = defaultModule.runTransaction;
277
323
  export const createSecureAuthStorage = defaultModule.createSecureAuthStorage;
278
324
 
325
+ export function setWebSecureStorageBackend(
326
+ _backend?: WebSecureStorageBackend,
327
+ ): void {}
328
+
329
+ export function getWebSecureStorageBackend():
330
+ WebSecureStorageBackend | undefined {
331
+ return undefined;
332
+ }
333
+
334
+ export function setWebDiskStorageBackend(
335
+ _backend?: WebDiskStorageBackend,
336
+ ): void {}
337
+
338
+ export function getWebDiskStorageBackend(): WebDiskStorageBackend | undefined {
339
+ return undefined;
340
+ }
341
+
342
+ export async function flushWebStorageBackends(): Promise<void> {}
343
+
279
344
  export function resetNitroStorageMock(): void {
280
345
  defaultModule.reset();
281
346
  }
@@ -283,3 +348,11 @@ export function resetNitroStorageMock(): void {
283
348
  export function createNitroStorageMock(): NitroStorageTestModule {
284
349
  return buildTestingModule();
285
350
  }
351
+
352
+ /**
353
+ * @deprecated Import `createIndexedDBBackend` from
354
+ * `react-native-nitro-storage/indexeddb-backend` instead. The root export
355
+ * will be removed in a future minor release.
356
+ */
357
+ export const createIndexedDBBackend: typeof createIndexedDBBackendFromSubpath =
358
+ createIndexedDBBackendFromSubpath;
@@ -7,6 +7,10 @@ export type WebBackendCapabilities = {
7
7
  subscribable: boolean;
8
8
  };
9
9
 
10
+ export function isIndexedDBBackendName(name: string | undefined): boolean {
11
+ return name?.startsWith("indexeddb:") ?? false;
12
+ }
13
+
10
14
  export function describeWebBackendCapabilities(
11
15
  backend: WebStorageBackend | undefined,
12
16
  ): WebBackendCapabilities {
@@ -20,7 +24,7 @@ export function describeWebBackendCapabilities(
20
24
  }
21
25
 
22
26
  return {
23
- buffered: backend.name?.startsWith("indexeddb:") ?? false,
27
+ buffered: isIndexedDBBackendName(backend.name),
24
28
  flushable: typeof backend.flush === "function",
25
29
  closable: typeof backend.close === "function",
26
30
  subscribable: typeof backend.subscribe === "function",
@@ -30,5 +34,5 @@ export function describeWebBackendCapabilities(
30
34
  export function isIndexedDBWebBackend(
31
35
  backend: WebStorageBackend | undefined,
32
36
  ): boolean {
33
- return backend?.name?.startsWith("indexeddb:") ?? false;
37
+ return isIndexedDBBackendName(backend?.name);
34
38
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "main": "../lib/commonjs/testing.js",
3
+ "module": "../lib/module/testing.js",
4
+ "types": "../lib/typescript/testing.d.ts",
5
+ "react-native": "../src/testing.ts",
6
+ "browser": "../src/testing.ts",
7
+ "sideEffects": false
8
+ }