react-native-nitro-storage 0.5.9 → 0.7.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 +39 -0
- package/README.md +193 -16
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +225 -76
- package/app.plugin.js +2 -0
- package/cpp/bindings/HybridStorage.cpp +29 -21
- package/cpp/bindings/HybridStorage.hpp +5 -0
- package/docs/secure-storage.md +4 -0
- package/lib/commonjs/index.js +19 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +19 -2
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/indexeddb-backend.js.map +1 -1
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/commonjs/shared.js.map +1 -1
- package/lib/commonjs/storage-core.js +306 -10
- package/lib/commonjs/storage-core.js.map +1 -1
- package/lib/commonjs/storage-events.js.map +1 -1
- package/lib/commonjs/storage-hooks.js +16 -1
- package/lib/commonjs/storage-hooks.js.map +1 -1
- package/lib/commonjs/testing.js +267 -0
- package/lib/commonjs/testing.js.map +1 -0
- package/lib/module/index.js +5 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +5 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/indexeddb-backend.js.map +1 -1
- package/lib/module/internal.js.map +1 -1
- package/lib/module/shared.js.map +1 -1
- package/lib/module/storage-core.js +307 -11
- package/lib/module/storage-core.js.map +1 -1
- package/lib/module/storage-events.js.map +1 -1
- package/lib/module/storage-hooks.js +15 -2
- package/lib/module/storage-hooks.js.map +1 -1
- package/lib/module/testing.js +188 -0
- package/lib/module/testing.js.map +1 -0
- package/lib/typescript/index.d.ts +28 -5
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +28 -5
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/indexeddb-backend.d.ts.map +1 -1
- package/lib/typescript/shared.d.ts +1 -0
- package/lib/typescript/shared.d.ts.map +1 -1
- package/lib/typescript/storage-core.d.ts +62 -3
- package/lib/typescript/storage-core.d.ts.map +1 -1
- package/lib/typescript/storage-events.d.ts +1 -1
- package/lib/typescript/storage-events.d.ts.map +1 -1
- package/lib/typescript/storage-hooks.d.ts +18 -1
- package/lib/typescript/storage-hooks.d.ts.map +1 -1
- package/lib/typescript/testing.d.ts +158 -0
- package/lib/typescript/testing.d.ts.map +1 -0
- package/package.json +11 -6
- package/src/index.ts +17 -4
- package/src/index.web.ts +17 -4
- package/src/indexeddb-backend.ts +1 -2
- package/src/internal.ts +1 -1
- package/src/shared.ts +1 -0
- package/src/storage-core.ts +463 -11
- package/src/storage-events.ts +3 -2
- package/src/storage-hooks.ts +42 -3
- package/src/testing.ts +270 -0
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { assertAccessControlLevel, assertBiometricLevel, canUseRawBatchPath, canUseSecureRawBatchPath, createKeyChange, defaultDeserialize, defaultSerialize, isUpdater, notifyAllListeners, notifyKeyListeners, now, redactSecureKeyChange, runMicrotask, typedKeys } from "./shared";
|
|
3
|
+
import { assertAccessControlLevel, assertBiometricLevel, canUseRawBatchPath, canUseSecureRawBatchPath, createKeyChange, defaultDeserialize, defaultSerialize, isKeychainLockedError, isUpdater, notifyAllListeners, notifyKeyListeners, now, redactSecureKeyChange, runMicrotask, typedKeys } from "./shared";
|
|
4
4
|
import { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
|
|
5
5
|
import { MIGRATION_VERSION_KEY, isStoredEnvelope, assertBatchScope, assertValidScope, toVersionToken, prefixKey, isNamespaced } from "./internal";
|
|
6
6
|
import { StorageEventRegistry } from "./storage-events";
|
|
7
|
+
const EMPTY_KEYS = Object.freeze([]);
|
|
7
8
|
function asInternal(item) {
|
|
8
9
|
return item;
|
|
9
10
|
}
|
|
10
11
|
export function createStorageCore(buildAdapter) {
|
|
11
12
|
const registeredMigrations = new Map();
|
|
13
|
+
const itemGroups = new Map();
|
|
14
|
+
const registeredKeyCounts = new Map();
|
|
12
15
|
const memoryStore = new Map();
|
|
13
16
|
const memoryListeners = new Map();
|
|
14
17
|
const scopedListeners = new Map([[StorageScope.Disk, new Map()], [StorageScope.Secure, new Map()]]);
|
|
@@ -355,6 +358,40 @@ export function createStorageCore(buildAdapter) {
|
|
|
355
358
|
}
|
|
356
359
|
};
|
|
357
360
|
const adapter = buildAdapter(internals);
|
|
361
|
+
function resolveKeyRef(ref) {
|
|
362
|
+
return typeof ref === "string" ? ref : ref.key;
|
|
363
|
+
}
|
|
364
|
+
function clearScopeExcept(scope, exceptRefs) {
|
|
365
|
+
measureOperation("storage:clearExcept", scope, () => {
|
|
366
|
+
assertValidScope(scope);
|
|
367
|
+
const keep = new Set(exceptRefs.map(resolveKeyRef));
|
|
368
|
+
if (scope === StorageScope.Memory) {
|
|
369
|
+
const removeKeys = Array.from(memoryStore.keys()).filter(key => !keep.has(key));
|
|
370
|
+
if (removeKeys.length === 0) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const previousValues = shouldReadPreviousEventValues(scope) ? removeKeys.map(key => getEventRawValue(scope, key)) : [];
|
|
374
|
+
removeKeys.forEach(key => memoryStore.delete(key));
|
|
375
|
+
removeKeys.forEach(key => notifyKeyListeners(memoryListeners, key));
|
|
376
|
+
emitBatchChange(scope, "clear", "memory", removeKeys.map((key, index) => createKeyChange(scope, key, previousValues[index], undefined, "clear", "memory")));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (scope === StorageScope.Disk) {
|
|
380
|
+
flushDiskWrites();
|
|
381
|
+
}
|
|
382
|
+
if (scope === StorageScope.Secure) {
|
|
383
|
+
flushSecureWrites();
|
|
384
|
+
}
|
|
385
|
+
const removeKeys = adapter.backend.getAllKeys(scope).filter(key => !keep.has(key));
|
|
386
|
+
if (removeKeys.length === 0) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const previousValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(removeKeys, scope) : [];
|
|
390
|
+
adapter.backend.removeBatch(removeKeys, scope);
|
|
391
|
+
removeKeys.forEach(key => cacheRawValue(scope, key, undefined));
|
|
392
|
+
emitBatchChange(scope, "clear", adapter.changeSource, removeKeys.map((key, index) => createKeyChange(scope, key, previousValues[index], undefined, "clear", adapter.changeSource)));
|
|
393
|
+
});
|
|
394
|
+
}
|
|
358
395
|
const storage = {
|
|
359
396
|
subscribe: (scope, listener) => {
|
|
360
397
|
assertValidScope(scope);
|
|
@@ -406,7 +443,11 @@ export function createStorageCore(buildAdapter) {
|
|
|
406
443
|
adapter.maybeCleanupScopeSubscription(StorageScope.Disk);
|
|
407
444
|
adapter.maybeCleanupScopeSubscription(StorageScope.Secure);
|
|
408
445
|
},
|
|
409
|
-
clear: scope => {
|
|
446
|
+
clear: (scope, options) => {
|
|
447
|
+
if (options?.except && options.except.length > 0) {
|
|
448
|
+
clearScopeExcept(scope, options.except);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
410
451
|
measureOperation("storage:clear", scope, () => {
|
|
411
452
|
const previousValues = shouldReadPreviousEventValues(scope) ? storage.getAll(scope) : {};
|
|
412
453
|
if (scope === StorageScope.Memory) {
|
|
@@ -435,6 +476,71 @@ export function createStorageCore(buildAdapter) {
|
|
|
435
476
|
storage.clear(StorageScope.Secure);
|
|
436
477
|
}, 3);
|
|
437
478
|
},
|
|
479
|
+
clearGroup: group => {
|
|
480
|
+
const items = itemGroups.get(group);
|
|
481
|
+
if (!items || items.size === 0) {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
measureOperation("storage:clearGroup", StorageScope.Memory, () => {
|
|
485
|
+
const byScope = new Map();
|
|
486
|
+
for (const item of items) {
|
|
487
|
+
const scoped = byScope.get(item.scope);
|
|
488
|
+
if (scoped) {
|
|
489
|
+
scoped.push(item);
|
|
490
|
+
} else {
|
|
491
|
+
byScope.set(item.scope, [item]);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
byScope.forEach((groupItems, scope) => {
|
|
495
|
+
removeBatch(groupItems, scope);
|
|
496
|
+
});
|
|
497
|
+
}, items.size);
|
|
498
|
+
},
|
|
499
|
+
getGroupItems: group => {
|
|
500
|
+
const items = itemGroups.get(group);
|
|
501
|
+
return items ? Array.from(items) : [];
|
|
502
|
+
},
|
|
503
|
+
subscribeExpired: (scope, listener) => {
|
|
504
|
+
return storage.subscribe(scope, event => {
|
|
505
|
+
if (event.type === "key") {
|
|
506
|
+
if (event.operation === "expire") {
|
|
507
|
+
listener(event);
|
|
508
|
+
}
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
event.changes.forEach(change => {
|
|
512
|
+
if (change.operation === "expire") {
|
|
513
|
+
listener(change);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
},
|
|
518
|
+
findDuplicateKeys: () => {
|
|
519
|
+
const duplicates = [];
|
|
520
|
+
registeredKeyCounts.forEach((count, registryKey) => {
|
|
521
|
+
if (count <= 1) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const separatorIndex = registryKey.indexOf(":");
|
|
525
|
+
duplicates.push({
|
|
526
|
+
scope: Number(registryKey.slice(0, separatorIndex)),
|
|
527
|
+
key: registryKey.slice(separatorIndex + 1),
|
|
528
|
+
count
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
return duplicates;
|
|
532
|
+
},
|
|
533
|
+
getRegisteredKeys: () => {
|
|
534
|
+
const result = [];
|
|
535
|
+
registeredKeyCounts.forEach((_count, registryKey) => {
|
|
536
|
+
const separatorIndex = registryKey.indexOf(":");
|
|
537
|
+
result.push({
|
|
538
|
+
scope: Number(registryKey.slice(0, separatorIndex)),
|
|
539
|
+
key: registryKey.slice(separatorIndex + 1)
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
return result;
|
|
543
|
+
},
|
|
438
544
|
clearNamespace: (namespace, scope) => {
|
|
439
545
|
measureOperation("storage:clearNamespace", scope, () => {
|
|
440
546
|
assertValidScope(scope);
|
|
@@ -734,6 +840,10 @@ export function createStorageCore(buildAdapter) {
|
|
|
734
840
|
const coalesceSecureWrites = config.scope === StorageScope.Secure && config.coalesceSecureWrites === true && !isBiometric;
|
|
735
841
|
const defaultValue = config.defaultValue;
|
|
736
842
|
const nonMemoryScope = config.scope === StorageScope.Disk ? StorageScope.Disk : config.scope === StorageScope.Secure ? StorageScope.Secure : null;
|
|
843
|
+
const renameFromKeys = config.renameFrom === undefined ? EMPTY_KEYS : typeof config.renameFrom === "string" ? [config.renameFrom] : config.renameFrom;
|
|
844
|
+
const fallbackToCacheOnReadError = config.fallbackToCacheOnReadError === true;
|
|
845
|
+
const onReadError = config.onReadError;
|
|
846
|
+
let renamesMigrated = isMemory || renameFromKeys.length === 0;
|
|
737
847
|
if (expiration && expiration.ttlMs <= 0) {
|
|
738
848
|
throw new Error("expiration.ttlMs must be greater than 0.");
|
|
739
849
|
}
|
|
@@ -775,15 +885,18 @@ export function createStorageCore(buildAdapter) {
|
|
|
775
885
|
if (memoryExpiration) {
|
|
776
886
|
const expiresAt = memoryExpiration.get(storageKey);
|
|
777
887
|
if (expiresAt !== undefined && expiresAt <= Date.now()) {
|
|
888
|
+
const expiredRaw = memoryStore.get(storageKey);
|
|
778
889
|
memoryExpiration.delete(storageKey);
|
|
779
890
|
memoryStore.delete(storageKey);
|
|
780
891
|
notifyKeyListeners(memoryListeners, storageKey);
|
|
892
|
+
emitKeyChange(config.scope, storageKey, typeof expiredRaw === "string" ? expiredRaw : undefined, undefined, "expire", "memory");
|
|
781
893
|
onExpired?.(storageKey);
|
|
782
894
|
return undefined;
|
|
783
895
|
}
|
|
784
896
|
}
|
|
785
897
|
return memoryStore.get(storageKey);
|
|
786
898
|
}
|
|
899
|
+
migrateRenamesIfNeeded();
|
|
787
900
|
if (nonMemoryScope === StorageScope.Disk) {
|
|
788
901
|
const pending = pendingDiskWrites.get(storageKey);
|
|
789
902
|
if (pending !== undefined) {
|
|
@@ -804,12 +917,27 @@ export function createStorageCore(buildAdapter) {
|
|
|
804
917
|
}
|
|
805
918
|
}
|
|
806
919
|
if (isBiometric) {
|
|
807
|
-
return adapter.backend.getSecureBiometric(storageKey);
|
|
920
|
+
return readBackendRaw(() => adapter.backend.getSecureBiometric(storageKey));
|
|
808
921
|
}
|
|
809
|
-
const raw = adapter.backend.get(storageKey, config.scope);
|
|
922
|
+
const raw = readBackendRaw(() => adapter.backend.get(storageKey, config.scope));
|
|
810
923
|
cacheRawValue(nonMemoryScope, storageKey, raw);
|
|
811
924
|
return raw;
|
|
812
925
|
};
|
|
926
|
+
const readBackendRaw = read => {
|
|
927
|
+
try {
|
|
928
|
+
return read();
|
|
929
|
+
} catch (error) {
|
|
930
|
+
onReadError?.(error);
|
|
931
|
+
if (fallbackToCacheOnReadError) {
|
|
932
|
+
const cached = getScopeRawCache(nonMemoryScope).get(storageKey);
|
|
933
|
+
if (cached !== undefined) {
|
|
934
|
+
return cached;
|
|
935
|
+
}
|
|
936
|
+
return typeof lastRaw === "string" ? lastRaw : undefined;
|
|
937
|
+
}
|
|
938
|
+
throw error;
|
|
939
|
+
}
|
|
940
|
+
};
|
|
813
941
|
const writeStoredRaw = rawValue => {
|
|
814
942
|
const oldValue = undefined;
|
|
815
943
|
if (isBiometric) {
|
|
@@ -840,32 +968,73 @@ export function createStorageCore(buildAdapter) {
|
|
|
840
968
|
adapter.backend.set(storageKey, rawValue, config.scope);
|
|
841
969
|
emitKeyChange(config.scope, storageKey, oldValue, rawValue, "set", adapter.changeSource);
|
|
842
970
|
};
|
|
843
|
-
const
|
|
971
|
+
const migrateRenamesIfNeeded = () => {
|
|
972
|
+
if (renamesMigrated) {
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
try {
|
|
976
|
+
const hasCurrent = isBiometric ? adapter.backend.hasSecureBiometric(storageKey) : adapter.backend.has(storageKey, config.scope);
|
|
977
|
+
if (hasCurrent) {
|
|
978
|
+
for (const legacyKey of renameFromKeys) {
|
|
979
|
+
if (isBiometric) {
|
|
980
|
+
if (adapter.backend.hasSecureBiometric(legacyKey)) {
|
|
981
|
+
adapter.backend.deleteSecureBiometric(legacyKey);
|
|
982
|
+
}
|
|
983
|
+
} else if (adapter.backend.has(legacyKey, config.scope)) {
|
|
984
|
+
adapter.backend.remove(legacyKey, config.scope);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
renamesMigrated = true;
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
for (const legacyKey of renameFromKeys) {
|
|
991
|
+
const legacyRaw = isBiometric ? adapter.backend.getSecureBiometric(legacyKey) : adapter.backend.get(legacyKey, config.scope);
|
|
992
|
+
if (legacyRaw === undefined) {
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
writeStoredRaw(legacyRaw);
|
|
996
|
+
if (isBiometric) {
|
|
997
|
+
adapter.backend.deleteSecureBiometric(legacyKey);
|
|
998
|
+
} else {
|
|
999
|
+
adapter.backend.remove(legacyKey, config.scope);
|
|
1000
|
+
}
|
|
1001
|
+
break;
|
|
1002
|
+
}
|
|
1003
|
+
renamesMigrated = true;
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
if (isKeychainLockedError(error)) {
|
|
1006
|
+
onReadError?.(error);
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
throw error;
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
const removeStoredRaw = (operation = "remove") => {
|
|
844
1013
|
const oldValue = getEventRawValue(config.scope, storageKey);
|
|
845
1014
|
if (isBiometric) {
|
|
846
1015
|
adapter.backend.deleteSecureBiometric(storageKey);
|
|
847
|
-
emitKeyChange(config.scope, storageKey, oldValue, undefined,
|
|
1016
|
+
emitKeyChange(config.scope, storageKey, oldValue, undefined, operation, adapter.changeSource);
|
|
848
1017
|
return;
|
|
849
1018
|
}
|
|
850
1019
|
cacheRawValue(nonMemoryScope, storageKey, undefined);
|
|
851
1020
|
if (nonMemoryScope === StorageScope.Disk) {
|
|
852
1021
|
if (coalesceDiskWrites || diskWritesAsync) {
|
|
853
1022
|
scheduleDiskWrite(storageKey, undefined);
|
|
854
|
-
emitKeyChange(config.scope, storageKey, oldValue, undefined,
|
|
1023
|
+
emitKeyChange(config.scope, storageKey, oldValue, undefined, operation, adapter.changeSource);
|
|
855
1024
|
return;
|
|
856
1025
|
}
|
|
857
1026
|
clearPendingDiskWrite(storageKey);
|
|
858
1027
|
}
|
|
859
1028
|
if (coalesceSecureWrites) {
|
|
860
1029
|
scheduleSecureWrite(storageKey, undefined, secureAccessControl ?? secureDefaultAccessControl);
|
|
861
|
-
emitKeyChange(config.scope, storageKey, oldValue, undefined,
|
|
1030
|
+
emitKeyChange(config.scope, storageKey, oldValue, undefined, operation, adapter.changeSource);
|
|
862
1031
|
return;
|
|
863
1032
|
}
|
|
864
1033
|
if (nonMemoryScope === StorageScope.Secure) {
|
|
865
1034
|
clearPendingSecureWrite(storageKey);
|
|
866
1035
|
}
|
|
867
1036
|
adapter.backend.remove(storageKey, config.scope);
|
|
868
|
-
emitKeyChange(config.scope, storageKey, oldValue, undefined,
|
|
1037
|
+
emitKeyChange(config.scope, storageKey, oldValue, undefined, operation, adapter.changeSource);
|
|
869
1038
|
};
|
|
870
1039
|
const writeValueWithoutValidation = value => {
|
|
871
1040
|
if (isMemory) {
|
|
@@ -919,7 +1088,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
919
1088
|
if (lastExpiresAt > Date.now()) {
|
|
920
1089
|
return lastValue;
|
|
921
1090
|
}
|
|
922
|
-
removeStoredRaw();
|
|
1091
|
+
removeStoredRaw("expire");
|
|
923
1092
|
invalidateParsedCache();
|
|
924
1093
|
onExpired?.(storageKey);
|
|
925
1094
|
lastValue = ensureValidatedValue(defaultValue, false);
|
|
@@ -955,7 +1124,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
955
1124
|
if (isStoredEnvelope(parsed)) {
|
|
956
1125
|
envelopeExpiresAt = parsed.expiresAt;
|
|
957
1126
|
if (parsed.expiresAt <= Date.now()) {
|
|
958
|
-
removeStoredRaw();
|
|
1127
|
+
removeStoredRaw("expire");
|
|
959
1128
|
invalidateParsedCache();
|
|
960
1129
|
onExpired?.(storageKey);
|
|
961
1130
|
lastValue = ensureValidatedValue(defaultValue, false);
|
|
@@ -1019,6 +1188,27 @@ export function createStorageCore(buildAdapter) {
|
|
|
1019
1188
|
removeStoredRaw();
|
|
1020
1189
|
});
|
|
1021
1190
|
};
|
|
1191
|
+
const merge = partial => {
|
|
1192
|
+
set(prev => {
|
|
1193
|
+
if (prev === null || typeof prev !== "object") {
|
|
1194
|
+
return partial;
|
|
1195
|
+
}
|
|
1196
|
+
return {
|
|
1197
|
+
...prev,
|
|
1198
|
+
...partial
|
|
1199
|
+
};
|
|
1200
|
+
});
|
|
1201
|
+
};
|
|
1202
|
+
const reset = () => {
|
|
1203
|
+
deleteItem();
|
|
1204
|
+
};
|
|
1205
|
+
const setOrDelete = value => {
|
|
1206
|
+
if (value === null || value === undefined) {
|
|
1207
|
+
deleteItem();
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
set(value);
|
|
1211
|
+
};
|
|
1022
1212
|
const hasItem = () => measureOperation("item:has", config.scope, () => {
|
|
1023
1213
|
if (isMemory) return memoryStore.has(storageKey);
|
|
1024
1214
|
if (isBiometric) return adapter.backend.hasSecureBiometric(storageKey);
|
|
@@ -1071,6 +1261,9 @@ export function createStorageCore(buildAdapter) {
|
|
|
1071
1261
|
getWithVersion,
|
|
1072
1262
|
set,
|
|
1073
1263
|
setIfVersion,
|
|
1264
|
+
merge,
|
|
1265
|
+
reset,
|
|
1266
|
+
setOrDelete,
|
|
1074
1267
|
delete: deleteItem,
|
|
1075
1268
|
has: hasItem,
|
|
1076
1269
|
subscribe,
|
|
@@ -1093,9 +1286,22 @@ export function createStorageCore(buildAdapter) {
|
|
|
1093
1286
|
...(secureAccessControl !== undefined ? {
|
|
1094
1287
|
_secureAccessControl: secureAccessControl
|
|
1095
1288
|
} : {}),
|
|
1289
|
+
...(config.group !== undefined ? {
|
|
1290
|
+
_group: config.group
|
|
1291
|
+
} : {}),
|
|
1096
1292
|
scope: config.scope,
|
|
1097
1293
|
key: storageKey
|
|
1098
1294
|
};
|
|
1295
|
+
if (config.group !== undefined) {
|
|
1296
|
+
let groupSet = itemGroups.get(config.group);
|
|
1297
|
+
if (!groupSet) {
|
|
1298
|
+
groupSet = new Set();
|
|
1299
|
+
itemGroups.set(config.group, groupSet);
|
|
1300
|
+
}
|
|
1301
|
+
groupSet.add(storageItem);
|
|
1302
|
+
}
|
|
1303
|
+
const registryKey = `${config.scope}:${storageKey}`;
|
|
1304
|
+
registeredKeyCounts.set(registryKey, (registeredKeyCounts.get(registryKey) ?? 0) + 1);
|
|
1099
1305
|
return storageItem;
|
|
1100
1306
|
}
|
|
1101
1307
|
function getBatch(items, scope) {
|
|
@@ -1480,14 +1686,104 @@ export function createStorageCore(buildAdapter) {
|
|
|
1480
1686
|
} : {}),
|
|
1481
1687
|
...(expirationConfig !== undefined ? {
|
|
1482
1688
|
expiration: expirationConfig
|
|
1689
|
+
} : {}),
|
|
1690
|
+
...(itemConfig.renameFrom !== undefined ? {
|
|
1691
|
+
renameFrom: itemConfig.renameFrom
|
|
1692
|
+
} : {}),
|
|
1693
|
+
...(options?.group !== undefined ? {
|
|
1694
|
+
group: options.group
|
|
1695
|
+
} : {}),
|
|
1696
|
+
...(options?.fallbackToCacheOnReadError !== undefined ? {
|
|
1697
|
+
fallbackToCacheOnReadError: options.fallbackToCacheOnReadError
|
|
1483
1698
|
} : {})
|
|
1484
1699
|
});
|
|
1485
1700
|
}
|
|
1486
1701
|
return result;
|
|
1487
1702
|
}
|
|
1703
|
+
function memoryItem(config) {
|
|
1704
|
+
return createStorageItem({
|
|
1705
|
+
...config,
|
|
1706
|
+
scope: StorageScope.Memory
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
function diskItem(config) {
|
|
1710
|
+
return createStorageItem({
|
|
1711
|
+
...config,
|
|
1712
|
+
scope: StorageScope.Disk
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
function secureItem(config) {
|
|
1716
|
+
return createStorageItem({
|
|
1717
|
+
...config,
|
|
1718
|
+
scope: StorageScope.Secure
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
function createSetItem(config) {
|
|
1722
|
+
const {
|
|
1723
|
+
defaultValue,
|
|
1724
|
+
...rest
|
|
1725
|
+
} = config;
|
|
1726
|
+
const initial = {};
|
|
1727
|
+
if (defaultValue) {
|
|
1728
|
+
for (const id of defaultValue) {
|
|
1729
|
+
initial[id] = true;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
const item = createStorageItem({
|
|
1733
|
+
...rest,
|
|
1734
|
+
defaultValue: initial
|
|
1735
|
+
});
|
|
1736
|
+
const has = id => item.get()[id] === true;
|
|
1737
|
+
const add = id => {
|
|
1738
|
+
if (item.get()[id] === true) {
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
item.merge({
|
|
1742
|
+
[id]: true
|
|
1743
|
+
});
|
|
1744
|
+
};
|
|
1745
|
+
const deleteId = id => {
|
|
1746
|
+
const current = item.get();
|
|
1747
|
+
if (current[id] !== true) {
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
const next = {
|
|
1751
|
+
...current
|
|
1752
|
+
};
|
|
1753
|
+
delete next[id];
|
|
1754
|
+
item.set(next);
|
|
1755
|
+
};
|
|
1756
|
+
const toggle = id => {
|
|
1757
|
+
if (has(id)) {
|
|
1758
|
+
deleteId(id);
|
|
1759
|
+
return false;
|
|
1760
|
+
}
|
|
1761
|
+
add(id);
|
|
1762
|
+
return true;
|
|
1763
|
+
};
|
|
1764
|
+
return {
|
|
1765
|
+
get: item.get,
|
|
1766
|
+
has,
|
|
1767
|
+
add,
|
|
1768
|
+
delete: deleteId,
|
|
1769
|
+
toggle,
|
|
1770
|
+
values: () => Object.keys(item.get()),
|
|
1771
|
+
size: () => Object.keys(item.get()).length,
|
|
1772
|
+
clear: () => item.set({}),
|
|
1773
|
+
reset: item.reset,
|
|
1774
|
+
subscribe: item.subscribe,
|
|
1775
|
+
scope: item.scope,
|
|
1776
|
+
key: item.key,
|
|
1777
|
+
item
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1488
1780
|
return {
|
|
1489
1781
|
storage,
|
|
1490
1782
|
createStorageItem,
|
|
1783
|
+
memoryItem,
|
|
1784
|
+
diskItem,
|
|
1785
|
+
secureItem,
|
|
1786
|
+
createSetItem,
|
|
1491
1787
|
getBatch,
|
|
1492
1788
|
setBatch,
|
|
1493
1789
|
removeBatch,
|