react-native-nitro-storage 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.watchmanconfig +6 -0
- package/README.md +45 -5
- package/android/build.gradle +5 -5
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +12 -25
- package/app.plugin.js +114 -9
- package/docs/api-reference.md +39 -36
- package/docs/batch-transactions-migrations.md +1 -1
- package/docs/recipes.md +1 -1
- package/docs/secure-storage.md +15 -4
- package/docs/web-backends.md +5 -0
- package/lib/commonjs/index.js +129 -27
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +169 -32
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/indexeddb-backend.js +28 -0
- package/lib/commonjs/indexeddb-backend.js.map +1 -1
- package/lib/commonjs/web-storage-backend.js.map +1 -1
- package/lib/module/index.js +129 -27
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +169 -32
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/indexeddb-backend.js +28 -0
- package/lib/module/indexeddb-backend.js.map +1 -1
- package/lib/module/web-storage-backend.js.map +1 -1
- package/lib/typescript/index.d.ts +10 -3
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +10 -3
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/indexeddb-backend.d.ts.map +1 -1
- package/lib/typescript/web-storage-backend.d.ts +1 -0
- package/lib/typescript/web-storage-backend.d.ts.map +1 -1
- package/package.json +5 -3
- package/src/index.ts +197 -32
- package/src/index.web.ts +250 -37
- package/src/indexeddb-backend.ts +30 -0
- package/src/web-storage-backend.ts +1 -0
|
@@ -89,6 +89,20 @@ function isUpdater(valueOrFn) {
|
|
|
89
89
|
function typedKeys(record) {
|
|
90
90
|
return Object.keys(record);
|
|
91
91
|
}
|
|
92
|
+
function assertEnumInteger(value, min, max, label) {
|
|
93
|
+
if (!Number.isFinite(value) || value < min || value > max) {
|
|
94
|
+
throw new Error(`NitroStorage: Invalid ${label}`);
|
|
95
|
+
}
|
|
96
|
+
if (value !== Math.trunc(value)) {
|
|
97
|
+
throw new Error(`NitroStorage: Invalid ${label}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function assertAccessControlLevel(level) {
|
|
101
|
+
assertEnumInteger(level, 0, 4, "access control level");
|
|
102
|
+
}
|
|
103
|
+
function assertBiometricLevel(level) {
|
|
104
|
+
assertEnumInteger(level, 0, 2, "biometric level");
|
|
105
|
+
}
|
|
92
106
|
const registeredMigrations = new Map();
|
|
93
107
|
const runMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : task => {
|
|
94
108
|
Promise.resolve().then(task);
|
|
@@ -112,6 +126,7 @@ let hasWarnedAboutWebBiometricFallback = false;
|
|
|
112
126
|
let hasWindowStorageEventSubscription = false;
|
|
113
127
|
let metricsObserver;
|
|
114
128
|
let eventObserver;
|
|
129
|
+
let eventObserverRedactSecureValues = true;
|
|
115
130
|
const metricsCounters = new Map();
|
|
116
131
|
const storageEvents = new _storageEvents.StorageEventRegistry();
|
|
117
132
|
function recordMetric(operation, scope, durationMs, keysCount = 1) {
|
|
@@ -307,6 +322,16 @@ function resetBackendChangeSubscription(scope) {
|
|
|
307
322
|
externalSyncUnsubscribers.get(scope)?.();
|
|
308
323
|
externalSyncUnsubscribers.delete(scope);
|
|
309
324
|
}
|
|
325
|
+
function closeWebBackend(scope, backend) {
|
|
326
|
+
if (!backend?.close) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
backend.close();
|
|
331
|
+
} catch (error) {
|
|
332
|
+
throw createWebStorageError(scope, "close", error, backend);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
310
335
|
function ensureExternalSyncSubscriptions() {
|
|
311
336
|
if (!hasWindowStorageEventSubscription && typeof window !== "undefined" && typeof window.addEventListener === "function") {
|
|
312
337
|
window.addEventListener("storage", handleWebStorageEvent);
|
|
@@ -387,10 +412,42 @@ function createKeyChange(scope, key, oldValue, newValue, operation, source) {
|
|
|
387
412
|
function hasStorageChangeObservers(scope) {
|
|
388
413
|
return storageEvents.hasListeners(scope) || eventObserver !== undefined;
|
|
389
414
|
}
|
|
415
|
+
function shouldReadPreviousEventValues(scope) {
|
|
416
|
+
if (storageEvents.hasListeners(scope)) {
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
if (!eventObserver) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
return scope !== _Storage.StorageScope.Secure || !eventObserverRedactSecureValues;
|
|
423
|
+
}
|
|
424
|
+
const SECURE_EVENT_REDACTED_VALUE = "[secure]";
|
|
425
|
+
function redactSecureKeyChange(event) {
|
|
426
|
+
if (event.scope !== _Storage.StorageScope.Secure) {
|
|
427
|
+
return event;
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
...event,
|
|
431
|
+
oldValue: event.oldValue === undefined ? undefined : SECURE_EVENT_REDACTED_VALUE,
|
|
432
|
+
newValue: event.newValue === undefined ? undefined : SECURE_EVENT_REDACTED_VALUE
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function eventForGlobalObserver(event) {
|
|
436
|
+
if (!eventObserverRedactSecureValues || event.scope !== _Storage.StorageScope.Secure) {
|
|
437
|
+
return event;
|
|
438
|
+
}
|
|
439
|
+
if (event.type === "key") {
|
|
440
|
+
return redactSecureKeyChange(event);
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
...event,
|
|
444
|
+
changes: event.changes.map(redactSecureKeyChange)
|
|
445
|
+
};
|
|
446
|
+
}
|
|
390
447
|
function emitKeyChange(scope, key, oldValue, newValue, operation, source) {
|
|
391
448
|
const event = createKeyChange(scope, key, oldValue, newValue, operation, source);
|
|
392
449
|
storageEvents.emitKey(event);
|
|
393
|
-
eventObserver?.(event);
|
|
450
|
+
eventObserver?.(eventForGlobalObserver(event));
|
|
394
451
|
}
|
|
395
452
|
function emitBatchChange(scope, operation, source, changes) {
|
|
396
453
|
if (changes.length === 0) {
|
|
@@ -404,7 +461,7 @@ function emitBatchChange(scope, operation, source, changes) {
|
|
|
404
461
|
changes
|
|
405
462
|
};
|
|
406
463
|
storageEvents.emitBatch(event);
|
|
407
|
-
eventObserver?.(event);
|
|
464
|
+
eventObserver?.(eventForGlobalObserver(event));
|
|
408
465
|
}
|
|
409
466
|
function readPendingSecureWrite(key) {
|
|
410
467
|
return pendingSecureWrites.get(key)?.value;
|
|
@@ -574,6 +631,9 @@ const WebStorage = {
|
|
|
574
631
|
if (scope !== _Storage.StorageScope.Disk && scope !== _Storage.StorageScope.Secure) {
|
|
575
632
|
return;
|
|
576
633
|
}
|
|
634
|
+
if (keys.length !== values.length) {
|
|
635
|
+
throw new Error("NitroStorage: Keys and values size mismatch in setBatch");
|
|
636
|
+
}
|
|
577
637
|
const entries = [];
|
|
578
638
|
keys.forEach((key, index) => {
|
|
579
639
|
const value = values[index];
|
|
@@ -592,7 +652,7 @@ const WebStorage = {
|
|
|
592
652
|
});
|
|
593
653
|
});
|
|
594
654
|
const keyIndex = ensureWebScopeKeyIndex(scope);
|
|
595
|
-
|
|
655
|
+
entries.forEach(([storageKey]) => keyIndex.add(scope === _Storage.StorageScope.Secure ? storageKey.slice(SECURE_WEB_PREFIX.length) : storageKey));
|
|
596
656
|
const listeners = getScopedListeners(scope);
|
|
597
657
|
keys.forEach(key => notifyKeyListeners(listeners, key));
|
|
598
658
|
},
|
|
@@ -678,13 +738,25 @@ const WebStorage = {
|
|
|
678
738
|
}
|
|
679
739
|
return 0;
|
|
680
740
|
},
|
|
681
|
-
setSecureAccessControl:
|
|
741
|
+
setSecureAccessControl: level => {
|
|
742
|
+
assertAccessControlLevel(level);
|
|
743
|
+
},
|
|
682
744
|
setSecureWritesAsync: _enabled => {},
|
|
683
745
|
setKeychainAccessGroup: () => {},
|
|
684
746
|
setSecureBiometric: (key, value) => {
|
|
685
747
|
WebStorage.setSecureBiometricWithLevel(key, value, _Storage.BiometricLevel.BiometryOnly);
|
|
686
748
|
},
|
|
687
|
-
setSecureBiometricWithLevel: (key, value,
|
|
749
|
+
setSecureBiometricWithLevel: (key, value, level) => {
|
|
750
|
+
assertBiometricLevel(level);
|
|
751
|
+
if (level === _Storage.BiometricLevel.None) {
|
|
752
|
+
withWebBackendOperation(_Storage.StorageScope.Secure, "setSecure", backend => {
|
|
753
|
+
backend.removeItem(toBiometricStorageKey(key));
|
|
754
|
+
backend.setItem(toSecureStorageKey(key), value);
|
|
755
|
+
});
|
|
756
|
+
ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).add(key);
|
|
757
|
+
notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), key);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
688
760
|
if (typeof __DEV__ !== "undefined" && __DEV__ && !hasWarnedAboutWebBiometricFallback) {
|
|
689
761
|
hasWarnedAboutWebBiometricFallback = true;
|
|
690
762
|
console.warn("[NitroStorage] Biometric storage is not supported on web. Using localStorage.");
|
|
@@ -837,15 +909,16 @@ const storage = exports.storage = {
|
|
|
837
909
|
subscribeNamespace: (namespace, scope, listener) => {
|
|
838
910
|
return storage.subscribePrefix(scope, (0, _internal.prefixKey)(namespace, ""), listener);
|
|
839
911
|
},
|
|
840
|
-
setEventObserver: observer => {
|
|
912
|
+
setEventObserver: (observer, options = {}) => {
|
|
841
913
|
eventObserver = observer;
|
|
914
|
+
eventObserverRedactSecureValues = options.redactSecureValues !== false;
|
|
842
915
|
if (observer) {
|
|
843
916
|
ensureExternalSyncSubscriptions();
|
|
844
917
|
}
|
|
845
918
|
},
|
|
846
919
|
clear: scope => {
|
|
847
920
|
measureOperation("storage:clear", scope, () => {
|
|
848
|
-
const previousValues =
|
|
921
|
+
const previousValues = shouldReadPreviousEventValues(scope) ? storage.getAll(scope) : {};
|
|
849
922
|
if (scope === _Storage.StorageScope.Memory) {
|
|
850
923
|
memoryStore.clear();
|
|
851
924
|
notifyAllListeners(memoryListeners);
|
|
@@ -895,7 +968,7 @@ const storage = exports.storage = {
|
|
|
895
968
|
return;
|
|
896
969
|
}
|
|
897
970
|
const keyPrefix = (0, _internal.prefixKey)(namespace, "");
|
|
898
|
-
const previousValues =
|
|
971
|
+
const previousValues = shouldReadPreviousEventValues(scope) ? storage.getByPrefix(keyPrefix, scope) : {};
|
|
899
972
|
if (scope === _Storage.StorageScope.Disk) {
|
|
900
973
|
flushDiskWrites();
|
|
901
974
|
}
|
|
@@ -1018,9 +1091,15 @@ const storage = exports.storage = {
|
|
|
1018
1091
|
return result;
|
|
1019
1092
|
});
|
|
1020
1093
|
},
|
|
1021
|
-
export: scope => {
|
|
1094
|
+
export: (scope, options = {}) => {
|
|
1095
|
+
if (scope === _Storage.StorageScope.Secure && options.includeSecureValues !== true) {
|
|
1096
|
+
throw new Error("NitroStorage: exporting Secure scope exposes raw secret values. Pass { includeSecureValues: true } or use exportSecureUnsafe().");
|
|
1097
|
+
}
|
|
1022
1098
|
return measureOperation("storage:export", scope, () => storage.getAll(scope));
|
|
1023
1099
|
},
|
|
1100
|
+
exportSecureUnsafe: () => {
|
|
1101
|
+
return measureOperation("storage:exportSecureUnsafe", _Storage.StorageScope.Secure, () => storage.getAll(_Storage.StorageScope.Secure));
|
|
1102
|
+
},
|
|
1024
1103
|
size: scope => {
|
|
1025
1104
|
return measureOperation("storage:size", scope, () => {
|
|
1026
1105
|
(0, _internal.assertValidScope)(scope);
|
|
@@ -1035,6 +1114,7 @@ const storage = exports.storage = {
|
|
|
1035
1114
|
});
|
|
1036
1115
|
},
|
|
1037
1116
|
setAccessControl: level => {
|
|
1117
|
+
assertAccessControlLevel(level);
|
|
1038
1118
|
secureDefaultAccessControl = level;
|
|
1039
1119
|
recordMetric("storage:setAccessControl", _Storage.StorageScope.Secure, 0);
|
|
1040
1120
|
},
|
|
@@ -1187,23 +1267,33 @@ const storage = exports.storage = {
|
|
|
1187
1267
|
}
|
|
1188
1268
|
};
|
|
1189
1269
|
function setWebSecureStorageBackend(backend) {
|
|
1270
|
+
const previousBackend = webSecureStorageBackend;
|
|
1271
|
+
const nextBackend = backend ?? createDefaultSecureBackend();
|
|
1190
1272
|
pendingSecureWrites.clear();
|
|
1191
|
-
webSecureStorageBackend = backend ?? createDefaultSecureBackend();
|
|
1192
1273
|
resetBackendChangeSubscription(_Storage.StorageScope.Secure);
|
|
1274
|
+
webSecureStorageBackend = nextBackend;
|
|
1193
1275
|
hydratedWebScopeKeyIndex.delete(_Storage.StorageScope.Secure);
|
|
1194
1276
|
clearScopeRawCache(_Storage.StorageScope.Secure);
|
|
1195
1277
|
ensureExternalSyncSubscriptions();
|
|
1278
|
+
if (previousBackend !== nextBackend) {
|
|
1279
|
+
closeWebBackend(_Storage.StorageScope.Secure, previousBackend);
|
|
1280
|
+
}
|
|
1196
1281
|
}
|
|
1197
1282
|
function getWebSecureStorageBackend() {
|
|
1198
1283
|
return webSecureStorageBackend;
|
|
1199
1284
|
}
|
|
1200
1285
|
function setWebDiskStorageBackend(backend) {
|
|
1286
|
+
const previousBackend = webDiskStorageBackend;
|
|
1287
|
+
const nextBackend = backend ?? createDefaultDiskBackend();
|
|
1201
1288
|
pendingDiskWrites.clear();
|
|
1202
|
-
webDiskStorageBackend = backend ?? createDefaultDiskBackend();
|
|
1203
1289
|
resetBackendChangeSubscription(_Storage.StorageScope.Disk);
|
|
1290
|
+
webDiskStorageBackend = nextBackend;
|
|
1204
1291
|
hydratedWebScopeKeyIndex.delete(_Storage.StorageScope.Disk);
|
|
1205
1292
|
clearScopeRawCache(_Storage.StorageScope.Disk);
|
|
1206
1293
|
ensureExternalSyncSubscriptions();
|
|
1294
|
+
if (previousBackend !== nextBackend) {
|
|
1295
|
+
closeWebBackend(_Storage.StorageScope.Disk, previousBackend);
|
|
1296
|
+
}
|
|
1207
1297
|
}
|
|
1208
1298
|
function getWebDiskStorageBackend() {
|
|
1209
1299
|
return webDiskStorageBackend;
|
|
@@ -1256,6 +1346,12 @@ function createStorageItem(config) {
|
|
|
1256
1346
|
if (expiration && expiration.ttlMs <= 0) {
|
|
1257
1347
|
throw new Error("expiration.ttlMs must be greater than 0.");
|
|
1258
1348
|
}
|
|
1349
|
+
if (config.scope === _Storage.StorageScope.Secure) {
|
|
1350
|
+
assertBiometricLevel(resolvedBiometricLevel);
|
|
1351
|
+
if (secureAccessControl !== undefined) {
|
|
1352
|
+
assertAccessControlLevel(secureAccessControl);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1259
1355
|
const listeners = new Set();
|
|
1260
1356
|
let unsubscribe = null;
|
|
1261
1357
|
let lastRaw = undefined;
|
|
@@ -1595,6 +1691,7 @@ function createStorageItem(config) {
|
|
|
1595
1691
|
_hasExpiration: expiration !== undefined,
|
|
1596
1692
|
_readCacheEnabled: readCache,
|
|
1597
1693
|
_isBiometric: isBiometric,
|
|
1694
|
+
_biometricLevel: resolvedBiometricLevel,
|
|
1598
1695
|
_defaultValue: defaultValue,
|
|
1599
1696
|
...(secureAccessControl !== undefined ? {
|
|
1600
1697
|
_secureAccessControl: secureAccessControl
|
|
@@ -1724,7 +1821,7 @@ function setBatch(items, scope) {
|
|
|
1724
1821
|
const keys = secureEntries.map(({
|
|
1725
1822
|
item
|
|
1726
1823
|
}) => item.key);
|
|
1727
|
-
const oldValues =
|
|
1824
|
+
const oldValues = shouldReadPreviousEventValues(scope) ? WebStorage.getBatch(keys, scope) : [];
|
|
1728
1825
|
const groupedByAccessControl = new Map();
|
|
1729
1826
|
secureEntries.forEach(({
|
|
1730
1827
|
item,
|
|
@@ -1767,7 +1864,7 @@ function setBatch(items, scope) {
|
|
|
1767
1864
|
}
|
|
1768
1865
|
const keys = items.map(entry => entry.item.key);
|
|
1769
1866
|
const values = items.map(entry => entry.item.serialize(entry.value));
|
|
1770
|
-
const oldValues =
|
|
1867
|
+
const oldValues = shouldReadPreviousEventValues(scope) ? WebStorage.getBatch(keys, scope) : [];
|
|
1771
1868
|
WebStorage.setBatch(keys, values, scope);
|
|
1772
1869
|
keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
|
|
1773
1870
|
emitBatchChange(scope, "setBatch", "web", keys.map((key, index) => createKeyChange(scope, key, oldValues[index], values[index], "setBatch", "web")));
|
|
@@ -1789,7 +1886,7 @@ function removeBatch(items, scope) {
|
|
|
1789
1886
|
if (scope === _Storage.StorageScope.Secure) {
|
|
1790
1887
|
flushSecureWrites();
|
|
1791
1888
|
}
|
|
1792
|
-
const oldValues =
|
|
1889
|
+
const oldValues = shouldReadPreviousEventValues(scope) ? WebStorage.getBatch(keys, scope) : [];
|
|
1793
1890
|
WebStorage.removeBatch(keys, scope);
|
|
1794
1891
|
keys.forEach(key => cacheRawValue(scope, key, undefined));
|
|
1795
1892
|
emitBatchChange(scope, "removeBatch", "web", keys.map((key, index) => createKeyChange(scope, key, oldValues[index], undefined, "removeBatch", "web")));
|
|
@@ -1841,14 +1938,32 @@ function runTransaction(scope, transaction) {
|
|
|
1841
1938
|
}
|
|
1842
1939
|
const NOT_SET = Symbol();
|
|
1843
1940
|
const rollback = new Map();
|
|
1844
|
-
const rememberRollback = key => {
|
|
1941
|
+
const rememberRollback = (key, item) => {
|
|
1845
1942
|
if (rollback.has(key)) {
|
|
1846
1943
|
return;
|
|
1847
1944
|
}
|
|
1848
1945
|
if (scope === _Storage.StorageScope.Memory) {
|
|
1849
|
-
rollback.set(key,
|
|
1946
|
+
rollback.set(key, {
|
|
1947
|
+
kind: "memory",
|
|
1948
|
+
value: memoryStore.has(key) ? memoryStore.get(key) : NOT_SET
|
|
1949
|
+
});
|
|
1850
1950
|
} else {
|
|
1851
|
-
|
|
1951
|
+
const internal = item ? item : undefined;
|
|
1952
|
+
if (scope === _Storage.StorageScope.Secure && internal?._isBiometric === true) {
|
|
1953
|
+
rollback.set(key, {
|
|
1954
|
+
kind: "biometric",
|
|
1955
|
+
value: WebStorage.getSecureBiometric(key),
|
|
1956
|
+
level: internal._biometricLevel
|
|
1957
|
+
});
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1960
|
+
rollback.set(key, {
|
|
1961
|
+
kind: "raw",
|
|
1962
|
+
value: getRawValue(key, scope),
|
|
1963
|
+
...(scope === _Storage.StorageScope.Secure && internal?._secureAccessControl !== undefined ? {
|
|
1964
|
+
accessControl: internal._secureAccessControl
|
|
1965
|
+
} : {})
|
|
1966
|
+
});
|
|
1852
1967
|
}
|
|
1853
1968
|
};
|
|
1854
1969
|
const tx = {
|
|
@@ -1868,12 +1983,12 @@ function runTransaction(scope, transaction) {
|
|
|
1868
1983
|
},
|
|
1869
1984
|
setItem: (item, value) => {
|
|
1870
1985
|
(0, _internal.assertBatchScope)([item], scope);
|
|
1871
|
-
rememberRollback(item.key);
|
|
1986
|
+
rememberRollback(item.key, item);
|
|
1872
1987
|
item.set(value);
|
|
1873
1988
|
},
|
|
1874
1989
|
removeItem: item => {
|
|
1875
1990
|
(0, _internal.assertBatchScope)([item], scope);
|
|
1876
|
-
rememberRollback(item.key);
|
|
1991
|
+
rememberRollback(item.key, item);
|
|
1877
1992
|
item.delete();
|
|
1878
1993
|
}
|
|
1879
1994
|
};
|
|
@@ -1882,24 +1997,43 @@ function runTransaction(scope, transaction) {
|
|
|
1882
1997
|
} catch (error) {
|
|
1883
1998
|
const rollbackEntries = Array.from(rollback.entries()).reverse();
|
|
1884
1999
|
if (scope === _Storage.StorageScope.Memory) {
|
|
1885
|
-
rollbackEntries.forEach(([key,
|
|
1886
|
-
if (
|
|
2000
|
+
rollbackEntries.forEach(([key, record]) => {
|
|
2001
|
+
if (record.value === NOT_SET) {
|
|
1887
2002
|
memoryStore.delete(key);
|
|
1888
2003
|
} else {
|
|
1889
|
-
memoryStore.set(key,
|
|
2004
|
+
memoryStore.set(key, record.value);
|
|
1890
2005
|
}
|
|
1891
2006
|
notifyKeyListeners(memoryListeners, key);
|
|
1892
2007
|
});
|
|
1893
2008
|
} else {
|
|
1894
|
-
const
|
|
1895
|
-
const valuesToSet = [];
|
|
2009
|
+
const groupedKeysToSet = new Map();
|
|
1896
2010
|
const keysToRemove = [];
|
|
1897
|
-
rollbackEntries.forEach(([key,
|
|
1898
|
-
if (
|
|
2011
|
+
rollbackEntries.forEach(([key, record]) => {
|
|
2012
|
+
if (record.kind === "biometric") {
|
|
2013
|
+
if (record.value === undefined) {
|
|
2014
|
+
WebStorage.deleteSecureBiometric(key);
|
|
2015
|
+
} else {
|
|
2016
|
+
WebStorage.setSecureBiometricWithLevel(key, record.value, record.level);
|
|
2017
|
+
}
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
if (record.kind !== "raw") {
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
if (record.value === undefined) {
|
|
1899
2024
|
keysToRemove.push(key);
|
|
1900
2025
|
} else {
|
|
1901
|
-
|
|
1902
|
-
|
|
2026
|
+
const accessControl = record.accessControl ?? secureDefaultAccessControl;
|
|
2027
|
+
const existingGroup = groupedKeysToSet.get(accessControl);
|
|
2028
|
+
const group = existingGroup ?? {
|
|
2029
|
+
keys: [],
|
|
2030
|
+
values: []
|
|
2031
|
+
};
|
|
2032
|
+
group.keys.push(key);
|
|
2033
|
+
group.values.push(record.value);
|
|
2034
|
+
if (!existingGroup) {
|
|
2035
|
+
groupedKeysToSet.set(accessControl, group);
|
|
2036
|
+
}
|
|
1903
2037
|
}
|
|
1904
2038
|
});
|
|
1905
2039
|
if (scope === _Storage.StorageScope.Disk) {
|
|
@@ -1908,10 +2042,13 @@ function runTransaction(scope, transaction) {
|
|
|
1908
2042
|
if (scope === _Storage.StorageScope.Secure) {
|
|
1909
2043
|
flushSecureWrites();
|
|
1910
2044
|
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
2045
|
+
groupedKeysToSet.forEach((group, accessControl) => {
|
|
2046
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
2047
|
+
WebStorage.setSecureAccessControl(accessControl);
|
|
2048
|
+
}
|
|
2049
|
+
WebStorage.setBatch(group.keys, group.values, scope);
|
|
2050
|
+
group.keys.forEach((key, index) => cacheRawValue(scope, key, group.values[index]));
|
|
2051
|
+
});
|
|
1915
2052
|
if (keysToRemove.length > 0) {
|
|
1916
2053
|
WebStorage.removeBatch(keysToRemove, scope);
|
|
1917
2054
|
keysToRemove.forEach(key => cacheRawValue(scope, key, undefined));
|