react-native-nitro-storage 0.7.0 → 0.9.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 (110) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +89 -22
  3. package/SECURITY.md +2 -2
  4. package/cpp/bindings/HybridStorage.cpp +5 -12
  5. package/cpp/bindings/HybridStorage.hpp +1 -1
  6. package/docs/api-reference.md +11 -1
  7. package/docs/batch-transactions-migrations.md +8 -2
  8. package/docs/benchmarks.md +19 -15
  9. package/docs/web-backends.md +18 -1
  10. package/ios/IOSStorageAdapterCpp.mm +162 -17
  11. package/lib/commonjs/capabilities.js +26 -0
  12. package/lib/commonjs/capabilities.js.map +1 -0
  13. package/lib/commonjs/core/durability.js +137 -0
  14. package/lib/commonjs/core/durability.js.map +1 -0
  15. package/lib/commonjs/core/metrics.js +91 -0
  16. package/lib/commonjs/core/metrics.js.map +1 -0
  17. package/lib/commonjs/index.js +47 -22
  18. package/lib/commonjs/index.js.map +1 -1
  19. package/lib/commonjs/index.web.js +118 -66
  20. package/lib/commonjs/index.web.js.map +1 -1
  21. package/lib/commonjs/indexeddb-backend.js +73 -23
  22. package/lib/commonjs/indexeddb-backend.js.map +1 -1
  23. package/lib/commonjs/internal.js +27 -4
  24. package/lib/commonjs/internal.js.map +1 -1
  25. package/lib/commonjs/shared.js +2 -2
  26. package/lib/commonjs/shared.js.map +1 -1
  27. package/lib/commonjs/storage-core.js +224 -237
  28. package/lib/commonjs/storage-core.js.map +1 -1
  29. package/lib/commonjs/storage-events.js.map +1 -1
  30. package/lib/commonjs/storage-platform.js +6 -0
  31. package/lib/commonjs/storage-platform.js.map +1 -0
  32. package/lib/commonjs/storage-runtime.js +3 -21
  33. package/lib/commonjs/storage-runtime.js.map +1 -1
  34. package/lib/commonjs/testing.js +19 -4
  35. package/lib/commonjs/testing.js.map +1 -1
  36. package/lib/commonjs/web-backend-contract.js +27 -0
  37. package/lib/commonjs/web-backend-contract.js.map +1 -0
  38. package/lib/module/capabilities.js +21 -0
  39. package/lib/module/capabilities.js.map +1 -0
  40. package/lib/module/core/durability.js +133 -0
  41. package/lib/module/core/durability.js.map +1 -0
  42. package/lib/module/core/metrics.js +87 -0
  43. package/lib/module/core/metrics.js.map +1 -0
  44. package/lib/module/index.js +48 -23
  45. package/lib/module/index.js.map +1 -1
  46. package/lib/module/index.web.js +103 -64
  47. package/lib/module/index.web.js.map +1 -1
  48. package/lib/module/indexeddb-backend.js +73 -23
  49. package/lib/module/indexeddb-backend.js.map +1 -1
  50. package/lib/module/internal.js +24 -3
  51. package/lib/module/internal.js.map +1 -1
  52. package/lib/module/shared.js +2 -2
  53. package/lib/module/shared.js.map +1 -1
  54. package/lib/module/storage-core.js +225 -238
  55. package/lib/module/storage-core.js.map +1 -1
  56. package/lib/module/storage-events.js.map +1 -1
  57. package/lib/module/storage-platform.js +4 -0
  58. package/lib/module/storage-platform.js.map +1 -0
  59. package/lib/module/storage-runtime.js +3 -21
  60. package/lib/module/storage-runtime.js.map +1 -1
  61. package/lib/module/testing.js +5 -3
  62. package/lib/module/testing.js.map +1 -1
  63. package/lib/module/web-backend-contract.js +22 -0
  64. package/lib/module/web-backend-contract.js.map +1 -0
  65. package/lib/typescript/capabilities.d.ts +11 -0
  66. package/lib/typescript/capabilities.d.ts.map +1 -0
  67. package/lib/typescript/core/durability.d.ts +27 -0
  68. package/lib/typescript/core/durability.d.ts.map +1 -0
  69. package/lib/typescript/core/metrics.d.ts +12 -0
  70. package/lib/typescript/core/metrics.d.ts.map +1 -0
  71. package/lib/typescript/index.d.ts +5 -2
  72. package/lib/typescript/index.d.ts.map +1 -1
  73. package/lib/typescript/index.web.d.ts +8 -4
  74. package/lib/typescript/index.web.d.ts.map +1 -1
  75. package/lib/typescript/indexeddb-backend.d.ts.map +1 -1
  76. package/lib/typescript/internal.d.ts +3 -0
  77. package/lib/typescript/internal.d.ts.map +1 -1
  78. package/lib/typescript/shared.d.ts +2 -1
  79. package/lib/typescript/shared.d.ts.map +1 -1
  80. package/lib/typescript/storage-core.d.ts +6 -6
  81. package/lib/typescript/storage-core.d.ts.map +1 -1
  82. package/lib/typescript/storage-events.d.ts +2 -2
  83. package/lib/typescript/storage-events.d.ts.map +1 -1
  84. package/lib/typescript/storage-platform.d.ts +12 -0
  85. package/lib/typescript/storage-platform.d.ts.map +1 -0
  86. package/lib/typescript/storage-runtime.d.ts.map +1 -1
  87. package/lib/typescript/testing.d.ts +5 -1
  88. package/lib/typescript/testing.d.ts.map +1 -1
  89. package/lib/typescript/web-backend-contract.d.ts +10 -0
  90. package/lib/typescript/web-backend-contract.d.ts.map +1 -0
  91. package/lib/typescript/web-storage-backend.d.ts +1 -1
  92. package/lib/typescript/web-storage-backend.d.ts.map +1 -1
  93. package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +1 -1
  94. package/package.json +4 -3
  95. package/react-native-nitro-storage.podspec +3 -1
  96. package/src/capabilities.ts +36 -0
  97. package/src/core/durability.ts +170 -0
  98. package/src/core/metrics.ts +133 -0
  99. package/src/index.ts +66 -40
  100. package/src/index.web.ts +158 -86
  101. package/src/indexeddb-backend.ts +91 -20
  102. package/src/internal.ts +50 -4
  103. package/src/shared.ts +7 -6
  104. package/src/storage-core.ts +327 -292
  105. package/src/storage-events.ts +2 -1
  106. package/src/storage-platform.ts +16 -0
  107. package/src/storage-runtime.ts +13 -47
  108. package/src/testing.ts +14 -7
  109. package/src/web-backend-contract.ts +34 -0
  110. package/src/web-storage-backend.ts +1 -1
package/src/index.web.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { resolveWebWriteBuffering } from "./capabilities";
2
+ import { unescapeCollidingRawValue } from "./internal";
1
3
  import {
2
4
  assertAccessControlLevel,
3
5
  assertBiometricLevel,
@@ -5,7 +7,17 @@ import {
5
7
  notifyKeyListeners,
6
8
  type NonMemoryScope,
7
9
  } from "./shared";
8
- import { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
10
+ import {
11
+ createStorageCore,
12
+ type StorageCoreAdapter,
13
+ type StorageCoreInternals,
14
+ } from "./storage-core";
15
+ import type {
16
+ SecurityCapabilities,
17
+ StorageCapabilities,
18
+ } from "./storage-runtime";
19
+ import type { AccessControl } from "./Storage.types";
20
+ import { StorageScope, BiometricLevel } from "./Storage.types";
9
21
  import {
10
22
  createLocalStorageWebBackend,
11
23
  type WebDiskStorageBackend,
@@ -13,15 +25,6 @@ import {
13
25
  type WebStorageBackend,
14
26
  type WebStorageChangeEvent,
15
27
  } from "./web-storage-backend";
16
- import type {
17
- SecurityCapabilities,
18
- StorageCapabilities,
19
- } from "./storage-runtime";
20
- import {
21
- createStorageCore,
22
- type StorageCoreAdapter,
23
- type StorageCoreInternals,
24
- } from "./storage-core";
25
28
  export type {
26
29
  ExpirationConfig,
27
30
  Migration,
@@ -65,6 +68,11 @@ export type {
65
68
  WebStorageChangeEvent,
66
69
  WebStorageScope,
67
70
  } from "./web-storage-backend";
71
+ export {
72
+ describeWebBackendCapabilities,
73
+ isIndexedDBWebBackend,
74
+ type WebBackendCapabilities,
75
+ } from "./web-backend-contract";
68
76
  export type {
69
77
  SetItemConfig,
70
78
  SetStorageItem,
@@ -75,8 +83,9 @@ export type {
75
83
  StorageKeyRef,
76
84
  TransactionContext,
77
85
  } from "./storage-core";
86
+ export type { PlatformScope, PlatformStorage } from "./storage-platform";
78
87
 
79
- export interface Storage {
88
+ export type Storage = {
80
89
  name: string;
81
90
  equals: (other: unknown) => boolean;
82
91
  dispose: () => void;
@@ -105,19 +114,26 @@ export interface Storage {
105
114
  deleteSecureBiometric(key: string): void;
106
115
  hasSecureBiometric(key: string): boolean;
107
116
  clearSecureBiometric(): void;
108
- }
117
+ };
109
118
 
110
- const webScopeKeyIndex = new Map<NonMemoryScope, Set<string>>([
111
- [StorageScope.Disk, new Set()],
112
- [StorageScope.Secure, new Set()],
113
- ]);
119
+ const webScopeKeyIndex: Record<NonMemoryScope, Set<string>> = {
120
+ [StorageScope.Disk]: new Set(),
121
+ [StorageScope.Secure]: new Set(),
122
+ };
114
123
  const hydratedWebScopeKeyIndex = new Set<NonMemoryScope>();
115
124
  const SECURE_WEB_PREFIX = "__secure_";
116
125
  const BIOMETRIC_WEB_PREFIX = "__bio_";
117
126
  let hasWarnedAboutWebBiometricFallback = false;
118
127
  let hasWindowStorageEventSubscription = false;
119
128
 
120
- let internals!: StorageCoreInternals;
129
+ let internals: StorageCoreInternals;
130
+
131
+ function getInternals(): StorageCoreInternals {
132
+ if (internals === undefined) {
133
+ throw new Error("NitroStorage(web): storage core is not initialized.");
134
+ }
135
+ return internals;
136
+ }
121
137
 
122
138
  function createDefaultDiskBackend(): WebDiskStorageBackend {
123
139
  return createLocalStorageWebBackend({
@@ -141,6 +157,7 @@ let webDiskStorageBackend: WebDiskStorageBackend | undefined =
141
157
  let webSecureStorageBackend: WebSecureStorageBackend | undefined =
142
158
  createDefaultSecureBackend();
143
159
  const externalSyncUnsubscribers = new Map<NonMemoryScope, () => void>();
160
+ const retiredWebBackends = new Map<WebStorageBackend, NonMemoryScope>();
144
161
 
145
162
  function getBackendName(
146
163
  scope: NonMemoryScope,
@@ -216,7 +233,7 @@ function fromBiometricStorageKey(key: string): string {
216
233
  }
217
234
 
218
235
  function getWebScopeKeyIndex(scope: NonMemoryScope): Set<string> {
219
- return webScopeKeyIndex.get(scope)!;
236
+ return webScopeKeyIndex[scope];
220
237
  }
221
238
 
222
239
  function hydrateWebScopeKeyIndex(scope: NonMemoryScope): void {
@@ -256,34 +273,34 @@ function applyExternalChangeEvent(
256
273
  newValue: string | null,
257
274
  ): void {
258
275
  if (key === null) {
259
- internals.clearScopeRawCache(scope);
276
+ getInternals().clearScopeRawCache(scope);
260
277
  ensureWebScopeKeyIndex(scope).clear();
261
- notifyAllListeners(internals.getScopedListeners(scope));
278
+ notifyAllListeners(getInternals().getScopedListeners(scope));
262
279
  return;
263
280
  }
264
281
 
265
282
  if (scope === StorageScope.Secure && key.startsWith(SECURE_WEB_PREFIX)) {
266
283
  const plainKey = fromSecureStorageKey(key);
267
- const oldValue = internals.readCachedRawValue(
284
+ const oldValue = getInternals().readCachedRawValue(
268
285
  StorageScope.Secure,
269
286
  plainKey,
270
287
  );
271
288
  if (newValue === null) {
272
289
  ensureWebScopeKeyIndex(StorageScope.Secure).delete(plainKey);
273
- internals.cacheRawValue(StorageScope.Secure, plainKey, undefined);
290
+ getInternals().cacheRawValue(StorageScope.Secure, plainKey, undefined);
274
291
  } else {
275
292
  ensureWebScopeKeyIndex(StorageScope.Secure).add(plainKey);
276
- internals.cacheRawValue(StorageScope.Secure, plainKey, newValue);
293
+ getInternals().cacheRawValue(StorageScope.Secure, plainKey, newValue);
277
294
  }
278
295
  notifyKeyListeners(
279
- internals.getScopedListeners(StorageScope.Secure),
296
+ getInternals().getScopedListeners(StorageScope.Secure),
280
297
  plainKey,
281
298
  );
282
- internals.emitKeyChange(
299
+ getInternals().emitKeyChange(
283
300
  StorageScope.Secure,
284
301
  plainKey,
285
- oldValue,
286
- newValue ?? undefined,
302
+ oldValue === undefined ? undefined : unescapeCollidingRawValue(oldValue),
303
+ newValue === null ? undefined : unescapeCollidingRawValue(newValue),
287
304
  "external",
288
305
  "external",
289
306
  );
@@ -292,7 +309,7 @@ function applyExternalChangeEvent(
292
309
 
293
310
  if (scope === StorageScope.Secure && key.startsWith(BIOMETRIC_WEB_PREFIX)) {
294
311
  const plainKey = fromBiometricStorageKey(key);
295
- const oldValue = internals.readCachedRawValue(
312
+ const oldValue = getInternals().readCachedRawValue(
296
313
  StorageScope.Secure,
297
314
  plainKey,
298
315
  );
@@ -306,40 +323,40 @@ function applyExternalChangeEvent(
306
323
  ) {
307
324
  ensureWebScopeKeyIndex(StorageScope.Secure).delete(plainKey);
308
325
  }
309
- internals.cacheRawValue(StorageScope.Secure, plainKey, undefined);
326
+ getInternals().cacheRawValue(StorageScope.Secure, plainKey, undefined);
310
327
  } else {
311
328
  ensureWebScopeKeyIndex(StorageScope.Secure).add(plainKey);
312
- internals.cacheRawValue(StorageScope.Secure, plainKey, newValue);
329
+ getInternals().cacheRawValue(StorageScope.Secure, plainKey, newValue);
313
330
  }
314
331
  notifyKeyListeners(
315
- internals.getScopedListeners(StorageScope.Secure),
332
+ getInternals().getScopedListeners(StorageScope.Secure),
316
333
  plainKey,
317
334
  );
318
- internals.emitKeyChange(
335
+ getInternals().emitKeyChange(
319
336
  StorageScope.Secure,
320
337
  plainKey,
321
- oldValue,
322
- newValue ?? undefined,
338
+ oldValue === undefined ? undefined : unescapeCollidingRawValue(oldValue),
339
+ newValue === null ? undefined : unescapeCollidingRawValue(newValue),
323
340
  "external",
324
341
  "external",
325
342
  );
326
343
  return;
327
344
  }
328
345
 
329
- const oldValue = internals.readCachedRawValue(scope, key);
346
+ const oldValue = getInternals().readCachedRawValue(scope, key);
330
347
  if (newValue === null) {
331
348
  ensureWebScopeKeyIndex(scope).delete(key);
332
- internals.cacheRawValue(scope, key, undefined);
349
+ getInternals().cacheRawValue(scope, key, undefined);
333
350
  } else {
334
351
  ensureWebScopeKeyIndex(scope).add(key);
335
- internals.cacheRawValue(scope, key, newValue);
352
+ getInternals().cacheRawValue(scope, key, newValue);
336
353
  }
337
- notifyKeyListeners(internals.getScopedListeners(scope), key);
338
- internals.emitKeyChange(
354
+ notifyKeyListeners(getInternals().getScopedListeners(scope), key);
355
+ getInternals().emitKeyChange(
339
356
  scope,
340
357
  key,
341
- oldValue,
342
- newValue ?? undefined,
358
+ oldValue === undefined ? undefined : unescapeCollidingRawValue(oldValue),
359
+ newValue === null ? undefined : unescapeCollidingRawValue(newValue),
343
360
  "external",
344
361
  "external",
345
362
  );
@@ -400,6 +417,21 @@ function closeWebBackend(
400
417
  }
401
418
  }
402
419
 
420
+ function retireWebBackend(
421
+ scope: NonMemoryScope,
422
+ previousBackend: WebStorageBackend | undefined,
423
+ nextBackend: WebStorageBackend | undefined,
424
+ ): void {
425
+ if (!previousBackend || previousBackend === nextBackend) {
426
+ return;
427
+ }
428
+ if (previousBackend.flush) {
429
+ retiredWebBackends.set(previousBackend, scope);
430
+ return;
431
+ }
432
+ closeWebBackend(scope, previousBackend);
433
+ }
434
+
403
435
  function ensureExternalSyncSubscriptions(): void {
404
436
  if (
405
437
  !hasWindowStorageEventSubscription &&
@@ -428,7 +460,7 @@ const WebStorage: Storage = {
428
460
  backend.setItem(storageKey, value);
429
461
  });
430
462
  ensureWebScopeKeyIndex(scope).add(key);
431
- notifyKeyListeners(internals.getScopedListeners(scope), key);
463
+ notifyKeyListeners(getInternals().getScopedListeners(scope), key);
432
464
  },
433
465
  get: (key: string, scope: number) => {
434
466
  if (scope !== StorageScope.Disk && scope !== StorageScope.Secure) {
@@ -463,7 +495,7 @@ const WebStorage: Storage = {
463
495
  });
464
496
  }
465
497
  ensureWebScopeKeyIndex(scope).delete(key);
466
- notifyKeyListeners(internals.getScopedListeners(scope), key);
498
+ notifyKeyListeners(getInternals().getScopedListeners(scope), key);
467
499
  },
468
500
  clear: (scope: number) => {
469
501
  if (scope !== StorageScope.Disk && scope !== StorageScope.Secure) {
@@ -473,7 +505,7 @@ const WebStorage: Storage = {
473
505
  backend.clear();
474
506
  });
475
507
  ensureWebScopeKeyIndex(scope).clear();
476
- notifyAllListeners(internals.getScopedListeners(scope));
508
+ notifyAllListeners(getInternals().getScopedListeners(scope));
477
509
  },
478
510
  setBatch: (keys: string[], values: string[], scope: number) => {
479
511
  if (scope !== StorageScope.Disk && scope !== StorageScope.Secure) {
@@ -513,8 +545,10 @@ const WebStorage: Storage = {
513
545
  : storageKey,
514
546
  ),
515
547
  );
516
- const listeners = internals.getScopedListeners(scope);
517
- keys.forEach((key) => notifyKeyListeners(listeners, key));
548
+ const listeners = getInternals().getScopedListeners(scope);
549
+ keys.forEach((key) => {
550
+ notifyKeyListeners(listeners, key);
551
+ });
518
552
  },
519
553
  getBatch: (keys: string[], scope: number) => {
520
554
  if (scope !== StorageScope.Disk && scope !== StorageScope.Secure) {
@@ -564,8 +598,10 @@ const WebStorage: Storage = {
564
598
 
565
599
  const keyIndex = ensureWebScopeKeyIndex(scope);
566
600
  keys.forEach((key) => keyIndex.delete(key));
567
- const listeners = internals.getScopedListeners(scope);
568
- keys.forEach((key) => notifyKeyListeners(listeners, key));
601
+ const listeners = getInternals().getScopedListeners(scope);
602
+ keys.forEach((key) => {
603
+ notifyKeyListeners(listeners, key);
604
+ });
569
605
  },
570
606
  removeByPrefix: (prefix: string, scope: number) => {
571
607
  if (scope !== StorageScope.Disk && scope !== StorageScope.Secure) {
@@ -633,7 +669,7 @@ const WebStorage: Storage = {
633
669
  });
634
670
  ensureWebScopeKeyIndex(StorageScope.Secure).add(key);
635
671
  notifyKeyListeners(
636
- internals.getScopedListeners(StorageScope.Secure),
672
+ getInternals().getScopedListeners(StorageScope.Secure),
637
673
  key,
638
674
  );
639
675
  return;
@@ -651,10 +687,16 @@ const WebStorage: Storage = {
651
687
  withWebBackendOperation(
652
688
  StorageScope.Secure,
653
689
  "setSecureBiometric",
654
- (backend) => backend.setItem(toBiometricStorageKey(key), value),
690
+ (backend) => {
691
+ backend.setItem(toBiometricStorageKey(key), value);
692
+ backend.removeItem(toSecureStorageKey(key));
693
+ },
655
694
  );
656
695
  ensureWebScopeKeyIndex(StorageScope.Secure).add(key);
657
- notifyKeyListeners(internals.getScopedListeners(StorageScope.Secure), key);
696
+ notifyKeyListeners(
697
+ getInternals().getScopedListeners(StorageScope.Secure),
698
+ key,
699
+ );
658
700
  },
659
701
  getSecureBiometric: (key: string) => {
660
702
  const value = withWebBackendOperation(
@@ -668,7 +710,9 @@ const WebStorage: Storage = {
668
710
  withWebBackendOperation(
669
711
  StorageScope.Secure,
670
712
  "deleteSecureBiometric",
671
- (backend) => backend.removeItem(toBiometricStorageKey(key)),
713
+ (backend) => {
714
+ backend.removeItem(toBiometricStorageKey(key));
715
+ },
672
716
  );
673
717
  if (
674
718
  withWebBackendOperation(
@@ -679,7 +723,10 @@ const WebStorage: Storage = {
679
723
  ) {
680
724
  ensureWebScopeKeyIndex(StorageScope.Secure).delete(key);
681
725
  }
682
- notifyKeyListeners(internals.getScopedListeners(StorageScope.Secure), key);
726
+ notifyKeyListeners(
727
+ getInternals().getScopedListeners(StorageScope.Secure),
728
+ key,
729
+ );
683
730
  },
684
731
  hasSecureBiometric: (key: string) => {
685
732
  return (
@@ -730,8 +777,10 @@ const WebStorage: Storage = {
730
777
  keyIndex.delete(key);
731
778
  }
732
779
  });
733
- const listeners = internals.getScopedListeners(StorageScope.Secure);
734
- keysToNotify.forEach((key) => notifyKeyListeners(listeners, key));
780
+ const listeners = getInternals().getScopedListeners(StorageScope.Secure);
781
+ keysToNotify.forEach((key) => {
782
+ notifyKeyListeners(listeners, key);
783
+ });
735
784
  },
736
785
  };
737
786
 
@@ -743,7 +792,6 @@ function buildWebAdapter(
743
792
  backend: WebStorage,
744
793
  changeSource: "web",
745
794
  applyAccessControlOnSecureRawWrite: false,
746
- flushDiskWritesOnImport: true,
747
795
  ensureScopeSubscription: () => {
748
796
  ensureExternalSyncSubscriptions();
749
797
  },
@@ -763,18 +811,22 @@ export const storage = {
763
811
  ...core.storage,
764
812
  setAccessControl: (level: AccessControl) => {
765
813
  assertAccessControlLevel(level);
766
- internals.setSecureDefaultAccessControl(level);
767
- internals.recordMetric("storage:setAccessControl", StorageScope.Secure, 0);
814
+ getInternals().setSecureDefaultAccessControl(level);
815
+ getInternals().recordMetric(
816
+ "storage:setAccessControl",
817
+ StorageScope.Secure,
818
+ 0,
819
+ );
768
820
  },
769
821
  setSecureWritesAsync: (_enabled: boolean) => {
770
- internals.recordMetric(
822
+ getInternals().recordMetric(
771
823
  "storage:setSecureWritesAsync",
772
824
  StorageScope.Secure,
773
825
  0,
774
826
  );
775
827
  },
776
828
  setKeychainAccessGroup: (_group: string) => {
777
- internals.recordMetric(
829
+ getInternals().recordMetric(
778
830
  "storage:setKeychainAccessGroup",
779
831
  StorageScope.Secure,
780
832
  0,
@@ -786,10 +838,10 @@ export const storage = {
786
838
  disk: getBackendName(StorageScope.Disk, webDiskStorageBackend),
787
839
  secure: getBackendName(StorageScope.Secure, webSecureStorageBackend),
788
840
  },
789
- writeBuffering: {
790
- disk: true,
791
- secure: true,
792
- },
841
+ writeBuffering: resolveWebWriteBuffering(
842
+ webDiskStorageBackend?.name,
843
+ webSecureStorageBackend?.name,
844
+ ),
793
845
  errorClassification: true,
794
846
  }),
795
847
  getSecurityCapabilities: (): SecurityCapabilities => {
@@ -839,15 +891,13 @@ export function setWebSecureStorageBackend(
839
891
  ): void {
840
892
  const previousBackend = webSecureStorageBackend;
841
893
  const nextBackend = backend ?? createDefaultSecureBackend();
842
- internals.clearAllPendingSecureWrites();
894
+ getInternals().flushSecureWrites();
843
895
  resetBackendChangeSubscription(StorageScope.Secure);
844
896
  webSecureStorageBackend = nextBackend;
845
897
  hydratedWebScopeKeyIndex.delete(StorageScope.Secure);
846
- internals.clearScopeRawCache(StorageScope.Secure);
898
+ getInternals().clearScopeRawCache(StorageScope.Secure);
847
899
  ensureExternalSyncSubscriptions();
848
- if (previousBackend !== nextBackend) {
849
- closeWebBackend(StorageScope.Secure, previousBackend);
850
- }
900
+ retireWebBackend(StorageScope.Secure, previousBackend, nextBackend);
851
901
  }
852
902
 
853
903
  export function getWebSecureStorageBackend():
@@ -860,15 +910,13 @@ export function setWebDiskStorageBackend(
860
910
  ): void {
861
911
  const previousBackend = webDiskStorageBackend;
862
912
  const nextBackend = backend ?? createDefaultDiskBackend();
863
- internals.clearAllPendingDiskWrites();
913
+ getInternals().flushDiskWrites();
864
914
  resetBackendChangeSubscription(StorageScope.Disk);
865
915
  webDiskStorageBackend = nextBackend;
866
916
  hydratedWebScopeKeyIndex.delete(StorageScope.Disk);
867
- internals.clearScopeRawCache(StorageScope.Disk);
917
+ getInternals().clearScopeRawCache(StorageScope.Disk);
868
918
  ensureExternalSyncSubscriptions();
869
- if (previousBackend !== nextBackend) {
870
- closeWebBackend(StorageScope.Disk, previousBackend);
871
- }
919
+ retireWebBackend(StorageScope.Disk, previousBackend, nextBackend);
872
920
  }
873
921
 
874
922
  export function getWebDiskStorageBackend(): WebDiskStorageBackend | undefined {
@@ -876,21 +924,45 @@ export function getWebDiskStorageBackend(): WebDiskStorageBackend | undefined {
876
924
  }
877
925
 
878
926
  export async function flushWebStorageBackends(): Promise<void> {
879
- internals.flushDiskWrites();
880
- internals.flushSecureWrites();
927
+ getInternals().flushDiskWrites();
928
+ getInternals().flushSecureWrites();
881
929
 
882
- const flushes: Promise<void>[] = [];
883
- const diskFlush = webDiskStorageBackend?.flush;
884
- const secureFlush = webSecureStorageBackend?.flush;
930
+ const backends = Array.from(
931
+ new Set([
932
+ webDiskStorageBackend,
933
+ webSecureStorageBackend,
934
+ ...retiredWebBackends.keys(),
935
+ ]),
936
+ ).filter((backend): backend is WebStorageBackend => backend !== undefined);
937
+
938
+ const results = await Promise.allSettled(
939
+ backends.map(async (backend) => {
940
+ if (backend.flush) {
941
+ await backend.flush();
942
+ }
943
+ if (!retiredWebBackends.has(backend)) {
944
+ return;
945
+ }
946
+ if (
947
+ backend === webDiskStorageBackend ||
948
+ backend === webSecureStorageBackend
949
+ ) {
950
+ retiredWebBackends.delete(backend);
951
+ return;
952
+ }
953
+ const scope = retiredWebBackends.get(backend);
954
+ if (scope === undefined) {
955
+ return;
956
+ }
957
+ closeWebBackend(scope, backend);
958
+ retiredWebBackends.delete(backend);
959
+ }),
960
+ );
885
961
 
886
- if (diskFlush) {
887
- flushes.push(diskFlush());
962
+ const failure = results.find((result) => result.status === "rejected");
963
+ if (failure) {
964
+ throw failure.reason;
888
965
  }
889
- if (secureFlush) {
890
- flushes.push(secureFlush());
891
- }
892
-
893
- await Promise.all(flushes);
894
966
  }
895
967
 
896
968
  export {