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.
- package/CHANGELOG.md +54 -0
- package/README.md +89 -22
- package/SECURITY.md +2 -2
- package/cpp/bindings/HybridStorage.cpp +5 -12
- package/cpp/bindings/HybridStorage.hpp +1 -1
- package/docs/api-reference.md +11 -1
- package/docs/batch-transactions-migrations.md +8 -2
- package/docs/benchmarks.md +19 -15
- package/docs/web-backends.md +18 -1
- package/ios/IOSStorageAdapterCpp.mm +162 -17
- package/lib/commonjs/capabilities.js +26 -0
- package/lib/commonjs/capabilities.js.map +1 -0
- package/lib/commonjs/core/durability.js +137 -0
- package/lib/commonjs/core/durability.js.map +1 -0
- package/lib/commonjs/core/metrics.js +91 -0
- package/lib/commonjs/core/metrics.js.map +1 -0
- package/lib/commonjs/index.js +47 -22
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +118 -66
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/indexeddb-backend.js +73 -23
- package/lib/commonjs/indexeddb-backend.js.map +1 -1
- package/lib/commonjs/internal.js +27 -4
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/commonjs/shared.js +2 -2
- package/lib/commonjs/shared.js.map +1 -1
- package/lib/commonjs/storage-core.js +224 -237
- package/lib/commonjs/storage-core.js.map +1 -1
- package/lib/commonjs/storage-events.js.map +1 -1
- package/lib/commonjs/storage-platform.js +6 -0
- package/lib/commonjs/storage-platform.js.map +1 -0
- package/lib/commonjs/storage-runtime.js +3 -21
- package/lib/commonjs/storage-runtime.js.map +1 -1
- package/lib/commonjs/testing.js +19 -4
- package/lib/commonjs/testing.js.map +1 -1
- package/lib/commonjs/web-backend-contract.js +27 -0
- package/lib/commonjs/web-backend-contract.js.map +1 -0
- package/lib/module/capabilities.js +21 -0
- package/lib/module/capabilities.js.map +1 -0
- package/lib/module/core/durability.js +133 -0
- package/lib/module/core/durability.js.map +1 -0
- package/lib/module/core/metrics.js +87 -0
- package/lib/module/core/metrics.js.map +1 -0
- package/lib/module/index.js +48 -23
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +103 -64
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/indexeddb-backend.js +73 -23
- package/lib/module/indexeddb-backend.js.map +1 -1
- package/lib/module/internal.js +24 -3
- package/lib/module/internal.js.map +1 -1
- package/lib/module/shared.js +2 -2
- package/lib/module/shared.js.map +1 -1
- package/lib/module/storage-core.js +225 -238
- package/lib/module/storage-core.js.map +1 -1
- package/lib/module/storage-events.js.map +1 -1
- package/lib/module/storage-platform.js +4 -0
- package/lib/module/storage-platform.js.map +1 -0
- package/lib/module/storage-runtime.js +3 -21
- package/lib/module/storage-runtime.js.map +1 -1
- package/lib/module/testing.js +5 -3
- package/lib/module/testing.js.map +1 -1
- package/lib/module/web-backend-contract.js +22 -0
- package/lib/module/web-backend-contract.js.map +1 -0
- package/lib/typescript/capabilities.d.ts +11 -0
- package/lib/typescript/capabilities.d.ts.map +1 -0
- package/lib/typescript/core/durability.d.ts +27 -0
- package/lib/typescript/core/durability.d.ts.map +1 -0
- package/lib/typescript/core/metrics.d.ts +12 -0
- package/lib/typescript/core/metrics.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +5 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +8 -4
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/indexeddb-backend.d.ts.map +1 -1
- package/lib/typescript/internal.d.ts +3 -0
- package/lib/typescript/internal.d.ts.map +1 -1
- package/lib/typescript/shared.d.ts +2 -1
- package/lib/typescript/shared.d.ts.map +1 -1
- package/lib/typescript/storage-core.d.ts +6 -6
- package/lib/typescript/storage-core.d.ts.map +1 -1
- package/lib/typescript/storage-events.d.ts +2 -2
- package/lib/typescript/storage-events.d.ts.map +1 -1
- package/lib/typescript/storage-platform.d.ts +12 -0
- package/lib/typescript/storage-platform.d.ts.map +1 -0
- package/lib/typescript/storage-runtime.d.ts.map +1 -1
- package/lib/typescript/testing.d.ts +5 -1
- package/lib/typescript/testing.d.ts.map +1 -1
- package/lib/typescript/web-backend-contract.d.ts +10 -0
- package/lib/typescript/web-backend-contract.d.ts.map +1 -0
- package/lib/typescript/web-storage-backend.d.ts +1 -1
- package/lib/typescript/web-storage-backend.d.ts.map +1 -1
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +1 -1
- package/package.json +4 -3
- package/react-native-nitro-storage.podspec +3 -1
- package/src/capabilities.ts +36 -0
- package/src/core/durability.ts +170 -0
- package/src/core/metrics.ts +133 -0
- package/src/index.ts +66 -40
- package/src/index.web.ts +158 -86
- package/src/indexeddb-backend.ts +91 -20
- package/src/internal.ts +50 -4
- package/src/shared.ts +7 -6
- package/src/storage-core.ts +327 -292
- package/src/storage-events.ts +2 -1
- package/src/storage-platform.ts +16 -0
- package/src/storage-runtime.ts +13 -47
- package/src/testing.ts +14 -7
- package/src/web-backend-contract.ts +34 -0
- 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) =>
|
|
94
|
-
|
|
95
|
-
|
|
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) =>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
getStorageModule().
|
|
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
|
-
|
|
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: () =>
|
|
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
|
|
125
|
-
[StorageScope.Disk
|
|
126
|
-
[StorageScope.Secure
|
|
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
|
|
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
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
310
|
+
writeBuffering: resolveNativeWriteBuffering(
|
|
311
|
+
Platform.OS === "android" ? "android" : "ios",
|
|
312
|
+
secureWritesBuffered,
|
|
313
|
+
),
|
|
288
314
|
errorClassification: true,
|
|
289
315
|
}),
|
|
290
316
|
getSecurityCapabilities: (): SecurityCapabilities => ({
|