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
@@ -0,0 +1,170 @@
1
+ import { runMicrotask } from "../shared";
2
+ import { StorageScope, type AccessControl } from "../Storage.types";
3
+ import type { PendingDiskWrite, PendingSecureWrite } from "../shared";
4
+
5
+ export type DurabilityBackend = {
6
+ setBatch(keys: string[], values: string[], scope: number): void;
7
+ removeBatch(keys: string[], scope: number): void;
8
+ setSecureAccessControl(level: AccessControl): void;
9
+ };
10
+
11
+ export type DurabilityCoordinator = {
12
+ setDiskWritesAsync(enabled: boolean): void;
13
+ isDiskWritesAsync(): boolean;
14
+ hasPendingDiskWrite(key: string): boolean;
15
+ hasPendingSecureWrite(key: string): boolean;
16
+ readPendingDiskWrite(key: string): string | undefined;
17
+ readPendingSecureWrite(key: string): string | undefined;
18
+ clearPendingDiskWrite(key: string): void;
19
+ clearPendingSecureWrite(key: string): void;
20
+ clearAllPendingDiskWrites(): void;
21
+ clearAllPendingSecureWrites(): void;
22
+ scheduleDiskWrite(key: string, value: string | undefined): void;
23
+ scheduleSecureWrite(
24
+ key: string,
25
+ value: string | undefined,
26
+ accessControl?: AccessControl,
27
+ ): void;
28
+ flushDiskWrites(): void;
29
+ flushSecureWrites(): void;
30
+ };
31
+
32
+ export function createDurabilityCoordinator(options: {
33
+ backend: DurabilityBackend;
34
+ resolveSecureDefaultAccessControl(): AccessControl;
35
+ }): DurabilityCoordinator {
36
+ const pendingDiskWrites = new Map<string, PendingDiskWrite>();
37
+ let diskFlushScheduled = false;
38
+ let diskWritesAsync = false;
39
+ const pendingSecureWrites = new Map<string, PendingSecureWrite>();
40
+ let secureFlushScheduled = false;
41
+
42
+ function flushDiskWrites(): void {
43
+ diskFlushScheduled = false;
44
+
45
+ if (pendingDiskWrites.size === 0) {
46
+ return;
47
+ }
48
+
49
+ const writes = Array.from(pendingDiskWrites.values());
50
+ pendingDiskWrites.clear();
51
+
52
+ const keysToSet: string[] = [];
53
+ const valuesToSet: string[] = [];
54
+ const keysToRemove: string[] = [];
55
+
56
+ writes.forEach(({ key, value }) => {
57
+ if (value === undefined) {
58
+ keysToRemove.push(key);
59
+ return;
60
+ }
61
+
62
+ keysToSet.push(key);
63
+ valuesToSet.push(value);
64
+ });
65
+
66
+ if (keysToSet.length > 0) {
67
+ options.backend.setBatch(keysToSet, valuesToSet, StorageScope.Disk);
68
+ }
69
+ if (keysToRemove.length > 0) {
70
+ options.backend.removeBatch(keysToRemove, StorageScope.Disk);
71
+ }
72
+ }
73
+
74
+ function flushSecureWrites(): void {
75
+ secureFlushScheduled = false;
76
+
77
+ if (pendingSecureWrites.size === 0) {
78
+ return;
79
+ }
80
+
81
+ const writes = Array.from(pendingSecureWrites.values());
82
+ pendingSecureWrites.clear();
83
+
84
+ const groupedSetWrites = new Map<
85
+ AccessControl,
86
+ { keys: string[]; values: string[] }
87
+ >();
88
+ const keysToRemove: string[] = [];
89
+
90
+ writes.forEach(({ key, value, accessControl }) => {
91
+ if (value === undefined) {
92
+ keysToRemove.push(key);
93
+ } else {
94
+ const resolvedAccessControl =
95
+ accessControl ?? options.resolveSecureDefaultAccessControl();
96
+ const existingGroup = groupedSetWrites.get(resolvedAccessControl);
97
+ const group = existingGroup ?? { keys: [], values: [] };
98
+ group.keys.push(key);
99
+ group.values.push(value);
100
+ if (!existingGroup) {
101
+ groupedSetWrites.set(resolvedAccessControl, group);
102
+ }
103
+ }
104
+ });
105
+
106
+ groupedSetWrites.forEach((group, accessControl) => {
107
+ options.backend.setSecureAccessControl(accessControl);
108
+ options.backend.setBatch(group.keys, group.values, StorageScope.Secure);
109
+ });
110
+ if (keysToRemove.length > 0) {
111
+ options.backend.removeBatch(keysToRemove, StorageScope.Secure);
112
+ }
113
+ }
114
+
115
+ function scheduleDiskWrite(key: string, value: string | undefined): void {
116
+ pendingDiskWrites.set(key, { key, value });
117
+ if (diskFlushScheduled) {
118
+ return;
119
+ }
120
+ diskFlushScheduled = true;
121
+ runMicrotask(flushDiskWrites);
122
+ }
123
+
124
+ function scheduleSecureWrite(
125
+ key: string,
126
+ value: string | undefined,
127
+ accessControl?: AccessControl,
128
+ ): void {
129
+ const pendingWrite: PendingSecureWrite = { key, value };
130
+ if (accessControl !== undefined) {
131
+ pendingWrite.accessControl = accessControl;
132
+ }
133
+ pendingSecureWrites.set(key, pendingWrite);
134
+ if (secureFlushScheduled) {
135
+ return;
136
+ }
137
+ secureFlushScheduled = true;
138
+ runMicrotask(flushSecureWrites);
139
+ }
140
+
141
+ return {
142
+ setDiskWritesAsync(enabled) {
143
+ diskWritesAsync = enabled;
144
+ if (!enabled) {
145
+ flushDiskWrites();
146
+ }
147
+ },
148
+ isDiskWritesAsync: () => diskWritesAsync,
149
+ hasPendingDiskWrite: (key) => pendingDiskWrites.has(key),
150
+ hasPendingSecureWrite: (key) => pendingSecureWrites.has(key),
151
+ readPendingDiskWrite: (key) => pendingDiskWrites.get(key)?.value,
152
+ readPendingSecureWrite: (key) => pendingSecureWrites.get(key)?.value,
153
+ clearPendingDiskWrite: (key) => {
154
+ pendingDiskWrites.delete(key);
155
+ },
156
+ clearPendingSecureWrite: (key) => {
157
+ pendingSecureWrites.delete(key);
158
+ },
159
+ clearAllPendingDiskWrites: () => {
160
+ pendingDiskWrites.clear();
161
+ },
162
+ clearAllPendingSecureWrites: () => {
163
+ pendingSecureWrites.clear();
164
+ },
165
+ scheduleDiskWrite,
166
+ scheduleSecureWrite,
167
+ flushDiskWrites,
168
+ flushSecureWrites,
169
+ };
170
+ }
@@ -0,0 +1,133 @@
1
+ import { now } from "../shared";
2
+ import type { StorageMetricSummary, StorageMetricsObserver } from "../shared";
3
+ import type { StorageScope } from "../Storage.types";
4
+
5
+ export type MetricsRegistry = {
6
+ setObserver(observer?: StorageMetricsObserver): void;
7
+ record(
8
+ operation: string,
9
+ scope: StorageScope,
10
+ durationMs: number,
11
+ keysCount?: number,
12
+ ): void;
13
+ measure<T>(
14
+ operation: string,
15
+ scope: StorageScope,
16
+ fn: () => T,
17
+ keysCount?: number,
18
+ ): T;
19
+ getSnapshot(): Record<string, StorageMetricSummary>;
20
+ getScopedSnapshot(): Record<string, StorageMetricSummary>;
21
+ reset(): void;
22
+ };
23
+
24
+ export function createMetricsRegistry(): MetricsRegistry {
25
+ let observer: StorageMetricsObserver | undefined;
26
+ const counters = new Map<
27
+ string,
28
+ { count: number; totalDurationMs: number; maxDurationMs: number }
29
+ >();
30
+
31
+ function record(
32
+ operation: string,
33
+ scope: StorageScope,
34
+ durationMs: number,
35
+ keysCount = 1,
36
+ ): void {
37
+ const counterKey = `${operation}:${scope}`;
38
+ const existing = counters.get(counterKey);
39
+ if (!existing) {
40
+ counters.set(counterKey, {
41
+ count: 1,
42
+ totalDurationMs: durationMs,
43
+ maxDurationMs: durationMs,
44
+ });
45
+ } else {
46
+ existing.count += 1;
47
+ existing.totalDurationMs += durationMs;
48
+ existing.maxDurationMs = Math.max(existing.maxDurationMs, durationMs);
49
+ }
50
+
51
+ observer?.({
52
+ operation,
53
+ scope,
54
+ durationMs,
55
+ keysCount,
56
+ });
57
+ }
58
+
59
+ function measure<T>(
60
+ operation: string,
61
+ scope: StorageScope,
62
+ fn: () => T,
63
+ keysCount = 1,
64
+ ): T {
65
+ if (!observer) {
66
+ return fn();
67
+ }
68
+ const start = now();
69
+ try {
70
+ return fn();
71
+ } finally {
72
+ record(operation, scope, now() - start, keysCount);
73
+ }
74
+ }
75
+
76
+ function toSummary(value: {
77
+ count: number;
78
+ totalDurationMs: number;
79
+ maxDurationMs: number;
80
+ }): StorageMetricSummary {
81
+ return {
82
+ count: value.count,
83
+ totalDurationMs: value.totalDurationMs,
84
+ avgDurationMs:
85
+ value.count === 0 ? 0 : value.totalDurationMs / value.count,
86
+ maxDurationMs: value.maxDurationMs,
87
+ };
88
+ }
89
+
90
+ return {
91
+ setObserver(next) {
92
+ observer = next;
93
+ },
94
+ record,
95
+ measure,
96
+ getSnapshot(): Record<string, StorageMetricSummary> {
97
+ const aggregated = new Map<
98
+ string,
99
+ { count: number; totalDurationMs: number; maxDurationMs: number }
100
+ >();
101
+ counters.forEach((value, key) => {
102
+ const separator = key.lastIndexOf(":");
103
+ const operation = separator === -1 ? key : key.slice(0, separator);
104
+ const existing = aggregated.get(operation);
105
+ if (!existing) {
106
+ aggregated.set(operation, { ...value });
107
+ return;
108
+ }
109
+ existing.count += value.count;
110
+ existing.totalDurationMs += value.totalDurationMs;
111
+ existing.maxDurationMs = Math.max(
112
+ existing.maxDurationMs,
113
+ value.maxDurationMs,
114
+ );
115
+ });
116
+ const snapshot: Record<string, StorageMetricSummary> = {};
117
+ aggregated.forEach((value, operation) => {
118
+ snapshot[operation] = toSummary(value);
119
+ });
120
+ return snapshot;
121
+ },
122
+ getScopedSnapshot(): Record<string, StorageMetricSummary> {
123
+ const snapshot: Record<string, StorageMetricSummary> = {};
124
+ counters.forEach((value, key) => {
125
+ snapshot[key] = toSummary(value);
126
+ });
127
+ return snapshot;
128
+ },
129
+ reset() {
130
+ counters.clear();
131
+ },
132
+ };
133
+ }
package/src/index.ts CHANGED
@@ -1,27 +1,30 @@
1
+ import { Platform } from "react-native";
2
+ import { NitroModules } from "react-native-nitro-modules";
3
+ import { resolveNativeWriteBuffering } from "./capabilities";
4
+ import { decodeNativeBatchValue, unescapeCollidingRawValue } from "./internal";
1
5
  import {
2
6
  assertAccessControlLevel,
3
7
  notifyAllListeners,
4
8
  notifyKeyListeners,
5
9
  type NonMemoryScope,
6
10
  } from "./shared";
7
- import { NitroModules } from "react-native-nitro-modules";
8
- import type { Storage } from "./Storage.nitro";
9
- import { StorageScope, AccessControl } from "./Storage.types";
10
- import { decodeNativeBatchValue } from "./internal";
11
- import type {
12
- WebDiskStorageBackend,
13
- WebSecureStorageBackend,
14
- } from "./web-storage-backend";
15
- import type {
16
- SecurityCapabilities,
17
- StorageCapabilities,
18
- } from "./storage-runtime";
19
11
  import {
20
12
  createStorageCore,
21
13
  type StorageCoreAdapter,
22
14
  type StorageCoreBackend,
23
15
  type StorageCoreInternals,
24
16
  } from "./storage-core";
17
+ import type {
18
+ SecurityCapabilities,
19
+ StorageCapabilities,
20
+ } from "./storage-runtime";
21
+ import type { Storage } from "./Storage.nitro";
22
+ import type { AccessControl } from "./Storage.types";
23
+ import { StorageScope } from "./Storage.types";
24
+ import type {
25
+ WebDiskStorageBackend,
26
+ WebSecureStorageBackend,
27
+ } from "./web-storage-backend";
25
28
  export type {
26
29
  ExpirationConfig,
27
30
  Migration,
@@ -76,6 +79,7 @@ export type {
76
79
  StorageKeyRef,
77
80
  TransactionContext,
78
81
  } from "./storage-core";
82
+ export type { PlatformScope, PlatformStorage } from "./storage-platform";
79
83
 
80
84
  let _storageModule: Storage | null = null;
81
85
 
@@ -90,44 +94,60 @@ const nativeSecureBackend = "platform-secure-storage";
90
94
 
91
95
  const nativeBackend: StorageCoreBackend = {
92
96
  get: (key, scope) => getStorageModule().get(key, scope),
93
- set: (key, value, scope) => getStorageModule().set(key, value, scope),
94
- remove: (key, scope) => getStorageModule().remove(key, scope),
95
- clear: (scope) => getStorageModule().clear(scope),
97
+ set: (key, value, scope) => {
98
+ getStorageModule().set(key, value, scope);
99
+ },
100
+ remove: (key, scope) => {
101
+ getStorageModule().remove(key, scope);
102
+ },
103
+ clear: (scope) => {
104
+ getStorageModule().clear(scope);
105
+ },
96
106
  has: (key, scope) => getStorageModule().has(key, scope),
97
107
  getAllKeys: (scope) => getStorageModule().getAllKeys(scope) ?? [],
98
108
  getKeysByPrefix: (prefix, scope) =>
99
109
  getStorageModule().getKeysByPrefix(prefix, scope) ?? [],
100
110
  size: (scope) => getStorageModule().size(scope),
101
- setBatch: (keys, values, scope) =>
102
- getStorageModule().setBatch(keys, values, scope),
111
+ setBatch: (keys, values, scope) => {
112
+ getStorageModule().setBatch(keys, values, scope);
113
+ },
103
114
  getBatch: (keys, scope) =>
104
115
  (getStorageModule().getBatch(keys, scope) ?? []).map((value) =>
105
116
  decodeNativeBatchValue(value),
106
117
  ),
107
- removeBatch: (keys, scope) => getStorageModule().removeBatch(keys, scope),
108
- removeByPrefix: (prefix, scope) =>
109
- getStorageModule().removeByPrefix(prefix, scope),
110
- setSecureAccessControl: (level) =>
111
- getStorageModule().setSecureAccessControl(level),
118
+ removeBatch: (keys, scope) => {
119
+ getStorageModule().removeBatch(keys, scope);
120
+ },
121
+ removeByPrefix: (prefix, scope) => {
122
+ getStorageModule().removeByPrefix(prefix, scope);
123
+ },
124
+ setSecureAccessControl: (level) => {
125
+ getStorageModule().setSecureAccessControl(level);
126
+ },
112
127
  getSecureBiometric: (key) => getStorageModule().getSecureBiometric(key),
113
- setSecureBiometricWithLevel: (key, value, level) =>
114
- getStorageModule().setSecureBiometricWithLevel(key, value, level),
115
- deleteSecureBiometric: (key) => getStorageModule().deleteSecureBiometric(key),
128
+ setSecureBiometricWithLevel: (key, value, level) => {
129
+ getStorageModule().setSecureBiometricWithLevel(key, value, level);
130
+ },
131
+ deleteSecureBiometric: (key) => {
132
+ getStorageModule().deleteSecureBiometric(key);
133
+ },
116
134
  hasSecureBiometric: (key) => getStorageModule().hasSecureBiometric(key),
117
- clearSecureBiometric: () => getStorageModule().clearSecureBiometric(),
135
+ clearSecureBiometric: () => {
136
+ getStorageModule().clearSecureBiometric();
137
+ },
118
138
  };
119
139
 
120
140
  function buildNativeAdapter(
121
141
  internals: StorageCoreInternals,
122
142
  ): StorageCoreAdapter {
123
143
  const scopedUnsubscribers = new Map<NonMemoryScope, () => void>();
124
- const suppressedNativeEvents = new Map<NonMemoryScope, Map<string, number>>([
125
- [StorageScope.Disk, new Map()],
126
- [StorageScope.Secure, new Map()],
127
- ]);
144
+ const suppressedNativeEvents: Record<NonMemoryScope, Map<string, number>> = {
145
+ [StorageScope.Disk]: new Map(),
146
+ [StorageScope.Secure]: new Map(),
147
+ };
128
148
 
129
149
  function suppressNativeEvent(scope: NonMemoryScope, key: string): void {
130
- const suppressedEvents = suppressedNativeEvents.get(scope)!;
150
+ const suppressedEvents = suppressedNativeEvents[scope];
131
151
  suppressedEvents.set(key, (suppressedEvents.get(key) ?? 0) + 1);
132
152
  }
133
153
 
@@ -135,7 +155,7 @@ function buildNativeAdapter(
135
155
  scope: NonMemoryScope,
136
156
  key: string,
137
157
  ): boolean {
138
- const suppressedEvents = suppressedNativeEvents.get(scope)!;
158
+ const suppressedEvents = suppressedNativeEvents[scope];
139
159
  const count = suppressedEvents.get(key);
140
160
  if (count === undefined) {
141
161
  return false;
@@ -185,8 +205,10 @@ function buildNativeAdapter(
185
205
  internals.emitKeyChange(
186
206
  scope,
187
207
  key,
188
- oldValue,
189
- value,
208
+ oldValue === undefined
209
+ ? undefined
210
+ : unescapeCollidingRawValue(oldValue),
211
+ value === undefined ? undefined : unescapeCollidingRawValue(value),
190
212
  "external",
191
213
  "native",
192
214
  );
@@ -220,7 +242,6 @@ function buildNativeAdapter(
220
242
  backend: nativeBackend,
221
243
  changeSource: "native",
222
244
  applyAccessControlOnSecureRawWrite: true,
223
- flushDiskWritesOnImport: false,
224
245
  ensureScopeSubscription: ensureNativeScopeSubscription,
225
246
  maybeCleanupScopeSubscription: maybeCleanupNativeScopeSubscription,
226
247
  onWillEmitChanges: (scope, keys, operation, source) => {
@@ -230,7 +251,9 @@ function buildNativeAdapter(
230
251
  scope !== StorageScope.Memory &&
231
252
  scopedUnsubscribers.has(scope)
232
253
  ) {
233
- keys.forEach((key) => suppressNativeEvent(scope, key));
254
+ keys.forEach((key) => {
255
+ suppressNativeEvent(scope, key);
256
+ });
234
257
  }
235
258
  },
236
259
  getSecureMetadataProfile: () => ({
@@ -244,6 +267,8 @@ function buildNativeAdapter(
244
267
  const core = createStorageCore(buildNativeAdapter);
245
268
  const { internals } = core;
246
269
 
270
+ let secureWritesBuffered = false;
271
+
247
272
  export const storage = {
248
273
  ...core.storage,
249
274
  setAccessControl: (level: AccessControl) => {
@@ -262,6 +287,7 @@ export const storage = {
262
287
  "storage:setSecureWritesAsync",
263
288
  StorageScope.Secure,
264
289
  () => {
290
+ secureWritesBuffered = enabled;
265
291
  getStorageModule().setSecureWritesAsync(enabled);
266
292
  },
267
293
  );
@@ -281,10 +307,10 @@ export const storage = {
281
307
  disk: "platform-preferences",
282
308
  secure: nativeSecureBackend,
283
309
  },
284
- writeBuffering: {
285
- disk: true,
286
- secure: true,
287
- },
310
+ writeBuffering: resolveNativeWriteBuffering(
311
+ Platform.OS === "android" ? "android" : "ios",
312
+ secureWritesBuffered,
313
+ ),
288
314
  errorClassification: true,
289
315
  }),
290
316
  getSecurityCapabilities: (): SecurityCapabilities => ({