react-native-nitro-storage 0.4.3 → 0.4.5

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 (41) hide show
  1. package/README.md +108 -8
  2. package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +61 -10
  3. package/ios/IOSStorageAdapterCpp.mm +44 -14
  4. package/lib/commonjs/index.js +221 -5
  5. package/lib/commonjs/index.js.map +1 -1
  6. package/lib/commonjs/index.web.js +444 -202
  7. package/lib/commonjs/index.web.js.map +1 -1
  8. package/lib/commonjs/indexeddb-backend.js +129 -7
  9. package/lib/commonjs/indexeddb-backend.js.map +1 -1
  10. package/lib/commonjs/storage-runtime.js +41 -0
  11. package/lib/commonjs/storage-runtime.js.map +1 -0
  12. package/lib/commonjs/web-storage-backend.js +90 -0
  13. package/lib/commonjs/web-storage-backend.js.map +1 -0
  14. package/lib/module/index.js +213 -5
  15. package/lib/module/index.js.map +1 -1
  16. package/lib/module/index.web.js +436 -202
  17. package/lib/module/index.web.js.map +1 -1
  18. package/lib/module/indexeddb-backend.js +129 -7
  19. package/lib/module/indexeddb-backend.js.map +1 -1
  20. package/lib/module/storage-runtime.js +36 -0
  21. package/lib/module/storage-runtime.js.map +1 -0
  22. package/lib/module/web-storage-backend.js +86 -0
  23. package/lib/module/web-storage-backend.js.map +1 -0
  24. package/lib/typescript/index.d.ts +11 -7
  25. package/lib/typescript/index.d.ts.map +1 -1
  26. package/lib/typescript/index.web.d.ts +12 -8
  27. package/lib/typescript/index.web.d.ts.map +1 -1
  28. package/lib/typescript/indexeddb-backend.d.ts +6 -2
  29. package/lib/typescript/indexeddb-backend.d.ts.map +1 -1
  30. package/lib/typescript/storage-runtime.d.ts +16 -0
  31. package/lib/typescript/storage-runtime.d.ts.map +1 -0
  32. package/lib/typescript/web-storage-backend.d.ts +30 -0
  33. package/lib/typescript/web-storage-backend.d.ts.map +1 -0
  34. package/nitro.json +8 -2
  35. package/nitrogen/generated/ios/NitroStorage+autolinking.rb +2 -0
  36. package/package.json +2 -2
  37. package/src/index.ts +268 -21
  38. package/src/index.web.ts +601 -246
  39. package/src/indexeddb-backend.ts +147 -6
  40. package/src/storage-runtime.ts +94 -0
  41. package/src/web-storage-backend.ts +129 -0
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createLocalStorageWebBackend = createLocalStorageWebBackend;
7
+ function getResolvedStorage(resolveStorage) {
8
+ if (resolveStorage) {
9
+ return resolveStorage();
10
+ }
11
+ if (typeof globalThis.localStorage === "undefined") {
12
+ return undefined;
13
+ }
14
+ return globalThis.localStorage;
15
+ }
16
+ function createLocalStorageWebBackend(options = {}) {
17
+ const includeKey = options.includeKey;
18
+ const resolveStorage = options.resolveStorage;
19
+ const listKeys = () => {
20
+ const storage = getResolvedStorage(resolveStorage);
21
+ if (!storage) {
22
+ return [];
23
+ }
24
+ const keys = [];
25
+ for (let index = 0; index < storage.length; index += 1) {
26
+ const key = storage.key(index);
27
+ if (!key) {
28
+ continue;
29
+ }
30
+ if (includeKey && !includeKey(key)) {
31
+ continue;
32
+ }
33
+ keys.push(key);
34
+ }
35
+ return keys;
36
+ };
37
+ return {
38
+ name: options.name ?? "localStorage",
39
+ getItem(key) {
40
+ return getResolvedStorage(resolveStorage)?.getItem(key) ?? null;
41
+ },
42
+ setItem(key, value) {
43
+ getResolvedStorage(resolveStorage)?.setItem(key, value);
44
+ },
45
+ removeItem(key) {
46
+ getResolvedStorage(resolveStorage)?.removeItem(key);
47
+ },
48
+ clear() {
49
+ const storage = getResolvedStorage(resolveStorage);
50
+ if (!storage) {
51
+ return;
52
+ }
53
+ listKeys().forEach(key => {
54
+ storage.removeItem(key);
55
+ });
56
+ },
57
+ getAllKeys() {
58
+ return listKeys();
59
+ },
60
+ getMany(keys) {
61
+ const storage = getResolvedStorage(resolveStorage);
62
+ if (!storage) {
63
+ return keys.map(() => null);
64
+ }
65
+ return keys.map(key => storage.getItem(key));
66
+ },
67
+ setMany(entries) {
68
+ const storage = getResolvedStorage(resolveStorage);
69
+ if (!storage) {
70
+ return;
71
+ }
72
+ entries.forEach(([key, value]) => {
73
+ storage.setItem(key, value);
74
+ });
75
+ },
76
+ removeMany(keys) {
77
+ const storage = getResolvedStorage(resolveStorage);
78
+ if (!storage) {
79
+ return;
80
+ }
81
+ keys.forEach(key => {
82
+ storage.removeItem(key);
83
+ });
84
+ },
85
+ size() {
86
+ return listKeys().length;
87
+ }
88
+ };
89
+ }
90
+ //# sourceMappingURL=web-storage-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getResolvedStorage","resolveStorage","globalThis","localStorage","undefined","createLocalStorageWebBackend","options","includeKey","listKeys","storage","keys","index","length","key","push","name","getItem","setItem","value","removeItem","clear","forEach","getAllKeys","getMany","map","setMany","entries","removeMany","size"],"sourceRoot":"../../src","sources":["web-storage-backend.ts"],"mappings":"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","ignoreList":[]}
@@ -3,8 +3,10 @@
3
3
  import { NitroModules } from "react-native-nitro-modules";
4
4
  import { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
5
5
  import { MIGRATION_VERSION_KEY, isStoredEnvelope, assertBatchScope, assertValidScope, decodeNativeBatchValue, serializeWithPrimitiveFastPath, deserializeWithPrimitiveFastPath, toVersionToken, prefixKey, isNamespaced } from "./internal";
6
+ import { getStorageErrorCode, isLockedStorageErrorCode } from "./storage-runtime";
6
7
  export { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
7
8
  export { migrateFromMMKV } from "./migration";
9
+ export { getStorageErrorCode } from "./storage-runtime";
8
10
  function asInternal(item) {
9
11
  return item;
10
12
  }
@@ -18,6 +20,7 @@ const registeredMigrations = new Map();
18
20
  const runMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : task => {
19
21
  Promise.resolve().then(task);
20
22
  };
23
+ const now = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
21
24
  let _storageModule = null;
22
25
  function getStorageModule() {
23
26
  if (!_storageModule) {
@@ -30,6 +33,9 @@ const memoryListeners = new Map();
30
33
  const scopedListeners = new Map([[StorageScope.Disk, new Map()], [StorageScope.Secure, new Map()]]);
31
34
  const scopedUnsubscribers = new Map();
32
35
  const scopedRawCache = new Map([[StorageScope.Disk, new Map()], [StorageScope.Secure, new Map()]]);
36
+ const pendingDiskWrites = new Map();
37
+ let diskFlushScheduled = false;
38
+ let diskWritesAsync = false;
33
39
  const pendingSecureWrites = new Map();
34
40
  let secureFlushScheduled = false;
35
41
  let secureDefaultAccessControl = AccessControl.WhenUnlocked;
@@ -59,11 +65,11 @@ function measureOperation(operation, scope, fn, keysCount = 1) {
59
65
  if (!metricsObserver) {
60
66
  return fn();
61
67
  }
62
- const start = Date.now();
68
+ const start = now();
63
69
  try {
64
70
  return fn();
65
71
  } finally {
66
- recordMetric(operation, scope, Date.now() - start, keysCount);
72
+ recordMetric(operation, scope, now() - start, keysCount);
67
73
  }
68
74
  }
69
75
  function getScopedListeners(scope) {
@@ -120,12 +126,50 @@ function addKeyListener(registry, key, listener) {
120
126
  function readPendingSecureWrite(key) {
121
127
  return pendingSecureWrites.get(key)?.value;
122
128
  }
129
+ function readPendingDiskWrite(key) {
130
+ return pendingDiskWrites.get(key)?.value;
131
+ }
132
+ function hasPendingDiskWrite(key) {
133
+ return pendingDiskWrites.has(key);
134
+ }
123
135
  function hasPendingSecureWrite(key) {
124
136
  return pendingSecureWrites.has(key);
125
137
  }
138
+ function clearPendingDiskWrite(key) {
139
+ pendingDiskWrites.delete(key);
140
+ }
126
141
  function clearPendingSecureWrite(key) {
127
142
  pendingSecureWrites.delete(key);
128
143
  }
144
+ function flushDiskWrites() {
145
+ diskFlushScheduled = false;
146
+ if (pendingDiskWrites.size === 0) {
147
+ return;
148
+ }
149
+ const writes = Array.from(pendingDiskWrites.values());
150
+ pendingDiskWrites.clear();
151
+ const keysToSet = [];
152
+ const valuesToSet = [];
153
+ const keysToRemove = [];
154
+ writes.forEach(({
155
+ key,
156
+ value
157
+ }) => {
158
+ if (value === undefined) {
159
+ keysToRemove.push(key);
160
+ return;
161
+ }
162
+ keysToSet.push(key);
163
+ valuesToSet.push(value);
164
+ });
165
+ const storageModule = getStorageModule();
166
+ if (keysToSet.length > 0) {
167
+ storageModule.setBatch(keysToSet, valuesToSet, StorageScope.Disk);
168
+ }
169
+ if (keysToRemove.length > 0) {
170
+ storageModule.removeBatch(keysToRemove, StorageScope.Disk);
171
+ }
172
+ }
129
173
  function flushSecureWrites() {
130
174
  secureFlushScheduled = false;
131
175
  if (pendingSecureWrites.size === 0) {
@@ -165,6 +209,17 @@ function flushSecureWrites() {
165
209
  storageModule.removeBatch(keysToRemove, StorageScope.Secure);
166
210
  }
167
211
  }
212
+ function scheduleDiskWrite(key, value) {
213
+ pendingDiskWrites.set(key, {
214
+ key,
215
+ value
216
+ });
217
+ if (diskFlushScheduled) {
218
+ return;
219
+ }
220
+ diskFlushScheduled = true;
221
+ runMicrotask(flushDiskWrites);
222
+ }
168
223
  function scheduleSecureWrite(key, value, accessControl) {
169
224
  const pendingWrite = {
170
225
  key,
@@ -185,6 +240,13 @@ function ensureNativeScopeSubscription(scope) {
185
240
  return;
186
241
  }
187
242
  const unsubscribe = getStorageModule().addOnChange(scope, (key, value) => {
243
+ if (scope === StorageScope.Disk) {
244
+ if (key === "") {
245
+ pendingDiskWrites.clear();
246
+ } else {
247
+ clearPendingDiskWrite(key);
248
+ }
249
+ }
188
250
  if (scope === StorageScope.Secure) {
189
251
  if (key === "") {
190
252
  pendingSecureWrites.clear();
@@ -220,6 +282,9 @@ function getRawValue(key, scope) {
220
282
  const value = memoryStore.get(key);
221
283
  return typeof value === "string" ? value : undefined;
222
284
  }
285
+ if (scope === StorageScope.Disk && hasPendingDiskWrite(key)) {
286
+ return readPendingDiskWrite(key);
287
+ }
223
288
  if (scope === StorageScope.Secure && hasPendingSecureWrite(key)) {
224
289
  return readPendingSecureWrite(key);
225
290
  }
@@ -232,6 +297,15 @@ function setRawValue(key, value, scope) {
232
297
  notifyKeyListeners(memoryListeners, key);
233
298
  return;
234
299
  }
300
+ if (scope === StorageScope.Disk) {
301
+ cacheRawValue(scope, key, value);
302
+ if (diskWritesAsync) {
303
+ scheduleDiskWrite(key, value);
304
+ return;
305
+ }
306
+ flushDiskWrites();
307
+ clearPendingDiskWrite(key);
308
+ }
235
309
  if (scope === StorageScope.Secure) {
236
310
  flushSecureWrites();
237
311
  clearPendingSecureWrite(key);
@@ -247,6 +321,15 @@ function removeRawValue(key, scope) {
247
321
  notifyKeyListeners(memoryListeners, key);
248
322
  return;
249
323
  }
324
+ if (scope === StorageScope.Disk) {
325
+ cacheRawValue(scope, key, undefined);
326
+ if (diskWritesAsync) {
327
+ scheduleDiskWrite(key, undefined);
328
+ return;
329
+ }
330
+ flushDiskWrites();
331
+ clearPendingDiskWrite(key);
332
+ }
250
333
  if (scope === StorageScope.Secure) {
251
334
  flushSecureWrites();
252
335
  clearPendingSecureWrite(key);
@@ -273,6 +356,10 @@ export const storage = {
273
356
  notifyAllListeners(memoryListeners);
274
357
  return;
275
358
  }
359
+ if (scope === StorageScope.Disk) {
360
+ flushDiskWrites();
361
+ pendingDiskWrites.clear();
362
+ }
276
363
  if (scope === StorageScope.Secure) {
277
364
  flushSecureWrites();
278
365
  pendingSecureWrites.clear();
@@ -301,6 +388,9 @@ export const storage = {
301
388
  return;
302
389
  }
303
390
  const keyPrefix = prefixKey(namespace, "");
391
+ if (scope === StorageScope.Disk) {
392
+ flushDiskWrites();
393
+ }
304
394
  if (scope === StorageScope.Secure) {
305
395
  flushSecureWrites();
306
396
  }
@@ -324,6 +414,12 @@ export const storage = {
324
414
  if (scope === StorageScope.Memory) {
325
415
  return memoryStore.has(key);
326
416
  }
417
+ if (scope === StorageScope.Disk) {
418
+ flushDiskWrites();
419
+ }
420
+ if (scope === StorageScope.Secure) {
421
+ flushSecureWrites();
422
+ }
327
423
  return getStorageModule().has(key, scope);
328
424
  });
329
425
  },
@@ -333,6 +429,12 @@ export const storage = {
333
429
  if (scope === StorageScope.Memory) {
334
430
  return Array.from(memoryStore.keys());
335
431
  }
432
+ if (scope === StorageScope.Disk) {
433
+ flushDiskWrites();
434
+ }
435
+ if (scope === StorageScope.Secure) {
436
+ flushSecureWrites();
437
+ }
336
438
  return getStorageModule().getAllKeys(scope);
337
439
  });
338
440
  },
@@ -342,6 +444,12 @@ export const storage = {
342
444
  if (scope === StorageScope.Memory) {
343
445
  return Array.from(memoryStore.keys()).filter(key => key.startsWith(prefix));
344
446
  }
447
+ if (scope === StorageScope.Disk) {
448
+ flushDiskWrites();
449
+ }
450
+ if (scope === StorageScope.Secure) {
451
+ flushSecureWrites();
452
+ }
345
453
  return getStorageModule().getKeysByPrefix(prefix, scope);
346
454
  });
347
455
  },
@@ -361,6 +469,12 @@ export const storage = {
361
469
  });
362
470
  return result;
363
471
  }
472
+ if (scope === StorageScope.Disk) {
473
+ flushDiskWrites();
474
+ }
475
+ if (scope === StorageScope.Secure) {
476
+ flushSecureWrites();
477
+ }
364
478
  const values = getStorageModule().getBatch(keys, scope);
365
479
  keys.forEach((key, idx) => {
366
480
  const value = decodeNativeBatchValue(values[idx]);
@@ -381,6 +495,12 @@ export const storage = {
381
495
  });
382
496
  return result;
383
497
  }
498
+ if (scope === StorageScope.Disk) {
499
+ flushDiskWrites();
500
+ }
501
+ if (scope === StorageScope.Secure) {
502
+ flushSecureWrites();
503
+ }
384
504
  const keys = getStorageModule().getAllKeys(scope);
385
505
  if (keys.length === 0) return result;
386
506
  const values = getStorageModule().getBatch(keys, scope);
@@ -397,6 +517,12 @@ export const storage = {
397
517
  if (scope === StorageScope.Memory) {
398
518
  return memoryStore.size;
399
519
  }
520
+ if (scope === StorageScope.Disk) {
521
+ flushDiskWrites();
522
+ }
523
+ if (scope === StorageScope.Secure) {
524
+ flushSecureWrites();
525
+ }
400
526
  return getStorageModule().size(scope);
401
527
  });
402
528
  },
@@ -411,6 +537,19 @@ export const storage = {
411
537
  getStorageModule().setSecureWritesAsync(enabled);
412
538
  });
413
539
  },
540
+ setDiskWritesAsync: enabled => {
541
+ measureOperation("storage:setDiskWritesAsync", StorageScope.Disk, () => {
542
+ diskWritesAsync = enabled;
543
+ if (!enabled) {
544
+ flushDiskWrites();
545
+ }
546
+ });
547
+ },
548
+ flushDiskWrites: () => {
549
+ measureOperation("storage:flushDiskWrites", StorageScope.Disk, () => {
550
+ flushDiskWrites();
551
+ });
552
+ },
414
553
  flushSecureWrites: () => {
415
554
  measureOperation("storage:flushSecureWrites", StorageScope.Secure, () => {
416
555
  flushSecureWrites();
@@ -439,6 +578,18 @@ export const storage = {
439
578
  resetMetrics: () => {
440
579
  metricsCounters.clear();
441
580
  },
581
+ getCapabilities: () => ({
582
+ platform: "native",
583
+ backend: {
584
+ disk: "platform-preferences",
585
+ secure: "platform-secure-storage"
586
+ },
587
+ writeBuffering: {
588
+ disk: true,
589
+ secure: true
590
+ },
591
+ errorClassification: true
592
+ }),
442
593
  getString: (key, scope) => {
443
594
  return measureOperation("storage:getString", scope, () => {
444
595
  return getRawValue(key, scope);
@@ -482,6 +633,15 @@ export function setWebSecureStorageBackend(_backend) {
482
633
  export function getWebSecureStorageBackend() {
483
634
  return undefined;
484
635
  }
636
+ export function setWebDiskStorageBackend(_backend) {
637
+ // Native platforms do not use web disk backends.
638
+ }
639
+ export function getWebDiskStorageBackend() {
640
+ return undefined;
641
+ }
642
+ export async function flushWebStorageBackends() {
643
+ // Native platforms do not use web storage backends.
644
+ }
485
645
  function canUseRawBatchPath(item) {
486
646
  return item._hasExpiration === false && item._hasValidation === false && item._isBiometric !== true && item._secureAccessControl === undefined;
487
647
  }
@@ -509,6 +669,7 @@ export function createStorageItem(config) {
509
669
  const expirationTtlMs = expiration?.ttlMs;
510
670
  const memoryExpiration = expiration && isMemory ? new Map() : null;
511
671
  const readCache = !isMemory && config.readCache === true;
672
+ const coalesceDiskWrites = config.scope === StorageScope.Disk && config.coalesceDiskWrites === true;
512
673
  const coalesceSecureWrites = config.scope === StorageScope.Secure && config.coalesceSecureWrites === true && !isBiometric;
513
674
  const defaultValue = config.defaultValue;
514
675
  const nonMemoryScope = config.scope === StorageScope.Disk ? StorageScope.Disk : config.scope === StorageScope.Secure ? StorageScope.Secure : null;
@@ -556,6 +717,12 @@ export function createStorageItem(config) {
556
717
  }
557
718
  return memoryStore.get(storageKey);
558
719
  }
720
+ if (nonMemoryScope === StorageScope.Disk) {
721
+ const pending = pendingDiskWrites.get(storageKey);
722
+ if (pending !== undefined) {
723
+ return pending.value;
724
+ }
725
+ }
559
726
  if (nonMemoryScope === StorageScope.Secure && !isBiometric) {
560
727
  const pending = pendingSecureWrites.get(storageKey);
561
728
  if (pending !== undefined) {
@@ -582,6 +749,13 @@ export function createStorageItem(config) {
582
749
  return;
583
750
  }
584
751
  cacheRawValue(nonMemoryScope, storageKey, rawValue);
752
+ if (nonMemoryScope === StorageScope.Disk) {
753
+ if (coalesceDiskWrites || diskWritesAsync) {
754
+ scheduleDiskWrite(storageKey, rawValue);
755
+ return;
756
+ }
757
+ clearPendingDiskWrite(storageKey);
758
+ }
585
759
  if (coalesceSecureWrites) {
586
760
  scheduleSecureWrite(storageKey, rawValue, secureAccessControl ?? secureDefaultAccessControl);
587
761
  return;
@@ -598,6 +772,13 @@ export function createStorageItem(config) {
598
772
  return;
599
773
  }
600
774
  cacheRawValue(nonMemoryScope, storageKey, undefined);
775
+ if (nonMemoryScope === StorageScope.Disk) {
776
+ if (coalesceDiskWrites || diskWritesAsync) {
777
+ scheduleDiskWrite(storageKey, undefined);
778
+ return;
779
+ }
780
+ clearPendingDiskWrite(storageKey);
781
+ }
601
782
  if (coalesceSecureWrites) {
602
783
  scheduleSecureWrite(storageKey, undefined, secureAccessControl ?? secureDefaultAccessControl);
603
784
  return;
@@ -758,6 +939,18 @@ export function createStorageItem(config) {
758
939
  const hasItem = () => measureOperation("item:has", config.scope, () => {
759
940
  if (isMemory) return memoryStore.has(storageKey);
760
941
  if (isBiometric) return getStorageModule().hasSecureBiometric(storageKey);
942
+ if (nonMemoryScope === StorageScope.Disk) {
943
+ const pending = pendingDiskWrites.get(storageKey);
944
+ if (pending !== undefined) {
945
+ return pending.value !== undefined;
946
+ }
947
+ }
948
+ if (nonMemoryScope === StorageScope.Secure) {
949
+ const pending = pendingSecureWrites.get(storageKey);
950
+ if (pending !== undefined) {
951
+ return pending.value !== undefined;
952
+ }
953
+ }
761
954
  return getStorageModule().has(storageKey, config.scope);
762
955
  });
763
956
  const subscribe = callback => {
@@ -820,6 +1013,13 @@ export function getBatch(items, scope) {
820
1013
  const keysToFetch = [];
821
1014
  const keyIndexes = [];
822
1015
  items.forEach((item, index) => {
1016
+ if (scope === StorageScope.Disk) {
1017
+ const pending = pendingDiskWrites.get(item.key);
1018
+ if (pending !== undefined) {
1019
+ rawValues[index] = pending.value;
1020
+ return;
1021
+ }
1022
+ }
823
1023
  if (scope === StorageScope.Secure) {
824
1024
  const pending = pendingSecureWrites.get(item.key);
825
1025
  if (pending !== undefined) {
@@ -938,6 +1138,7 @@ export function setBatch(items, scope) {
938
1138
  });
939
1139
  return;
940
1140
  }
1141
+ flushDiskWrites();
941
1142
  const useRawBatchPath = items.every(({
942
1143
  item
943
1144
  }) => canUseRawBatchPath(asInternal(item)));
@@ -962,6 +1163,9 @@ export function removeBatch(items, scope) {
962
1163
  return;
963
1164
  }
964
1165
  const keys = items.map(item => item.key);
1166
+ if (scope === StorageScope.Disk) {
1167
+ flushDiskWrites();
1168
+ }
965
1169
  if (scope === StorageScope.Secure) {
966
1170
  flushSecureWrites();
967
1171
  }
@@ -1007,6 +1211,9 @@ export function migrateToLatest(scope = StorageScope.Disk) {
1007
1211
  export function runTransaction(scope, transaction) {
1008
1212
  return measureOperation("transaction:run", scope, () => {
1009
1213
  assertValidScope(scope);
1214
+ if (scope === StorageScope.Disk) {
1215
+ flushDiskWrites();
1216
+ }
1010
1217
  if (scope === StorageScope.Secure) {
1011
1218
  flushSecureWrites();
1012
1219
  }
@@ -1073,6 +1280,9 @@ export function runTransaction(scope, transaction) {
1073
1280
  valuesToSet.push(previousValue);
1074
1281
  }
1075
1282
  });
1283
+ if (scope === StorageScope.Disk) {
1284
+ flushDiskWrites();
1285
+ }
1076
1286
  if (scope === StorageScope.Secure) {
1077
1287
  flushSecureWrites();
1078
1288
  }
@@ -1090,9 +1300,7 @@ export function runTransaction(scope, transaction) {
1090
1300
  });
1091
1301
  }
1092
1302
  export function isKeychainLockedError(err) {
1093
- if (!(err instanceof Error)) return false;
1094
- const msg = err.message;
1095
- return msg.includes("errSecInteractionNotAllowed") || msg.includes("UserNotAuthenticatedException") || msg.includes("KeyStoreException") || msg.includes("KeyPermanentlyInvalidatedException") || msg.includes("InvalidKeyException") || msg.includes("android.security.keystore");
1303
+ return isLockedStorageErrorCode(getStorageErrorCode(err));
1096
1304
  }
1097
1305
  export function createSecureAuthStorage(config, options) {
1098
1306
  const ns = options?.namespace ?? "auth";