react-native-nitro-storage 0.4.4 → 0.4.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/README.md +107 -7
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +61 -10
- package/ios/IOSStorageAdapterCpp.mm +44 -14
- package/lib/commonjs/index.js +221 -5
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +444 -202
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/indexeddb-backend.js +129 -7
- package/lib/commonjs/indexeddb-backend.js.map +1 -1
- package/lib/commonjs/storage-runtime.js +41 -0
- package/lib/commonjs/storage-runtime.js.map +1 -0
- package/lib/commonjs/web-storage-backend.js +90 -0
- package/lib/commonjs/web-storage-backend.js.map +1 -0
- package/lib/module/index.js +213 -5
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +436 -202
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/indexeddb-backend.js +129 -7
- package/lib/module/indexeddb-backend.js.map +1 -1
- package/lib/module/storage-runtime.js +36 -0
- package/lib/module/storage-runtime.js.map +1 -0
- package/lib/module/web-storage-backend.js +86 -0
- package/lib/module/web-storage-backend.js.map +1 -0
- package/lib/typescript/index.d.ts +11 -7
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +12 -8
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/indexeddb-backend.d.ts +6 -2
- package/lib/typescript/indexeddb-backend.d.ts.map +1 -1
- package/lib/typescript/storage-runtime.d.ts +16 -0
- package/lib/typescript/storage-runtime.d.ts.map +1 -0
- package/lib/typescript/web-storage-backend.d.ts +30 -0
- package/lib/typescript/web-storage-backend.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +264 -20
- package/src/index.web.ts +597 -245
- package/src/indexeddb-backend.ts +147 -6
- package/src/storage-runtime.ts +94 -0
- package/src/web-storage-backend.ts +129 -0
package/src/index.ts
CHANGED
|
@@ -14,10 +14,30 @@ import {
|
|
|
14
14
|
prefixKey,
|
|
15
15
|
isNamespaced,
|
|
16
16
|
} from "./internal";
|
|
17
|
+
import type {
|
|
18
|
+
WebDiskStorageBackend,
|
|
19
|
+
WebSecureStorageBackend,
|
|
20
|
+
} from "./web-storage-backend";
|
|
21
|
+
import {
|
|
22
|
+
getStorageErrorCode,
|
|
23
|
+
isLockedStorageErrorCode,
|
|
24
|
+
type StorageCapabilities,
|
|
25
|
+
type StorageErrorCode,
|
|
26
|
+
} from "./storage-runtime";
|
|
17
27
|
|
|
18
28
|
export { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
|
|
19
29
|
export type { Storage } from "./Storage.nitro";
|
|
20
30
|
export { migrateFromMMKV } from "./migration";
|
|
31
|
+
export {
|
|
32
|
+
getStorageErrorCode,
|
|
33
|
+
type StorageCapabilities,
|
|
34
|
+
type StorageErrorCode,
|
|
35
|
+
} from "./storage-runtime";
|
|
36
|
+
export type {
|
|
37
|
+
WebStorageBackend,
|
|
38
|
+
WebStorageChangeEvent,
|
|
39
|
+
WebStorageScope,
|
|
40
|
+
} from "./web-storage-backend";
|
|
21
41
|
|
|
22
42
|
export type Validator<T> = (value: unknown) => value is T;
|
|
23
43
|
export type ExpirationConfig = {
|
|
@@ -41,14 +61,6 @@ export type StorageMetricSummary = {
|
|
|
41
61
|
avgDurationMs: number;
|
|
42
62
|
maxDurationMs: number;
|
|
43
63
|
};
|
|
44
|
-
export type WebSecureStorageBackend = {
|
|
45
|
-
getItem: (key: string) => string | null;
|
|
46
|
-
setItem: (key: string, value: string) => void;
|
|
47
|
-
removeItem: (key: string) => void;
|
|
48
|
-
clear: () => void;
|
|
49
|
-
getAllKeys: () => string[];
|
|
50
|
-
};
|
|
51
|
-
|
|
52
64
|
export type MigrationContext = {
|
|
53
65
|
scope: StorageScope;
|
|
54
66
|
getRaw: (key: string) => string | undefined;
|
|
@@ -95,6 +107,10 @@ function typedKeys<K extends string, V>(record: Record<K, V>): K[] {
|
|
|
95
107
|
return Object.keys(record) as K[];
|
|
96
108
|
}
|
|
97
109
|
type NonMemoryScope = StorageScope.Disk | StorageScope.Secure;
|
|
110
|
+
type PendingDiskWrite = {
|
|
111
|
+
key: string;
|
|
112
|
+
value: string | undefined;
|
|
113
|
+
};
|
|
98
114
|
type PendingSecureWrite = {
|
|
99
115
|
key: string;
|
|
100
116
|
value: string | undefined;
|
|
@@ -108,6 +124,10 @@ const runMicrotask =
|
|
|
108
124
|
: (task: () => void) => {
|
|
109
125
|
Promise.resolve().then(task);
|
|
110
126
|
};
|
|
127
|
+
const now =
|
|
128
|
+
typeof performance !== "undefined" && typeof performance.now === "function"
|
|
129
|
+
? () => performance.now()
|
|
130
|
+
: () => Date.now();
|
|
111
131
|
|
|
112
132
|
let _storageModule: Storage | null = null;
|
|
113
133
|
|
|
@@ -131,6 +151,9 @@ const scopedRawCache = new Map<NonMemoryScope, Map<string, string | undefined>>(
|
|
|
131
151
|
[StorageScope.Secure, new Map()],
|
|
132
152
|
],
|
|
133
153
|
);
|
|
154
|
+
const pendingDiskWrites = new Map<string, PendingDiskWrite>();
|
|
155
|
+
let diskFlushScheduled = false;
|
|
156
|
+
let diskWritesAsync = false;
|
|
134
157
|
const pendingSecureWrites = new Map<string, PendingSecureWrite>();
|
|
135
158
|
let secureFlushScheduled = false;
|
|
136
159
|
let secureDefaultAccessControl: AccessControl = AccessControl.WhenUnlocked;
|
|
@@ -176,11 +199,11 @@ function measureOperation<T>(
|
|
|
176
199
|
if (!metricsObserver) {
|
|
177
200
|
return fn();
|
|
178
201
|
}
|
|
179
|
-
const start =
|
|
202
|
+
const start = now();
|
|
180
203
|
try {
|
|
181
204
|
return fn();
|
|
182
205
|
} finally {
|
|
183
|
-
recordMetric(operation, scope,
|
|
206
|
+
recordMetric(operation, scope, now() - start, keysCount);
|
|
184
207
|
}
|
|
185
208
|
}
|
|
186
209
|
|
|
@@ -262,14 +285,59 @@ function readPendingSecureWrite(key: string): string | undefined {
|
|
|
262
285
|
return pendingSecureWrites.get(key)?.value;
|
|
263
286
|
}
|
|
264
287
|
|
|
288
|
+
function readPendingDiskWrite(key: string): string | undefined {
|
|
289
|
+
return pendingDiskWrites.get(key)?.value;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function hasPendingDiskWrite(key: string): boolean {
|
|
293
|
+
return pendingDiskWrites.has(key);
|
|
294
|
+
}
|
|
295
|
+
|
|
265
296
|
function hasPendingSecureWrite(key: string): boolean {
|
|
266
297
|
return pendingSecureWrites.has(key);
|
|
267
298
|
}
|
|
268
299
|
|
|
300
|
+
function clearPendingDiskWrite(key: string): void {
|
|
301
|
+
pendingDiskWrites.delete(key);
|
|
302
|
+
}
|
|
303
|
+
|
|
269
304
|
function clearPendingSecureWrite(key: string): void {
|
|
270
305
|
pendingSecureWrites.delete(key);
|
|
271
306
|
}
|
|
272
307
|
|
|
308
|
+
function flushDiskWrites(): void {
|
|
309
|
+
diskFlushScheduled = false;
|
|
310
|
+
|
|
311
|
+
if (pendingDiskWrites.size === 0) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const writes = Array.from(pendingDiskWrites.values());
|
|
316
|
+
pendingDiskWrites.clear();
|
|
317
|
+
|
|
318
|
+
const keysToSet: string[] = [];
|
|
319
|
+
const valuesToSet: string[] = [];
|
|
320
|
+
const keysToRemove: string[] = [];
|
|
321
|
+
|
|
322
|
+
writes.forEach(({ key, value }) => {
|
|
323
|
+
if (value === undefined) {
|
|
324
|
+
keysToRemove.push(key);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
keysToSet.push(key);
|
|
329
|
+
valuesToSet.push(value);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const storageModule = getStorageModule();
|
|
333
|
+
if (keysToSet.length > 0) {
|
|
334
|
+
storageModule.setBatch(keysToSet, valuesToSet, StorageScope.Disk);
|
|
335
|
+
}
|
|
336
|
+
if (keysToRemove.length > 0) {
|
|
337
|
+
storageModule.removeBatch(keysToRemove, StorageScope.Disk);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
273
341
|
function flushSecureWrites(): void {
|
|
274
342
|
secureFlushScheduled = false;
|
|
275
343
|
|
|
@@ -311,6 +379,15 @@ function flushSecureWrites(): void {
|
|
|
311
379
|
}
|
|
312
380
|
}
|
|
313
381
|
|
|
382
|
+
function scheduleDiskWrite(key: string, value: string | undefined): void {
|
|
383
|
+
pendingDiskWrites.set(key, { key, value });
|
|
384
|
+
if (diskFlushScheduled) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
diskFlushScheduled = true;
|
|
388
|
+
runMicrotask(flushDiskWrites);
|
|
389
|
+
}
|
|
390
|
+
|
|
314
391
|
function scheduleSecureWrite(
|
|
315
392
|
key: string,
|
|
316
393
|
value: string | undefined,
|
|
@@ -334,6 +411,14 @@ function ensureNativeScopeSubscription(scope: NonMemoryScope): void {
|
|
|
334
411
|
}
|
|
335
412
|
|
|
336
413
|
const unsubscribe = getStorageModule().addOnChange(scope, (key, value) => {
|
|
414
|
+
if (scope === StorageScope.Disk) {
|
|
415
|
+
if (key === "") {
|
|
416
|
+
pendingDiskWrites.clear();
|
|
417
|
+
} else {
|
|
418
|
+
clearPendingDiskWrite(key);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
337
422
|
if (scope === StorageScope.Secure) {
|
|
338
423
|
if (key === "") {
|
|
339
424
|
pendingSecureWrites.clear();
|
|
@@ -376,6 +461,10 @@ function getRawValue(key: string, scope: StorageScope): string | undefined {
|
|
|
376
461
|
return typeof value === "string" ? value : undefined;
|
|
377
462
|
}
|
|
378
463
|
|
|
464
|
+
if (scope === StorageScope.Disk && hasPendingDiskWrite(key)) {
|
|
465
|
+
return readPendingDiskWrite(key);
|
|
466
|
+
}
|
|
467
|
+
|
|
379
468
|
if (scope === StorageScope.Secure && hasPendingSecureWrite(key)) {
|
|
380
469
|
return readPendingSecureWrite(key);
|
|
381
470
|
}
|
|
@@ -391,6 +480,17 @@ function setRawValue(key: string, value: string, scope: StorageScope): void {
|
|
|
391
480
|
return;
|
|
392
481
|
}
|
|
393
482
|
|
|
483
|
+
if (scope === StorageScope.Disk) {
|
|
484
|
+
cacheRawValue(scope, key, value);
|
|
485
|
+
if (diskWritesAsync) {
|
|
486
|
+
scheduleDiskWrite(key, value);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
flushDiskWrites();
|
|
491
|
+
clearPendingDiskWrite(key);
|
|
492
|
+
}
|
|
493
|
+
|
|
394
494
|
if (scope === StorageScope.Secure) {
|
|
395
495
|
flushSecureWrites();
|
|
396
496
|
clearPendingSecureWrite(key);
|
|
@@ -409,6 +509,17 @@ function removeRawValue(key: string, scope: StorageScope): void {
|
|
|
409
509
|
return;
|
|
410
510
|
}
|
|
411
511
|
|
|
512
|
+
if (scope === StorageScope.Disk) {
|
|
513
|
+
cacheRawValue(scope, key, undefined);
|
|
514
|
+
if (diskWritesAsync) {
|
|
515
|
+
scheduleDiskWrite(key, undefined);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
flushDiskWrites();
|
|
520
|
+
clearPendingDiskWrite(key);
|
|
521
|
+
}
|
|
522
|
+
|
|
412
523
|
if (scope === StorageScope.Secure) {
|
|
413
524
|
flushSecureWrites();
|
|
414
525
|
clearPendingSecureWrite(key);
|
|
@@ -441,6 +552,11 @@ export const storage = {
|
|
|
441
552
|
return;
|
|
442
553
|
}
|
|
443
554
|
|
|
555
|
+
if (scope === StorageScope.Disk) {
|
|
556
|
+
flushDiskWrites();
|
|
557
|
+
pendingDiskWrites.clear();
|
|
558
|
+
}
|
|
559
|
+
|
|
444
560
|
if (scope === StorageScope.Secure) {
|
|
445
561
|
flushSecureWrites();
|
|
446
562
|
pendingSecureWrites.clear();
|
|
@@ -476,6 +592,9 @@ export const storage = {
|
|
|
476
592
|
}
|
|
477
593
|
|
|
478
594
|
const keyPrefix = prefixKey(namespace, "");
|
|
595
|
+
if (scope === StorageScope.Disk) {
|
|
596
|
+
flushDiskWrites();
|
|
597
|
+
}
|
|
479
598
|
if (scope === StorageScope.Secure) {
|
|
480
599
|
flushSecureWrites();
|
|
481
600
|
}
|
|
@@ -500,6 +619,12 @@ export const storage = {
|
|
|
500
619
|
if (scope === StorageScope.Memory) {
|
|
501
620
|
return memoryStore.has(key);
|
|
502
621
|
}
|
|
622
|
+
if (scope === StorageScope.Disk) {
|
|
623
|
+
flushDiskWrites();
|
|
624
|
+
}
|
|
625
|
+
if (scope === StorageScope.Secure) {
|
|
626
|
+
flushSecureWrites();
|
|
627
|
+
}
|
|
503
628
|
return getStorageModule().has(key, scope);
|
|
504
629
|
});
|
|
505
630
|
},
|
|
@@ -509,6 +634,12 @@ export const storage = {
|
|
|
509
634
|
if (scope === StorageScope.Memory) {
|
|
510
635
|
return Array.from(memoryStore.keys());
|
|
511
636
|
}
|
|
637
|
+
if (scope === StorageScope.Disk) {
|
|
638
|
+
flushDiskWrites();
|
|
639
|
+
}
|
|
640
|
+
if (scope === StorageScope.Secure) {
|
|
641
|
+
flushSecureWrites();
|
|
642
|
+
}
|
|
512
643
|
return getStorageModule().getAllKeys(scope);
|
|
513
644
|
});
|
|
514
645
|
},
|
|
@@ -520,6 +651,12 @@ export const storage = {
|
|
|
520
651
|
key.startsWith(prefix),
|
|
521
652
|
);
|
|
522
653
|
}
|
|
654
|
+
if (scope === StorageScope.Disk) {
|
|
655
|
+
flushDiskWrites();
|
|
656
|
+
}
|
|
657
|
+
if (scope === StorageScope.Secure) {
|
|
658
|
+
flushSecureWrites();
|
|
659
|
+
}
|
|
523
660
|
return getStorageModule().getKeysByPrefix(prefix, scope);
|
|
524
661
|
});
|
|
525
662
|
},
|
|
@@ -544,6 +681,12 @@ export const storage = {
|
|
|
544
681
|
return result;
|
|
545
682
|
}
|
|
546
683
|
|
|
684
|
+
if (scope === StorageScope.Disk) {
|
|
685
|
+
flushDiskWrites();
|
|
686
|
+
}
|
|
687
|
+
if (scope === StorageScope.Secure) {
|
|
688
|
+
flushSecureWrites();
|
|
689
|
+
}
|
|
547
690
|
const values = getStorageModule().getBatch(keys, scope);
|
|
548
691
|
keys.forEach((key, idx) => {
|
|
549
692
|
const value = decodeNativeBatchValue(values[idx]);
|
|
@@ -564,6 +707,12 @@ export const storage = {
|
|
|
564
707
|
});
|
|
565
708
|
return result;
|
|
566
709
|
}
|
|
710
|
+
if (scope === StorageScope.Disk) {
|
|
711
|
+
flushDiskWrites();
|
|
712
|
+
}
|
|
713
|
+
if (scope === StorageScope.Secure) {
|
|
714
|
+
flushSecureWrites();
|
|
715
|
+
}
|
|
567
716
|
const keys = getStorageModule().getAllKeys(scope);
|
|
568
717
|
if (keys.length === 0) return result;
|
|
569
718
|
const values = getStorageModule().getBatch(keys, scope);
|
|
@@ -580,6 +729,12 @@ export const storage = {
|
|
|
580
729
|
if (scope === StorageScope.Memory) {
|
|
581
730
|
return memoryStore.size;
|
|
582
731
|
}
|
|
732
|
+
if (scope === StorageScope.Disk) {
|
|
733
|
+
flushDiskWrites();
|
|
734
|
+
}
|
|
735
|
+
if (scope === StorageScope.Secure) {
|
|
736
|
+
flushSecureWrites();
|
|
737
|
+
}
|
|
583
738
|
return getStorageModule().size(scope);
|
|
584
739
|
});
|
|
585
740
|
},
|
|
@@ -598,6 +753,19 @@ export const storage = {
|
|
|
598
753
|
},
|
|
599
754
|
);
|
|
600
755
|
},
|
|
756
|
+
setDiskWritesAsync: (enabled: boolean) => {
|
|
757
|
+
measureOperation("storage:setDiskWritesAsync", StorageScope.Disk, () => {
|
|
758
|
+
diskWritesAsync = enabled;
|
|
759
|
+
if (!enabled) {
|
|
760
|
+
flushDiskWrites();
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
},
|
|
764
|
+
flushDiskWrites: () => {
|
|
765
|
+
measureOperation("storage:flushDiskWrites", StorageScope.Disk, () => {
|
|
766
|
+
flushDiskWrites();
|
|
767
|
+
});
|
|
768
|
+
},
|
|
601
769
|
flushSecureWrites: () => {
|
|
602
770
|
measureOperation("storage:flushSecureWrites", StorageScope.Secure, () => {
|
|
603
771
|
flushSecureWrites();
|
|
@@ -631,6 +799,18 @@ export const storage = {
|
|
|
631
799
|
resetMetrics: () => {
|
|
632
800
|
metricsCounters.clear();
|
|
633
801
|
},
|
|
802
|
+
getCapabilities: (): StorageCapabilities => ({
|
|
803
|
+
platform: "native",
|
|
804
|
+
backend: {
|
|
805
|
+
disk: "platform-preferences",
|
|
806
|
+
secure: "platform-secure-storage",
|
|
807
|
+
},
|
|
808
|
+
writeBuffering: {
|
|
809
|
+
disk: true,
|
|
810
|
+
secure: true,
|
|
811
|
+
},
|
|
812
|
+
errorClassification: true,
|
|
813
|
+
}),
|
|
634
814
|
getString: (key: string, scope: StorageScope): string | undefined => {
|
|
635
815
|
return measureOperation("storage:getString", scope, () => {
|
|
636
816
|
return getRawValue(key, scope);
|
|
@@ -689,6 +869,20 @@ export function getWebSecureStorageBackend():
|
|
|
689
869
|
return undefined;
|
|
690
870
|
}
|
|
691
871
|
|
|
872
|
+
export function setWebDiskStorageBackend(
|
|
873
|
+
_backend?: WebDiskStorageBackend,
|
|
874
|
+
): void {
|
|
875
|
+
// Native platforms do not use web disk backends.
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
export function getWebDiskStorageBackend(): WebDiskStorageBackend | undefined {
|
|
879
|
+
return undefined;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
export async function flushWebStorageBackends(): Promise<void> {
|
|
883
|
+
// Native platforms do not use web storage backends.
|
|
884
|
+
}
|
|
885
|
+
|
|
692
886
|
export interface StorageItemConfig<T> {
|
|
693
887
|
key: string;
|
|
694
888
|
scope: StorageScope;
|
|
@@ -700,6 +894,7 @@ export interface StorageItemConfig<T> {
|
|
|
700
894
|
expiration?: ExpirationConfig;
|
|
701
895
|
onExpired?: (key: string) => void;
|
|
702
896
|
readCache?: boolean;
|
|
897
|
+
coalesceDiskWrites?: boolean;
|
|
703
898
|
coalesceSecureWrites?: boolean;
|
|
704
899
|
namespace?: string;
|
|
705
900
|
biometric?: boolean;
|
|
@@ -784,6 +979,8 @@ export function createStorageItem<T = undefined>(
|
|
|
784
979
|
const memoryExpiration =
|
|
785
980
|
expiration && isMemory ? new Map<string, number>() : null;
|
|
786
981
|
const readCache = !isMemory && config.readCache === true;
|
|
982
|
+
const coalesceDiskWrites =
|
|
983
|
+
config.scope === StorageScope.Disk && config.coalesceDiskWrites === true;
|
|
787
984
|
const coalesceSecureWrites =
|
|
788
985
|
config.scope === StorageScope.Secure &&
|
|
789
986
|
config.coalesceSecureWrites === true &&
|
|
@@ -852,6 +1049,13 @@ export function createStorageItem<T = undefined>(
|
|
|
852
1049
|
return memoryStore.get(storageKey);
|
|
853
1050
|
}
|
|
854
1051
|
|
|
1052
|
+
if (nonMemoryScope === StorageScope.Disk) {
|
|
1053
|
+
const pending = pendingDiskWrites.get(storageKey);
|
|
1054
|
+
if (pending !== undefined) {
|
|
1055
|
+
return pending.value;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
855
1059
|
if (nonMemoryScope === StorageScope.Secure && !isBiometric) {
|
|
856
1060
|
const pending = pendingSecureWrites.get(storageKey);
|
|
857
1061
|
if (pending !== undefined) {
|
|
@@ -888,6 +1092,15 @@ export function createStorageItem<T = undefined>(
|
|
|
888
1092
|
|
|
889
1093
|
cacheRawValue(nonMemoryScope!, storageKey, rawValue);
|
|
890
1094
|
|
|
1095
|
+
if (nonMemoryScope === StorageScope.Disk) {
|
|
1096
|
+
if (coalesceDiskWrites || diskWritesAsync) {
|
|
1097
|
+
scheduleDiskWrite(storageKey, rawValue);
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
clearPendingDiskWrite(storageKey);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
891
1104
|
if (coalesceSecureWrites) {
|
|
892
1105
|
scheduleSecureWrite(
|
|
893
1106
|
storageKey,
|
|
@@ -915,6 +1128,15 @@ export function createStorageItem<T = undefined>(
|
|
|
915
1128
|
|
|
916
1129
|
cacheRawValue(nonMemoryScope!, storageKey, undefined);
|
|
917
1130
|
|
|
1131
|
+
if (nonMemoryScope === StorageScope.Disk) {
|
|
1132
|
+
if (coalesceDiskWrites || diskWritesAsync) {
|
|
1133
|
+
scheduleDiskWrite(storageKey, undefined);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
clearPendingDiskWrite(storageKey);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
918
1140
|
if (coalesceSecureWrites) {
|
|
919
1141
|
scheduleSecureWrite(
|
|
920
1142
|
storageKey,
|
|
@@ -1125,6 +1347,18 @@ export function createStorageItem<T = undefined>(
|
|
|
1125
1347
|
measureOperation("item:has", config.scope, () => {
|
|
1126
1348
|
if (isMemory) return memoryStore.has(storageKey);
|
|
1127
1349
|
if (isBiometric) return getStorageModule().hasSecureBiometric(storageKey);
|
|
1350
|
+
if (nonMemoryScope === StorageScope.Disk) {
|
|
1351
|
+
const pending = pendingDiskWrites.get(storageKey);
|
|
1352
|
+
if (pending !== undefined) {
|
|
1353
|
+
return pending.value !== undefined;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
if (nonMemoryScope === StorageScope.Secure) {
|
|
1357
|
+
const pending = pendingSecureWrites.get(storageKey);
|
|
1358
|
+
if (pending !== undefined) {
|
|
1359
|
+
return pending.value !== undefined;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1128
1362
|
return getStorageModule().has(storageKey, config.scope);
|
|
1129
1363
|
});
|
|
1130
1364
|
|
|
@@ -1224,6 +1458,14 @@ export function getBatch(
|
|
|
1224
1458
|
const keyIndexes: number[] = [];
|
|
1225
1459
|
|
|
1226
1460
|
items.forEach((item, index) => {
|
|
1461
|
+
if (scope === StorageScope.Disk) {
|
|
1462
|
+
const pending = pendingDiskWrites.get(item.key);
|
|
1463
|
+
if (pending !== undefined) {
|
|
1464
|
+
rawValues[index] = pending.value;
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1227
1469
|
if (scope === StorageScope.Secure) {
|
|
1228
1470
|
const pending = pendingSecureWrites.get(item.key);
|
|
1229
1471
|
if (pending !== undefined) {
|
|
@@ -1353,6 +1595,8 @@ export function setBatch<T>(
|
|
|
1353
1595
|
return;
|
|
1354
1596
|
}
|
|
1355
1597
|
|
|
1598
|
+
flushDiskWrites();
|
|
1599
|
+
|
|
1356
1600
|
const useRawBatchPath = items.every(({ item }) =>
|
|
1357
1601
|
canUseRawBatchPath(asInternal(item)),
|
|
1358
1602
|
);
|
|
@@ -1387,6 +1631,9 @@ export function removeBatch(
|
|
|
1387
1631
|
}
|
|
1388
1632
|
|
|
1389
1633
|
const keys = items.map((item) => item.key);
|
|
1634
|
+
if (scope === StorageScope.Disk) {
|
|
1635
|
+
flushDiskWrites();
|
|
1636
|
+
}
|
|
1390
1637
|
if (scope === StorageScope.Secure) {
|
|
1391
1638
|
flushSecureWrites();
|
|
1392
1639
|
}
|
|
@@ -1450,6 +1697,9 @@ export function runTransaction<T>(
|
|
|
1450
1697
|
): T {
|
|
1451
1698
|
return measureOperation("transaction:run", scope, () => {
|
|
1452
1699
|
assertValidScope(scope);
|
|
1700
|
+
if (scope === StorageScope.Disk) {
|
|
1701
|
+
flushDiskWrites();
|
|
1702
|
+
}
|
|
1453
1703
|
if (scope === StorageScope.Secure) {
|
|
1454
1704
|
flushSecureWrites();
|
|
1455
1705
|
}
|
|
@@ -1525,6 +1775,9 @@ export function runTransaction<T>(
|
|
|
1525
1775
|
}
|
|
1526
1776
|
});
|
|
1527
1777
|
|
|
1778
|
+
if (scope === StorageScope.Disk) {
|
|
1779
|
+
flushDiskWrites();
|
|
1780
|
+
}
|
|
1528
1781
|
if (scope === StorageScope.Secure) {
|
|
1529
1782
|
flushSecureWrites();
|
|
1530
1783
|
}
|
|
@@ -1555,16 +1808,7 @@ export type SecureAuthStorageConfig<K extends string = string> = Record<
|
|
|
1555
1808
|
>;
|
|
1556
1809
|
|
|
1557
1810
|
export function isKeychainLockedError(err: unknown): boolean {
|
|
1558
|
-
|
|
1559
|
-
const msg = err.message;
|
|
1560
|
-
return (
|
|
1561
|
-
msg.includes("errSecInteractionNotAllowed") ||
|
|
1562
|
-
msg.includes("UserNotAuthenticatedException") ||
|
|
1563
|
-
msg.includes("KeyStoreException") ||
|
|
1564
|
-
msg.includes("KeyPermanentlyInvalidatedException") ||
|
|
1565
|
-
msg.includes("InvalidKeyException") ||
|
|
1566
|
-
msg.includes("android.security.keystore")
|
|
1567
|
-
);
|
|
1811
|
+
return isLockedStorageErrorCode(getStorageErrorCode(err));
|
|
1568
1812
|
}
|
|
1569
1813
|
|
|
1570
1814
|
export function createSecureAuthStorage<K extends string>(
|