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
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { MIGRATION_VERSION_KEY, isStoredEnvelope, assertBatchScope, assertValidScope, toVersionToken, prefixKey, isNamespaced } from "./internal";
|
|
3
|
+
import { createDurabilityCoordinator } from "./core/durability";
|
|
4
|
+
import { createMetricsRegistry } from "./core/metrics";
|
|
5
|
+
import { MIGRATION_VERSION_KEY, isStoredEnvelope, assertBatchScope, assertValidScope, toVersionToken, prefixKey, isNamespaced, escapeCollidingRawValue, unescapeCollidingRawValue } from "./internal";
|
|
6
|
+
import { assertAccessControlLevel, assertBiometricLevel, canUseRawBatchPath, canUseSecureRawBatchPath, createKeyChange, defaultDeserialize, defaultSerialize, isKeychainLockedError, isUpdater, notifyAllListeners, notifyKeyListeners, redactSecureKeyChange, typedKeys } from "./shared";
|
|
6
7
|
import { StorageEventRegistry } from "./storage-events";
|
|
8
|
+
import { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
|
|
7
9
|
const EMPTY_KEYS = Object.freeze([]);
|
|
8
10
|
function asInternal(item) {
|
|
9
11
|
return item;
|
|
@@ -14,55 +16,38 @@ export function createStorageCore(buildAdapter) {
|
|
|
14
16
|
const registeredKeyCounts = new Map();
|
|
15
17
|
const memoryStore = new Map();
|
|
16
18
|
const memoryListeners = new Map();
|
|
17
|
-
const scopedListeners =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
const scopedListeners = {
|
|
20
|
+
[StorageScope.Disk]: new Map(),
|
|
21
|
+
[StorageScope.Secure]: new Map()
|
|
22
|
+
};
|
|
23
|
+
const scopedRawCache = {
|
|
24
|
+
[StorageScope.Disk]: new Map(),
|
|
25
|
+
[StorageScope.Secure]: new Map()
|
|
26
|
+
};
|
|
24
27
|
let secureDefaultAccessControl = AccessControl.WhenUnlocked;
|
|
25
|
-
let metricsObserver;
|
|
26
28
|
let eventObserver;
|
|
27
29
|
let eventObserverRedactSecureValues = true;
|
|
28
|
-
const metricsCounters = new Map();
|
|
29
30
|
const storageEvents = new StorageEventRegistry();
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
scope,
|
|
46
|
-
durationMs,
|
|
47
|
-
keysCount
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
function measureOperation(operation, scope, fn, keysCount = 1) {
|
|
51
|
-
if (!metricsObserver) {
|
|
52
|
-
return fn();
|
|
53
|
-
}
|
|
54
|
-
const start = now();
|
|
55
|
-
try {
|
|
56
|
-
return fn();
|
|
57
|
-
} finally {
|
|
58
|
-
recordMetric(operation, scope, now() - start, keysCount);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
31
|
+
const metrics = createMetricsRegistry();
|
|
32
|
+
const durability = createDurabilityCoordinator({
|
|
33
|
+
backend: {
|
|
34
|
+
setBatch: (keys, values, scope) => {
|
|
35
|
+
adapter.backend.setBatch(keys, values, scope);
|
|
36
|
+
},
|
|
37
|
+
removeBatch: (keys, scope) => {
|
|
38
|
+
adapter.backend.removeBatch(keys, scope);
|
|
39
|
+
},
|
|
40
|
+
setSecureAccessControl: level => {
|
|
41
|
+
adapter.backend.setSecureAccessControl(level);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
resolveSecureDefaultAccessControl: () => secureDefaultAccessControl
|
|
45
|
+
});
|
|
61
46
|
function getScopedListeners(scope) {
|
|
62
|
-
return scopedListeners
|
|
47
|
+
return scopedListeners[scope];
|
|
63
48
|
}
|
|
64
49
|
function getScopeRawCache(scope) {
|
|
65
|
-
return scopedRawCache
|
|
50
|
+
return scopedRawCache[scope];
|
|
66
51
|
}
|
|
67
52
|
function cacheRawValue(scope, key, value) {
|
|
68
53
|
getScopeRawCache(scope).set(key, value);
|
|
@@ -94,7 +79,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
94
79
|
function getEventRawValue(scope, key) {
|
|
95
80
|
if (scope === StorageScope.Memory) {
|
|
96
81
|
const value = memoryStore.get(key);
|
|
97
|
-
return typeof value === "string" ? value : undefined;
|
|
82
|
+
return typeof value === "string" ? unescapeCollidingRawValue(value) : undefined;
|
|
98
83
|
}
|
|
99
84
|
return getRawValue(key, scope);
|
|
100
85
|
}
|
|
@@ -140,118 +125,49 @@ export function createStorageCore(buildAdapter) {
|
|
|
140
125
|
storageEvents.emitBatch(event);
|
|
141
126
|
eventObserver?.(eventForGlobalObserver(event));
|
|
142
127
|
}
|
|
128
|
+
function measureOperation(operation, scope, fn, keysCount = 1) {
|
|
129
|
+
return metrics.measure(operation, scope, fn, keysCount);
|
|
130
|
+
}
|
|
131
|
+
function recordMetric(operation, scope, durationMs, keysCount = 1) {
|
|
132
|
+
metrics.record(operation, scope, durationMs, keysCount);
|
|
133
|
+
}
|
|
143
134
|
function readPendingSecureWrite(key) {
|
|
144
|
-
return
|
|
135
|
+
return durability.readPendingSecureWrite(key);
|
|
145
136
|
}
|
|
146
137
|
function readPendingDiskWrite(key) {
|
|
147
|
-
return
|
|
138
|
+
return durability.readPendingDiskWrite(key);
|
|
148
139
|
}
|
|
149
140
|
function hasPendingDiskWrite(key) {
|
|
150
|
-
return
|
|
141
|
+
return durability.hasPendingDiskWrite(key);
|
|
151
142
|
}
|
|
152
143
|
function hasPendingSecureWrite(key) {
|
|
153
|
-
return
|
|
144
|
+
return durability.hasPendingSecureWrite(key);
|
|
154
145
|
}
|
|
155
146
|
function clearPendingDiskWrite(key) {
|
|
156
|
-
|
|
147
|
+
durability.clearPendingDiskWrite(key);
|
|
157
148
|
}
|
|
158
149
|
function clearPendingSecureWrite(key) {
|
|
159
|
-
|
|
150
|
+
durability.clearPendingSecureWrite(key);
|
|
160
151
|
}
|
|
161
152
|
function flushDiskWrites() {
|
|
162
|
-
|
|
163
|
-
if (pendingDiskWrites.size === 0) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
const writes = Array.from(pendingDiskWrites.values());
|
|
167
|
-
pendingDiskWrites.clear();
|
|
168
|
-
const keysToSet = [];
|
|
169
|
-
const valuesToSet = [];
|
|
170
|
-
const keysToRemove = [];
|
|
171
|
-
writes.forEach(({
|
|
172
|
-
key,
|
|
173
|
-
value
|
|
174
|
-
}) => {
|
|
175
|
-
if (value === undefined) {
|
|
176
|
-
keysToRemove.push(key);
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
keysToSet.push(key);
|
|
180
|
-
valuesToSet.push(value);
|
|
181
|
-
});
|
|
182
|
-
if (keysToSet.length > 0) {
|
|
183
|
-
adapter.backend.setBatch(keysToSet, valuesToSet, StorageScope.Disk);
|
|
184
|
-
}
|
|
185
|
-
if (keysToRemove.length > 0) {
|
|
186
|
-
adapter.backend.removeBatch(keysToRemove, StorageScope.Disk);
|
|
187
|
-
}
|
|
153
|
+
durability.flushDiskWrites();
|
|
188
154
|
}
|
|
189
155
|
function flushSecureWrites() {
|
|
190
|
-
|
|
191
|
-
if (pendingSecureWrites.size === 0) {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
const writes = Array.from(pendingSecureWrites.values());
|
|
195
|
-
pendingSecureWrites.clear();
|
|
196
|
-
const groupedSetWrites = new Map();
|
|
197
|
-
const keysToRemove = [];
|
|
198
|
-
writes.forEach(({
|
|
199
|
-
key,
|
|
200
|
-
value,
|
|
201
|
-
accessControl
|
|
202
|
-
}) => {
|
|
203
|
-
if (value === undefined) {
|
|
204
|
-
keysToRemove.push(key);
|
|
205
|
-
} else {
|
|
206
|
-
const resolvedAccessControl = accessControl ?? secureDefaultAccessControl;
|
|
207
|
-
const existingGroup = groupedSetWrites.get(resolvedAccessControl);
|
|
208
|
-
const group = existingGroup ?? {
|
|
209
|
-
keys: [],
|
|
210
|
-
values: []
|
|
211
|
-
};
|
|
212
|
-
group.keys.push(key);
|
|
213
|
-
group.values.push(value);
|
|
214
|
-
if (!existingGroup) {
|
|
215
|
-
groupedSetWrites.set(resolvedAccessControl, group);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
groupedSetWrites.forEach((group, accessControl) => {
|
|
220
|
-
adapter.backend.setSecureAccessControl(accessControl);
|
|
221
|
-
adapter.backend.setBatch(group.keys, group.values, StorageScope.Secure);
|
|
222
|
-
});
|
|
223
|
-
if (keysToRemove.length > 0) {
|
|
224
|
-
adapter.backend.removeBatch(keysToRemove, StorageScope.Secure);
|
|
225
|
-
}
|
|
156
|
+
durability.flushSecureWrites();
|
|
226
157
|
}
|
|
227
158
|
function scheduleDiskWrite(key, value) {
|
|
228
|
-
|
|
229
|
-
key,
|
|
230
|
-
value
|
|
231
|
-
});
|
|
232
|
-
if (diskFlushScheduled) {
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
diskFlushScheduled = true;
|
|
236
|
-
runMicrotask(flushDiskWrites);
|
|
159
|
+
durability.scheduleDiskWrite(key, value);
|
|
237
160
|
}
|
|
238
161
|
function scheduleSecureWrite(key, value, accessControl) {
|
|
239
|
-
|
|
240
|
-
key,
|
|
241
|
-
value
|
|
242
|
-
};
|
|
243
|
-
if (accessControl !== undefined) {
|
|
244
|
-
pendingWrite.accessControl = accessControl;
|
|
245
|
-
}
|
|
246
|
-
pendingSecureWrites.set(key, pendingWrite);
|
|
247
|
-
if (secureFlushScheduled) {
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
secureFlushScheduled = true;
|
|
251
|
-
runMicrotask(flushSecureWrites);
|
|
162
|
+
durability.scheduleSecureWrite(key, value, accessControl);
|
|
252
163
|
}
|
|
253
|
-
function
|
|
254
|
-
|
|
164
|
+
function setDiskWritesAsyncMode(enabled) {
|
|
165
|
+
durability.setDiskWritesAsync(enabled);
|
|
166
|
+
}
|
|
167
|
+
function isDiskWritesAsync() {
|
|
168
|
+
return durability.isDiskWritesAsync();
|
|
169
|
+
}
|
|
170
|
+
function getStoredRawValue(key, scope) {
|
|
255
171
|
if (scope === StorageScope.Memory) {
|
|
256
172
|
const value = memoryStore.get(key);
|
|
257
173
|
return typeof value === "string" ? value : undefined;
|
|
@@ -264,19 +180,37 @@ export function createStorageCore(buildAdapter) {
|
|
|
264
180
|
}
|
|
265
181
|
return adapter.backend.get(key, scope);
|
|
266
182
|
}
|
|
183
|
+
function getRawValue(key, scope) {
|
|
184
|
+
assertValidScope(scope);
|
|
185
|
+
if (scope === StorageScope.Memory) {
|
|
186
|
+
const value = memoryStore.get(key);
|
|
187
|
+
return typeof value === "string" ? unescapeCollidingRawValue(value) : undefined;
|
|
188
|
+
}
|
|
189
|
+
if (scope === StorageScope.Disk && hasPendingDiskWrite(key)) {
|
|
190
|
+
const pending = readPendingDiskWrite(key);
|
|
191
|
+
return pending === undefined ? undefined : unescapeCollidingRawValue(pending);
|
|
192
|
+
}
|
|
193
|
+
if (scope === StorageScope.Secure && hasPendingSecureWrite(key)) {
|
|
194
|
+
const pending = readPendingSecureWrite(key);
|
|
195
|
+
return pending === undefined ? undefined : unescapeCollidingRawValue(pending);
|
|
196
|
+
}
|
|
197
|
+
const raw = adapter.backend.get(key, scope);
|
|
198
|
+
return raw === undefined ? undefined : unescapeCollidingRawValue(raw);
|
|
199
|
+
}
|
|
267
200
|
function setRawValue(key, value, scope) {
|
|
268
201
|
assertValidScope(scope);
|
|
202
|
+
const storedValue = escapeCollidingRawValue(value);
|
|
269
203
|
const oldValue = scope === StorageScope.Memory ? getEventRawValue(scope, key) : undefined;
|
|
270
204
|
if (scope === StorageScope.Memory) {
|
|
271
|
-
memoryStore.set(key,
|
|
205
|
+
memoryStore.set(key, storedValue);
|
|
272
206
|
notifyKeyListeners(memoryListeners, key);
|
|
273
207
|
emitKeyChange(scope, key, oldValue, value, "set", "memory");
|
|
274
208
|
return;
|
|
275
209
|
}
|
|
276
210
|
if (scope === StorageScope.Disk) {
|
|
277
|
-
cacheRawValue(scope, key,
|
|
278
|
-
if (
|
|
279
|
-
scheduleDiskWrite(key,
|
|
211
|
+
cacheRawValue(scope, key, storedValue);
|
|
212
|
+
if (isDiskWritesAsync()) {
|
|
213
|
+
scheduleDiskWrite(key, storedValue);
|
|
280
214
|
emitKeyChange(scope, key, oldValue, value, "set", adapter.changeSource);
|
|
281
215
|
return;
|
|
282
216
|
}
|
|
@@ -290,8 +224,8 @@ export function createStorageCore(buildAdapter) {
|
|
|
290
224
|
adapter.backend.setSecureAccessControl(secureDefaultAccessControl);
|
|
291
225
|
}
|
|
292
226
|
}
|
|
293
|
-
adapter.backend.set(key,
|
|
294
|
-
cacheRawValue(scope, key,
|
|
227
|
+
adapter.backend.set(key, storedValue, scope);
|
|
228
|
+
cacheRawValue(scope, key, storedValue);
|
|
295
229
|
emitKeyChange(scope, key, oldValue, value, "set", adapter.changeSource);
|
|
296
230
|
}
|
|
297
231
|
function removeRawValue(key, scope) {
|
|
@@ -305,7 +239,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
305
239
|
}
|
|
306
240
|
if (scope === StorageScope.Disk) {
|
|
307
241
|
cacheRawValue(scope, key, undefined);
|
|
308
|
-
if (
|
|
242
|
+
if (isDiskWritesAsync()) {
|
|
309
243
|
scheduleDiskWrite(key, undefined);
|
|
310
244
|
emitKeyChange(scope, key, oldValue, undefined, "remove", adapter.changeSource);
|
|
311
245
|
return;
|
|
@@ -329,9 +263,6 @@ export function createStorageCore(buildAdapter) {
|
|
|
329
263
|
const parsed = Number.parseInt(raw, 10);
|
|
330
264
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
331
265
|
}
|
|
332
|
-
function writeMigrationVersion(scope, version) {
|
|
333
|
-
setRawValue(MIGRATION_VERSION_KEY, String(version), scope);
|
|
334
|
-
}
|
|
335
266
|
const internals = {
|
|
336
267
|
getScopedListeners,
|
|
337
268
|
cacheRawValue,
|
|
@@ -340,10 +271,10 @@ export function createStorageCore(buildAdapter) {
|
|
|
340
271
|
clearPendingDiskWrite,
|
|
341
272
|
clearPendingSecureWrite,
|
|
342
273
|
clearAllPendingDiskWrites: () => {
|
|
343
|
-
|
|
274
|
+
durability.clearAllPendingDiskWrites();
|
|
344
275
|
},
|
|
345
276
|
clearAllPendingSecureWrites: () => {
|
|
346
|
-
|
|
277
|
+
durability.clearAllPendingSecureWrites();
|
|
347
278
|
},
|
|
348
279
|
flushDiskWrites,
|
|
349
280
|
flushSecureWrites,
|
|
@@ -372,7 +303,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
372
303
|
}
|
|
373
304
|
const previousValues = shouldReadPreviousEventValues(scope) ? removeKeys.map(key => getEventRawValue(scope, key)) : [];
|
|
374
305
|
removeKeys.forEach(key => memoryStore.delete(key));
|
|
375
|
-
removeKeys.forEach(key =>
|
|
306
|
+
removeKeys.forEach(key => {
|
|
307
|
+
notifyKeyListeners(memoryListeners, key);
|
|
308
|
+
});
|
|
376
309
|
emitBatchChange(scope, "clear", "memory", removeKeys.map((key, index) => createKeyChange(scope, key, previousValues[index], undefined, "clear", "memory")));
|
|
377
310
|
return;
|
|
378
311
|
}
|
|
@@ -386,9 +319,11 @@ export function createStorageCore(buildAdapter) {
|
|
|
386
319
|
if (removeKeys.length === 0) {
|
|
387
320
|
return;
|
|
388
321
|
}
|
|
389
|
-
const previousValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(removeKeys, scope) : [];
|
|
322
|
+
const previousValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(removeKeys, scope).map(value => value === undefined ? undefined : unescapeCollidingRawValue(value)) : [];
|
|
390
323
|
adapter.backend.removeBatch(removeKeys, scope);
|
|
391
|
-
removeKeys.forEach(key =>
|
|
324
|
+
removeKeys.forEach(key => {
|
|
325
|
+
cacheRawValue(scope, key, undefined);
|
|
326
|
+
});
|
|
392
327
|
emitBatchChange(scope, "clear", adapter.changeSource, removeKeys.map((key, index) => createKeyChange(scope, key, previousValues[index], undefined, "clear", adapter.changeSource)));
|
|
393
328
|
});
|
|
394
329
|
}
|
|
@@ -458,11 +393,11 @@ export function createStorageCore(buildAdapter) {
|
|
|
458
393
|
}
|
|
459
394
|
if (scope === StorageScope.Disk) {
|
|
460
395
|
flushDiskWrites();
|
|
461
|
-
|
|
396
|
+
durability.clearAllPendingDiskWrites();
|
|
462
397
|
}
|
|
463
398
|
if (scope === StorageScope.Secure) {
|
|
464
399
|
flushSecureWrites();
|
|
465
|
-
|
|
400
|
+
durability.clearAllPendingSecureWrites();
|
|
466
401
|
}
|
|
467
402
|
clearScopeRawCache(scope);
|
|
468
403
|
adapter.backend.clear(scope);
|
|
@@ -556,7 +491,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
556
491
|
affectedKeys.forEach(key => {
|
|
557
492
|
memoryStore.delete(key);
|
|
558
493
|
});
|
|
559
|
-
affectedKeys.forEach(key =>
|
|
494
|
+
affectedKeys.forEach(key => {
|
|
495
|
+
notifyKeyListeners(memoryListeners, key);
|
|
496
|
+
});
|
|
560
497
|
emitBatchChange(scope, "clearNamespace", "memory", previousValues.map(({
|
|
561
498
|
key,
|
|
562
499
|
value
|
|
@@ -657,7 +594,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
657
594
|
keys.forEach((key, idx) => {
|
|
658
595
|
const value = values[idx];
|
|
659
596
|
if (value !== undefined) {
|
|
660
|
-
result[key] = value;
|
|
597
|
+
result[key] = unescapeCollidingRawValue(value);
|
|
661
598
|
}
|
|
662
599
|
});
|
|
663
600
|
return result;
|
|
@@ -670,7 +607,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
670
607
|
if (scope === StorageScope.Memory) {
|
|
671
608
|
for (const key of memoryStore.keys()) {
|
|
672
609
|
const value = memoryStore.get(key);
|
|
673
|
-
if (typeof value === "string") result[key] = value;
|
|
610
|
+
if (typeof value === "string") result[key] = unescapeCollidingRawValue(value);
|
|
674
611
|
}
|
|
675
612
|
return result;
|
|
676
613
|
}
|
|
@@ -685,7 +622,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
685
622
|
const values = adapter.backend.getBatch(keys, scope);
|
|
686
623
|
keys.forEach((key, idx) => {
|
|
687
624
|
const val = values[idx];
|
|
688
|
-
if (val !== undefined) result[key] = val;
|
|
625
|
+
if (val !== undefined) result[key] = unescapeCollidingRawValue(val);
|
|
689
626
|
});
|
|
690
627
|
return result;
|
|
691
628
|
});
|
|
@@ -716,7 +653,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
716
653
|
},
|
|
717
654
|
setDiskWritesAsync: enabled => {
|
|
718
655
|
measureOperation("storage:setDiskWritesAsync", StorageScope.Disk, () => {
|
|
719
|
-
|
|
656
|
+
setDiskWritesAsyncMode(enabled);
|
|
720
657
|
if (!enabled) {
|
|
721
658
|
flushDiskWrites();
|
|
722
659
|
}
|
|
@@ -733,22 +670,16 @@ export function createStorageCore(buildAdapter) {
|
|
|
733
670
|
});
|
|
734
671
|
},
|
|
735
672
|
setMetricsObserver: observer => {
|
|
736
|
-
|
|
673
|
+
metrics.setObserver(observer);
|
|
737
674
|
},
|
|
738
675
|
getMetricsSnapshot: () => {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
totalDurationMs: value.totalDurationMs,
|
|
744
|
-
avgDurationMs: value.count === 0 ? 0 : value.totalDurationMs / value.count,
|
|
745
|
-
maxDurationMs: value.maxDurationMs
|
|
746
|
-
};
|
|
747
|
-
});
|
|
748
|
-
return snapshot;
|
|
676
|
+
return metrics.getSnapshot();
|
|
677
|
+
},
|
|
678
|
+
getScopedMetricsSnapshot: () => {
|
|
679
|
+
return metrics.getScopedSnapshot();
|
|
749
680
|
},
|
|
750
681
|
resetMetrics: () => {
|
|
751
|
-
|
|
682
|
+
metrics.reset();
|
|
752
683
|
},
|
|
753
684
|
getSecureMetadata: key => {
|
|
754
685
|
return measureOperation("storage:getSecureMetadata", StorageScope.Secure, () => {
|
|
@@ -799,12 +730,15 @@ export function createStorageCore(buildAdapter) {
|
|
|
799
730
|
assertValidScope(scope);
|
|
800
731
|
if (keys.length === 0) return;
|
|
801
732
|
const values = keys.map(k => data[k]);
|
|
733
|
+
const storedValues = values.map(escapeCollidingRawValue);
|
|
802
734
|
const changes = keys.map((key, index) => createKeyChange(scope, key, getEventRawValue(scope, key), values[index], "import", scope === StorageScope.Memory ? "memory" : adapter.changeSource));
|
|
803
735
|
if (scope === StorageScope.Memory) {
|
|
804
736
|
keys.forEach((key, index) => {
|
|
805
|
-
memoryStore.set(key,
|
|
737
|
+
memoryStore.set(key, storedValues[index]);
|
|
738
|
+
});
|
|
739
|
+
keys.forEach(key => {
|
|
740
|
+
notifyKeyListeners(memoryListeners, key);
|
|
806
741
|
});
|
|
807
|
-
keys.forEach(key => notifyKeyListeners(memoryListeners, key));
|
|
808
742
|
emitBatchChange(scope, "import", "memory", changes);
|
|
809
743
|
return;
|
|
810
744
|
}
|
|
@@ -812,11 +746,13 @@ export function createStorageCore(buildAdapter) {
|
|
|
812
746
|
flushSecureWrites();
|
|
813
747
|
adapter.backend.setSecureAccessControl(secureDefaultAccessControl);
|
|
814
748
|
}
|
|
815
|
-
if (scope === StorageScope.Disk
|
|
749
|
+
if (scope === StorageScope.Disk) {
|
|
816
750
|
flushDiskWrites();
|
|
817
751
|
}
|
|
818
|
-
adapter.backend.setBatch(keys,
|
|
819
|
-
keys.forEach((key, index) =>
|
|
752
|
+
adapter.backend.setBatch(keys, storedValues, scope);
|
|
753
|
+
keys.forEach((key, index) => {
|
|
754
|
+
cacheRawValue(scope, key, storedValues[index]);
|
|
755
|
+
});
|
|
820
756
|
emitBatchChange(scope, "import", adapter.changeSource, changes);
|
|
821
757
|
}, keys.length);
|
|
822
758
|
}
|
|
@@ -865,20 +801,28 @@ export function createStorageCore(buildAdapter) {
|
|
|
865
801
|
hasLastValue = false;
|
|
866
802
|
lastExpiresAt = undefined;
|
|
867
803
|
};
|
|
804
|
+
const resolveNonMemoryScope = () => {
|
|
805
|
+
if (nonMemoryScope === null) {
|
|
806
|
+
throw new Error("NitroStorage: this operation requires Disk or Secure scope.");
|
|
807
|
+
}
|
|
808
|
+
return nonMemoryScope;
|
|
809
|
+
};
|
|
868
810
|
const ensureSubscription = () => {
|
|
869
811
|
if (unsubscribe) {
|
|
870
812
|
return;
|
|
871
813
|
}
|
|
872
814
|
const listener = () => {
|
|
873
815
|
invalidateParsedCache();
|
|
874
|
-
listeners.forEach(callback =>
|
|
816
|
+
listeners.forEach(callback => {
|
|
817
|
+
callback();
|
|
818
|
+
});
|
|
875
819
|
};
|
|
876
820
|
if (isMemory) {
|
|
877
821
|
unsubscribe = addKeyListener(memoryListeners, storageKey, listener);
|
|
878
822
|
return;
|
|
879
823
|
}
|
|
880
|
-
adapter.ensureScopeSubscription(
|
|
881
|
-
unsubscribe = addKeyListener(getScopedListeners(
|
|
824
|
+
adapter.ensureScopeSubscription(resolveNonMemoryScope());
|
|
825
|
+
unsubscribe = addKeyListener(getScopedListeners(resolveNonMemoryScope()), storageKey, listener);
|
|
882
826
|
};
|
|
883
827
|
const readStoredRaw = () => {
|
|
884
828
|
if (isMemory) {
|
|
@@ -889,28 +833,29 @@ export function createStorageCore(buildAdapter) {
|
|
|
889
833
|
memoryExpiration.delete(storageKey);
|
|
890
834
|
memoryStore.delete(storageKey);
|
|
891
835
|
notifyKeyListeners(memoryListeners, storageKey);
|
|
892
|
-
emitKeyChange(config.scope, storageKey, typeof expiredRaw === "string" ? expiredRaw : undefined, undefined, "expire", "memory");
|
|
836
|
+
emitKeyChange(config.scope, storageKey, typeof expiredRaw === "string" ? unescapeCollidingRawValue(expiredRaw) : undefined, undefined, "expire", "memory");
|
|
893
837
|
onExpired?.(storageKey);
|
|
894
838
|
return undefined;
|
|
895
839
|
}
|
|
896
840
|
}
|
|
897
|
-
|
|
841
|
+
const memoryStored = memoryStore.get(storageKey);
|
|
842
|
+
return typeof memoryStored === "string" ? unescapeCollidingRawValue(memoryStored) : memoryStored;
|
|
898
843
|
}
|
|
899
844
|
migrateRenamesIfNeeded();
|
|
900
845
|
if (nonMemoryScope === StorageScope.Disk) {
|
|
901
|
-
const pending =
|
|
846
|
+
const pending = durability.readPendingDiskWrite(storageKey);
|
|
902
847
|
if (pending !== undefined) {
|
|
903
|
-
return pending
|
|
848
|
+
return pending;
|
|
904
849
|
}
|
|
905
850
|
}
|
|
906
851
|
if (nonMemoryScope === StorageScope.Secure && !isBiometric) {
|
|
907
|
-
const pending =
|
|
852
|
+
const pending = durability.readPendingSecureWrite(storageKey);
|
|
908
853
|
if (pending !== undefined) {
|
|
909
|
-
return pending
|
|
854
|
+
return pending;
|
|
910
855
|
}
|
|
911
856
|
}
|
|
912
857
|
if (readCache) {
|
|
913
|
-
const cache = getScopeRawCache(
|
|
858
|
+
const cache = getScopeRawCache(resolveNonMemoryScope());
|
|
914
859
|
const cached = cache.get(storageKey);
|
|
915
860
|
if (cached !== undefined || cache.has(storageKey)) {
|
|
916
861
|
return cached;
|
|
@@ -920,7 +865,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
920
865
|
return readBackendRaw(() => adapter.backend.getSecureBiometric(storageKey));
|
|
921
866
|
}
|
|
922
867
|
const raw = readBackendRaw(() => adapter.backend.get(storageKey, config.scope));
|
|
923
|
-
cacheRawValue(
|
|
868
|
+
cacheRawValue(resolveNonMemoryScope(), storageKey, raw);
|
|
924
869
|
return raw;
|
|
925
870
|
};
|
|
926
871
|
const readBackendRaw = read => {
|
|
@@ -929,7 +874,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
929
874
|
} catch (error) {
|
|
930
875
|
onReadError?.(error);
|
|
931
876
|
if (fallbackToCacheOnReadError) {
|
|
932
|
-
const cached = getScopeRawCache(
|
|
877
|
+
const cached = getScopeRawCache(resolveNonMemoryScope()).get(storageKey);
|
|
933
878
|
if (cached !== undefined) {
|
|
934
879
|
return cached;
|
|
935
880
|
}
|
|
@@ -945,9 +890,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
945
890
|
emitKeyChange(config.scope, storageKey, oldValue, rawValue, "set", adapter.changeSource);
|
|
946
891
|
return;
|
|
947
892
|
}
|
|
948
|
-
cacheRawValue(
|
|
893
|
+
cacheRawValue(resolveNonMemoryScope(), storageKey, rawValue);
|
|
949
894
|
if (nonMemoryScope === StorageScope.Disk) {
|
|
950
|
-
if (coalesceDiskWrites ||
|
|
895
|
+
if (coalesceDiskWrites || isDiskWritesAsync()) {
|
|
951
896
|
scheduleDiskWrite(storageKey, rawValue);
|
|
952
897
|
emitKeyChange(config.scope, storageKey, oldValue, rawValue, "set", adapter.changeSource);
|
|
953
898
|
return;
|
|
@@ -1016,9 +961,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
1016
961
|
emitKeyChange(config.scope, storageKey, oldValue, undefined, operation, adapter.changeSource);
|
|
1017
962
|
return;
|
|
1018
963
|
}
|
|
1019
|
-
cacheRawValue(
|
|
964
|
+
cacheRawValue(resolveNonMemoryScope(), storageKey, undefined);
|
|
1020
965
|
if (nonMemoryScope === StorageScope.Disk) {
|
|
1021
|
-
if (coalesceDiskWrites ||
|
|
966
|
+
if (coalesceDiskWrites || isDiskWritesAsync()) {
|
|
1022
967
|
scheduleDiskWrite(storageKey, undefined);
|
|
1023
968
|
emitKeyChange(config.scope, storageKey, oldValue, undefined, operation, adapter.changeSource);
|
|
1024
969
|
return;
|
|
@@ -1042,7 +987,8 @@ export function createStorageCore(buildAdapter) {
|
|
|
1042
987
|
if (memoryExpiration) {
|
|
1043
988
|
memoryExpiration.set(storageKey, Date.now() + (expirationTtlMs ?? 0));
|
|
1044
989
|
}
|
|
1045
|
-
|
|
990
|
+
const storedValue = typeof value === "string" ? escapeCollidingRawValue(value) : value;
|
|
991
|
+
memoryStore.set(storageKey, storedValue);
|
|
1046
992
|
notifyKeyListeners(memoryListeners, storageKey);
|
|
1047
993
|
emitKeyChange(config.scope, storageKey, oldValue, typeof value === "string" ? value : undefined, "set", "memory");
|
|
1048
994
|
return;
|
|
@@ -1093,7 +1039,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
1093
1039
|
onExpired?.(storageKey);
|
|
1094
1040
|
lastValue = ensureValidatedValue(defaultValue, false);
|
|
1095
1041
|
hasLastValue = true;
|
|
1096
|
-
listeners.forEach(cb =>
|
|
1042
|
+
listeners.forEach(cb => {
|
|
1043
|
+
cb();
|
|
1044
|
+
});
|
|
1097
1045
|
return lastValue;
|
|
1098
1046
|
}
|
|
1099
1047
|
}
|
|
@@ -1129,7 +1077,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
1129
1077
|
onExpired?.(storageKey);
|
|
1130
1078
|
lastValue = ensureValidatedValue(defaultValue, false);
|
|
1131
1079
|
hasLastValue = true;
|
|
1132
|
-
listeners.forEach(cb =>
|
|
1080
|
+
listeners.forEach(cb => {
|
|
1081
|
+
cb();
|
|
1082
|
+
});
|
|
1133
1083
|
return lastValue;
|
|
1134
1084
|
}
|
|
1135
1085
|
deserializableRaw = parsed.payload;
|
|
@@ -1147,7 +1097,10 @@ export function createStorageCore(buildAdapter) {
|
|
|
1147
1097
|
};
|
|
1148
1098
|
const getCurrentVersion = () => {
|
|
1149
1099
|
const raw = readStoredRaw();
|
|
1150
|
-
|
|
1100
|
+
if (raw === undefined) {
|
|
1101
|
+
return toVersionToken(undefined);
|
|
1102
|
+
}
|
|
1103
|
+
return toVersionToken(typeof raw === "string" ? unescapeCollidingRawValue(raw) : raw);
|
|
1151
1104
|
};
|
|
1152
1105
|
const get = () => measureOperation("item:get", config.scope, () => getInternal());
|
|
1153
1106
|
const getWithVersion = () => measureOperation("item:getWithVersion", config.scope, () => ({
|
|
@@ -1213,15 +1166,13 @@ export function createStorageCore(buildAdapter) {
|
|
|
1213
1166
|
if (isMemory) return memoryStore.has(storageKey);
|
|
1214
1167
|
if (isBiometric) return adapter.backend.hasSecureBiometric(storageKey);
|
|
1215
1168
|
if (nonMemoryScope === StorageScope.Disk) {
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
return pending.value !== undefined;
|
|
1169
|
+
if (durability.hasPendingDiskWrite(storageKey)) {
|
|
1170
|
+
return durability.readPendingDiskWrite(storageKey) !== undefined;
|
|
1219
1171
|
}
|
|
1220
1172
|
}
|
|
1221
1173
|
if (nonMemoryScope === StorageScope.Secure) {
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
return pending.value !== undefined;
|
|
1174
|
+
if (durability.hasPendingSecureWrite(storageKey)) {
|
|
1175
|
+
return durability.readPendingSecureWrite(storageKey) !== undefined;
|
|
1225
1176
|
}
|
|
1226
1177
|
}
|
|
1227
1178
|
return adapter.backend.has(storageKey, config.scope);
|
|
@@ -1234,7 +1185,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
1234
1185
|
if (listeners.size === 0 && unsubscribe) {
|
|
1235
1186
|
unsubscribe();
|
|
1236
1187
|
if (!isMemory) {
|
|
1237
|
-
adapter.maybeCleanupScopeSubscription(
|
|
1188
|
+
adapter.maybeCleanupScopeSubscription(resolveNonMemoryScope());
|
|
1238
1189
|
}
|
|
1239
1190
|
unsubscribe = null;
|
|
1240
1191
|
}
|
|
@@ -1272,11 +1223,20 @@ export function createStorageCore(buildAdapter) {
|
|
|
1272
1223
|
deserialize,
|
|
1273
1224
|
_triggerListeners: () => {
|
|
1274
1225
|
invalidateParsedCache();
|
|
1275
|
-
listeners.forEach(listener =>
|
|
1226
|
+
listeners.forEach(listener => {
|
|
1227
|
+
listener();
|
|
1228
|
+
});
|
|
1276
1229
|
},
|
|
1277
1230
|
_invalidateParsedCacheOnly: () => {
|
|
1278
1231
|
invalidateParsedCache();
|
|
1279
1232
|
},
|
|
1233
|
+
_deleteMemoryEntry: () => {
|
|
1234
|
+
if (memoryExpiration) {
|
|
1235
|
+
memoryExpiration.delete(storageKey);
|
|
1236
|
+
}
|
|
1237
|
+
memoryStore.delete(storageKey);
|
|
1238
|
+
invalidateParsedCache();
|
|
1239
|
+
},
|
|
1280
1240
|
_hasValidation: validate !== undefined,
|
|
1281
1241
|
_hasExpiration: expiration !== undefined,
|
|
1282
1242
|
_readCacheEnabled: readCache,
|
|
@@ -1319,16 +1279,16 @@ export function createStorageCore(buildAdapter) {
|
|
|
1319
1279
|
const keyIndexes = [];
|
|
1320
1280
|
items.forEach((item, index) => {
|
|
1321
1281
|
if (scope === StorageScope.Disk) {
|
|
1322
|
-
const pending =
|
|
1282
|
+
const pending = durability.readPendingDiskWrite(item.key);
|
|
1323
1283
|
if (pending !== undefined) {
|
|
1324
|
-
rawValues[index] = pending
|
|
1284
|
+
rawValues[index] = pending;
|
|
1325
1285
|
return;
|
|
1326
1286
|
}
|
|
1327
1287
|
}
|
|
1328
1288
|
if (scope === StorageScope.Secure) {
|
|
1329
|
-
const pending =
|
|
1289
|
+
const pending = durability.readPendingSecureWrite(item.key);
|
|
1330
1290
|
if (pending !== undefined) {
|
|
1331
|
-
rawValues[index] = pending
|
|
1291
|
+
rawValues[index] = pending;
|
|
1332
1292
|
return;
|
|
1333
1293
|
}
|
|
1334
1294
|
}
|
|
@@ -1380,7 +1340,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
1380
1340
|
items.forEach(({
|
|
1381
1341
|
item,
|
|
1382
1342
|
value
|
|
1383
|
-
}) =>
|
|
1343
|
+
}) => {
|
|
1344
|
+
item.set(value);
|
|
1345
|
+
});
|
|
1384
1346
|
return;
|
|
1385
1347
|
}
|
|
1386
1348
|
const changes = items.map(({
|
|
@@ -1398,7 +1360,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
1398
1360
|
});
|
|
1399
1361
|
items.forEach(({
|
|
1400
1362
|
item
|
|
1401
|
-
}) =>
|
|
1363
|
+
}) => {
|
|
1364
|
+
notifyKeyListeners(memoryListeners, item.key);
|
|
1365
|
+
});
|
|
1402
1366
|
emitBatchChange(scope, "setBatch", "memory", changes);
|
|
1403
1367
|
return;
|
|
1404
1368
|
}
|
|
@@ -1418,14 +1382,16 @@ export function createStorageCore(buildAdapter) {
|
|
|
1418
1382
|
items.forEach(({
|
|
1419
1383
|
item,
|
|
1420
1384
|
value
|
|
1421
|
-
}) =>
|
|
1385
|
+
}) => {
|
|
1386
|
+
item.set(value);
|
|
1387
|
+
});
|
|
1422
1388
|
return;
|
|
1423
1389
|
}
|
|
1424
1390
|
flushSecureWrites();
|
|
1425
1391
|
const keys = secureEntries.map(({
|
|
1426
1392
|
item
|
|
1427
1393
|
}) => item.key);
|
|
1428
|
-
const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope) : [];
|
|
1394
|
+
const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope).map(value => value === undefined ? undefined : unescapeCollidingRawValue(value)) : [];
|
|
1429
1395
|
const groupedByAccessControl = new Map();
|
|
1430
1396
|
secureEntries.forEach(({
|
|
1431
1397
|
item,
|
|
@@ -1447,7 +1413,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
1447
1413
|
groupedByAccessControl.forEach((group, accessControl) => {
|
|
1448
1414
|
adapter.backend.setSecureAccessControl(accessControl);
|
|
1449
1415
|
adapter.backend.setBatch(group.keys, group.values, scope);
|
|
1450
|
-
group.keys.forEach((key, index) =>
|
|
1416
|
+
group.keys.forEach((key, index) => {
|
|
1417
|
+
cacheRawValue(scope, key, group.values[index]);
|
|
1418
|
+
});
|
|
1451
1419
|
});
|
|
1452
1420
|
emitBatchChange(scope, "setBatch", adapter.changeSource, secureEntries.map(({
|
|
1453
1421
|
item,
|
|
@@ -1463,14 +1431,18 @@ export function createStorageCore(buildAdapter) {
|
|
|
1463
1431
|
items.forEach(({
|
|
1464
1432
|
item,
|
|
1465
1433
|
value
|
|
1466
|
-
}) =>
|
|
1434
|
+
}) => {
|
|
1435
|
+
item.set(value);
|
|
1436
|
+
});
|
|
1467
1437
|
return;
|
|
1468
1438
|
}
|
|
1469
1439
|
const keys = items.map(entry => entry.item.key);
|
|
1470
1440
|
const values = items.map(entry => entry.item.serialize(entry.value));
|
|
1471
|
-
const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope) : [];
|
|
1441
|
+
const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope).map(value => value === undefined ? undefined : unescapeCollidingRawValue(value)) : [];
|
|
1472
1442
|
adapter.backend.setBatch(keys, values, scope);
|
|
1473
|
-
keys.forEach((key, index) =>
|
|
1443
|
+
keys.forEach((key, index) => {
|
|
1444
|
+
cacheRawValue(scope, key, values[index]);
|
|
1445
|
+
});
|
|
1474
1446
|
emitBatchChange(scope, "setBatch", adapter.changeSource, keys.map((key, index) => createKeyChange(scope, key, oldValues[index], values[index], "setBatch", adapter.changeSource)));
|
|
1475
1447
|
}, items.length);
|
|
1476
1448
|
}
|
|
@@ -1479,7 +1451,12 @@ export function createStorageCore(buildAdapter) {
|
|
|
1479
1451
|
assertBatchScope(items, scope);
|
|
1480
1452
|
if (scope === StorageScope.Memory) {
|
|
1481
1453
|
const changes = items.map(item => createKeyChange(scope, item.key, getEventRawValue(scope, item.key), undefined, "removeBatch", "memory"));
|
|
1482
|
-
items.forEach(item =>
|
|
1454
|
+
items.forEach(item => {
|
|
1455
|
+
asInternal(item)._deleteMemoryEntry();
|
|
1456
|
+
});
|
|
1457
|
+
items.forEach(item => {
|
|
1458
|
+
notifyKeyListeners(memoryListeners, item.key);
|
|
1459
|
+
});
|
|
1483
1460
|
emitBatchChange(scope, "removeBatch", "memory", changes);
|
|
1484
1461
|
return;
|
|
1485
1462
|
}
|
|
@@ -1490,9 +1467,11 @@ export function createStorageCore(buildAdapter) {
|
|
|
1490
1467
|
if (scope === StorageScope.Secure) {
|
|
1491
1468
|
flushSecureWrites();
|
|
1492
1469
|
}
|
|
1493
|
-
const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope) : [];
|
|
1470
|
+
const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope).map(value => value === undefined ? undefined : unescapeCollidingRawValue(value)) : [];
|
|
1494
1471
|
adapter.backend.removeBatch(keys, scope);
|
|
1495
|
-
keys.forEach(key =>
|
|
1472
|
+
keys.forEach(key => {
|
|
1473
|
+
cacheRawValue(scope, key, undefined);
|
|
1474
|
+
});
|
|
1496
1475
|
emitBatchChange(scope, "removeBatch", adapter.changeSource, keys.map((key, index) => createKeyChange(scope, key, oldValues[index], undefined, "removeBatch", adapter.changeSource)));
|
|
1497
1476
|
}, items.length);
|
|
1498
1477
|
}
|
|
@@ -1511,23 +1490,17 @@ export function createStorageCore(buildAdapter) {
|
|
|
1511
1490
|
const currentVersion = readMigrationVersion(scope);
|
|
1512
1491
|
const versions = Array.from(registeredMigrations.keys()).filter(version => version > currentVersion).sort((a, b) => a - b);
|
|
1513
1492
|
let appliedVersion = currentVersion;
|
|
1514
|
-
const context = {
|
|
1515
|
-
scope,
|
|
1516
|
-
getRaw: key => getRawValue(key, scope),
|
|
1517
|
-
setRaw: (key, value) => setRawValue(key, value, scope),
|
|
1518
|
-
removeRaw: key => removeRawValue(key, scope)
|
|
1519
|
-
};
|
|
1520
1493
|
versions.forEach(version => {
|
|
1521
1494
|
const migration = registeredMigrations.get(version);
|
|
1522
1495
|
if (!migration) {
|
|
1523
1496
|
return;
|
|
1524
1497
|
}
|
|
1525
|
-
|
|
1498
|
+
runTransaction(scope, tx => {
|
|
1499
|
+
migration(tx);
|
|
1500
|
+
tx.setRaw(MIGRATION_VERSION_KEY, String(version));
|
|
1501
|
+
});
|
|
1526
1502
|
appliedVersion = version;
|
|
1527
1503
|
});
|
|
1528
|
-
if (appliedVersion !== currentVersion) {
|
|
1529
|
-
writeMigrationVersion(scope, appliedVersion);
|
|
1530
|
-
}
|
|
1531
1504
|
return appliedVersion;
|
|
1532
1505
|
});
|
|
1533
1506
|
}
|
|
@@ -1563,7 +1536,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
1563
1536
|
}
|
|
1564
1537
|
rollback.set(key, {
|
|
1565
1538
|
kind: "raw",
|
|
1566
|
-
value:
|
|
1539
|
+
value: getStoredRawValue(key, scope),
|
|
1567
1540
|
...(scope === StorageScope.Secure && internal?._secureAccessControl !== undefined ? {
|
|
1568
1541
|
accessControl: internal._secureAccessControl
|
|
1569
1542
|
} : {})
|
|
@@ -1600,6 +1573,8 @@ export function createStorageCore(buildAdapter) {
|
|
|
1600
1573
|
return transaction(tx);
|
|
1601
1574
|
} catch (error) {
|
|
1602
1575
|
const rollbackEntries = Array.from(rollback.entries()).reverse();
|
|
1576
|
+
const rollbackSource = scope === StorageScope.Memory ? "memory" : adapter.changeSource;
|
|
1577
|
+
const preRollbackValues = rollbackEntries.length > 0 ? rollbackEntries.map(([key]) => getEventRawValue(scope, key)) : [];
|
|
1603
1578
|
if (scope === StorageScope.Memory) {
|
|
1604
1579
|
rollbackEntries.forEach(([key, record]) => {
|
|
1605
1580
|
if (record.value === NOT_SET) {
|
|
@@ -1651,13 +1626,20 @@ export function createStorageCore(buildAdapter) {
|
|
|
1651
1626
|
adapter.backend.setSecureAccessControl(accessControl);
|
|
1652
1627
|
}
|
|
1653
1628
|
adapter.backend.setBatch(group.keys, group.values, scope);
|
|
1654
|
-
group.keys.forEach((key, index) =>
|
|
1629
|
+
group.keys.forEach((key, index) => {
|
|
1630
|
+
cacheRawValue(scope, key, group.values[index]);
|
|
1631
|
+
});
|
|
1655
1632
|
});
|
|
1656
1633
|
if (keysToRemove.length > 0) {
|
|
1657
1634
|
adapter.backend.removeBatch(keysToRemove, scope);
|
|
1658
|
-
keysToRemove.forEach(key =>
|
|
1635
|
+
keysToRemove.forEach(key => {
|
|
1636
|
+
cacheRawValue(scope, key, undefined);
|
|
1637
|
+
});
|
|
1659
1638
|
}
|
|
1660
1639
|
}
|
|
1640
|
+
if (rollbackEntries.length > 0) {
|
|
1641
|
+
emitBatchChange(scope, "rollback", rollbackSource, rollbackEntries.map(([key], index) => createKeyChange(scope, key, preRollbackValues[index], getEventRawValue(scope, key), "rollback", rollbackSource)));
|
|
1642
|
+
}
|
|
1661
1643
|
throw error;
|
|
1662
1644
|
}
|
|
1663
1645
|
});
|
|
@@ -1735,12 +1717,15 @@ export function createStorageCore(buildAdapter) {
|
|
|
1735
1717
|
});
|
|
1736
1718
|
const has = id => item.get()[id] === true;
|
|
1737
1719
|
const add = id => {
|
|
1738
|
-
|
|
1720
|
+
const current = item.get();
|
|
1721
|
+
if (current[id] === true) {
|
|
1739
1722
|
return;
|
|
1740
1723
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
}
|
|
1724
|
+
const next = {
|
|
1725
|
+
...current
|
|
1726
|
+
};
|
|
1727
|
+
next[id] = true;
|
|
1728
|
+
item.set(next);
|
|
1744
1729
|
};
|
|
1745
1730
|
const deleteId = id => {
|
|
1746
1731
|
const current = item.get();
|
|
@@ -1769,7 +1754,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
1769
1754
|
toggle,
|
|
1770
1755
|
values: () => Object.keys(item.get()),
|
|
1771
1756
|
size: () => Object.keys(item.get()).length,
|
|
1772
|
-
clear: () =>
|
|
1757
|
+
clear: () => {
|
|
1758
|
+
item.set({});
|
|
1759
|
+
},
|
|
1773
1760
|
reset: item.reset,
|
|
1774
1761
|
subscribe: item.subscribe,
|
|
1775
1762
|
scope: item.scope,
|