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
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createLocalStorageWebBackend = createLocalStorageWebBackend;
|
|
7
|
+
function getResolvedStorage(resolveStorage) {
|
|
8
|
+
if (resolveStorage) {
|
|
9
|
+
return resolveStorage();
|
|
10
|
+
}
|
|
11
|
+
if (typeof globalThis.localStorage === "undefined") {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return globalThis.localStorage;
|
|
15
|
+
}
|
|
16
|
+
function createLocalStorageWebBackend(options = {}) {
|
|
17
|
+
const includeKey = options.includeKey;
|
|
18
|
+
const resolveStorage = options.resolveStorage;
|
|
19
|
+
const listKeys = () => {
|
|
20
|
+
const storage = getResolvedStorage(resolveStorage);
|
|
21
|
+
if (!storage) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
const keys = [];
|
|
25
|
+
for (let index = 0; index < storage.length; index += 1) {
|
|
26
|
+
const key = storage.key(index);
|
|
27
|
+
if (!key) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (includeKey && !includeKey(key)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
keys.push(key);
|
|
34
|
+
}
|
|
35
|
+
return keys;
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
name: options.name ?? "localStorage",
|
|
39
|
+
getItem(key) {
|
|
40
|
+
return getResolvedStorage(resolveStorage)?.getItem(key) ?? null;
|
|
41
|
+
},
|
|
42
|
+
setItem(key, value) {
|
|
43
|
+
getResolvedStorage(resolveStorage)?.setItem(key, value);
|
|
44
|
+
},
|
|
45
|
+
removeItem(key) {
|
|
46
|
+
getResolvedStorage(resolveStorage)?.removeItem(key);
|
|
47
|
+
},
|
|
48
|
+
clear() {
|
|
49
|
+
const storage = getResolvedStorage(resolveStorage);
|
|
50
|
+
if (!storage) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
listKeys().forEach(key => {
|
|
54
|
+
storage.removeItem(key);
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
getAllKeys() {
|
|
58
|
+
return listKeys();
|
|
59
|
+
},
|
|
60
|
+
getMany(keys) {
|
|
61
|
+
const storage = getResolvedStorage(resolveStorage);
|
|
62
|
+
if (!storage) {
|
|
63
|
+
return keys.map(() => null);
|
|
64
|
+
}
|
|
65
|
+
return keys.map(key => storage.getItem(key));
|
|
66
|
+
},
|
|
67
|
+
setMany(entries) {
|
|
68
|
+
const storage = getResolvedStorage(resolveStorage);
|
|
69
|
+
if (!storage) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
entries.forEach(([key, value]) => {
|
|
73
|
+
storage.setItem(key, value);
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
removeMany(keys) {
|
|
77
|
+
const storage = getResolvedStorage(resolveStorage);
|
|
78
|
+
if (!storage) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
keys.forEach(key => {
|
|
82
|
+
storage.removeItem(key);
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
size() {
|
|
86
|
+
return listKeys().length;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=web-storage-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["getResolvedStorage","resolveStorage","globalThis","localStorage","undefined","createLocalStorageWebBackend","options","includeKey","listKeys","storage","keys","index","length","key","push","name","getItem","setItem","value","removeItem","clear","forEach","getAllKeys","getMany","map","setMany","entries","removeMany","size"],"sourceRoot":"../../src","sources":["web-storage-backend.ts"],"mappings":"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","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
import { NitroModules } from "react-native-nitro-modules";
|
|
4
4
|
import { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
|
|
5
5
|
import { MIGRATION_VERSION_KEY, isStoredEnvelope, assertBatchScope, assertValidScope, decodeNativeBatchValue, serializeWithPrimitiveFastPath, deserializeWithPrimitiveFastPath, toVersionToken, prefixKey, isNamespaced } from "./internal";
|
|
6
|
+
import { getStorageErrorCode, isLockedStorageErrorCode } from "./storage-runtime";
|
|
6
7
|
export { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
|
|
7
8
|
export { migrateFromMMKV } from "./migration";
|
|
9
|
+
export { getStorageErrorCode } from "./storage-runtime";
|
|
8
10
|
function asInternal(item) {
|
|
9
11
|
return item;
|
|
10
12
|
}
|
|
@@ -18,6 +20,7 @@ const registeredMigrations = new Map();
|
|
|
18
20
|
const runMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : task => {
|
|
19
21
|
Promise.resolve().then(task);
|
|
20
22
|
};
|
|
23
|
+
const now = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
|
|
21
24
|
let _storageModule = null;
|
|
22
25
|
function getStorageModule() {
|
|
23
26
|
if (!_storageModule) {
|
|
@@ -30,6 +33,9 @@ const memoryListeners = new Map();
|
|
|
30
33
|
const scopedListeners = new Map([[StorageScope.Disk, new Map()], [StorageScope.Secure, new Map()]]);
|
|
31
34
|
const scopedUnsubscribers = new Map();
|
|
32
35
|
const scopedRawCache = new Map([[StorageScope.Disk, new Map()], [StorageScope.Secure, new Map()]]);
|
|
36
|
+
const pendingDiskWrites = new Map();
|
|
37
|
+
let diskFlushScheduled = false;
|
|
38
|
+
let diskWritesAsync = false;
|
|
33
39
|
const pendingSecureWrites = new Map();
|
|
34
40
|
let secureFlushScheduled = false;
|
|
35
41
|
let secureDefaultAccessControl = AccessControl.WhenUnlocked;
|
|
@@ -59,11 +65,11 @@ function measureOperation(operation, scope, fn, keysCount = 1) {
|
|
|
59
65
|
if (!metricsObserver) {
|
|
60
66
|
return fn();
|
|
61
67
|
}
|
|
62
|
-
const start =
|
|
68
|
+
const start = now();
|
|
63
69
|
try {
|
|
64
70
|
return fn();
|
|
65
71
|
} finally {
|
|
66
|
-
recordMetric(operation, scope,
|
|
72
|
+
recordMetric(operation, scope, now() - start, keysCount);
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
function getScopedListeners(scope) {
|
|
@@ -120,12 +126,50 @@ function addKeyListener(registry, key, listener) {
|
|
|
120
126
|
function readPendingSecureWrite(key) {
|
|
121
127
|
return pendingSecureWrites.get(key)?.value;
|
|
122
128
|
}
|
|
129
|
+
function readPendingDiskWrite(key) {
|
|
130
|
+
return pendingDiskWrites.get(key)?.value;
|
|
131
|
+
}
|
|
132
|
+
function hasPendingDiskWrite(key) {
|
|
133
|
+
return pendingDiskWrites.has(key);
|
|
134
|
+
}
|
|
123
135
|
function hasPendingSecureWrite(key) {
|
|
124
136
|
return pendingSecureWrites.has(key);
|
|
125
137
|
}
|
|
138
|
+
function clearPendingDiskWrite(key) {
|
|
139
|
+
pendingDiskWrites.delete(key);
|
|
140
|
+
}
|
|
126
141
|
function clearPendingSecureWrite(key) {
|
|
127
142
|
pendingSecureWrites.delete(key);
|
|
128
143
|
}
|
|
144
|
+
function flushDiskWrites() {
|
|
145
|
+
diskFlushScheduled = false;
|
|
146
|
+
if (pendingDiskWrites.size === 0) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const writes = Array.from(pendingDiskWrites.values());
|
|
150
|
+
pendingDiskWrites.clear();
|
|
151
|
+
const keysToSet = [];
|
|
152
|
+
const valuesToSet = [];
|
|
153
|
+
const keysToRemove = [];
|
|
154
|
+
writes.forEach(({
|
|
155
|
+
key,
|
|
156
|
+
value
|
|
157
|
+
}) => {
|
|
158
|
+
if (value === undefined) {
|
|
159
|
+
keysToRemove.push(key);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
keysToSet.push(key);
|
|
163
|
+
valuesToSet.push(value);
|
|
164
|
+
});
|
|
165
|
+
const storageModule = getStorageModule();
|
|
166
|
+
if (keysToSet.length > 0) {
|
|
167
|
+
storageModule.setBatch(keysToSet, valuesToSet, StorageScope.Disk);
|
|
168
|
+
}
|
|
169
|
+
if (keysToRemove.length > 0) {
|
|
170
|
+
storageModule.removeBatch(keysToRemove, StorageScope.Disk);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
129
173
|
function flushSecureWrites() {
|
|
130
174
|
secureFlushScheduled = false;
|
|
131
175
|
if (pendingSecureWrites.size === 0) {
|
|
@@ -165,6 +209,17 @@ function flushSecureWrites() {
|
|
|
165
209
|
storageModule.removeBatch(keysToRemove, StorageScope.Secure);
|
|
166
210
|
}
|
|
167
211
|
}
|
|
212
|
+
function scheduleDiskWrite(key, value) {
|
|
213
|
+
pendingDiskWrites.set(key, {
|
|
214
|
+
key,
|
|
215
|
+
value
|
|
216
|
+
});
|
|
217
|
+
if (diskFlushScheduled) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
diskFlushScheduled = true;
|
|
221
|
+
runMicrotask(flushDiskWrites);
|
|
222
|
+
}
|
|
168
223
|
function scheduleSecureWrite(key, value, accessControl) {
|
|
169
224
|
const pendingWrite = {
|
|
170
225
|
key,
|
|
@@ -185,6 +240,13 @@ function ensureNativeScopeSubscription(scope) {
|
|
|
185
240
|
return;
|
|
186
241
|
}
|
|
187
242
|
const unsubscribe = getStorageModule().addOnChange(scope, (key, value) => {
|
|
243
|
+
if (scope === StorageScope.Disk) {
|
|
244
|
+
if (key === "") {
|
|
245
|
+
pendingDiskWrites.clear();
|
|
246
|
+
} else {
|
|
247
|
+
clearPendingDiskWrite(key);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
188
250
|
if (scope === StorageScope.Secure) {
|
|
189
251
|
if (key === "") {
|
|
190
252
|
pendingSecureWrites.clear();
|
|
@@ -220,6 +282,9 @@ function getRawValue(key, scope) {
|
|
|
220
282
|
const value = memoryStore.get(key);
|
|
221
283
|
return typeof value === "string" ? value : undefined;
|
|
222
284
|
}
|
|
285
|
+
if (scope === StorageScope.Disk && hasPendingDiskWrite(key)) {
|
|
286
|
+
return readPendingDiskWrite(key);
|
|
287
|
+
}
|
|
223
288
|
if (scope === StorageScope.Secure && hasPendingSecureWrite(key)) {
|
|
224
289
|
return readPendingSecureWrite(key);
|
|
225
290
|
}
|
|
@@ -232,6 +297,15 @@ function setRawValue(key, value, scope) {
|
|
|
232
297
|
notifyKeyListeners(memoryListeners, key);
|
|
233
298
|
return;
|
|
234
299
|
}
|
|
300
|
+
if (scope === StorageScope.Disk) {
|
|
301
|
+
cacheRawValue(scope, key, value);
|
|
302
|
+
if (diskWritesAsync) {
|
|
303
|
+
scheduleDiskWrite(key, value);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
flushDiskWrites();
|
|
307
|
+
clearPendingDiskWrite(key);
|
|
308
|
+
}
|
|
235
309
|
if (scope === StorageScope.Secure) {
|
|
236
310
|
flushSecureWrites();
|
|
237
311
|
clearPendingSecureWrite(key);
|
|
@@ -247,6 +321,15 @@ function removeRawValue(key, scope) {
|
|
|
247
321
|
notifyKeyListeners(memoryListeners, key);
|
|
248
322
|
return;
|
|
249
323
|
}
|
|
324
|
+
if (scope === StorageScope.Disk) {
|
|
325
|
+
cacheRawValue(scope, key, undefined);
|
|
326
|
+
if (diskWritesAsync) {
|
|
327
|
+
scheduleDiskWrite(key, undefined);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
flushDiskWrites();
|
|
331
|
+
clearPendingDiskWrite(key);
|
|
332
|
+
}
|
|
250
333
|
if (scope === StorageScope.Secure) {
|
|
251
334
|
flushSecureWrites();
|
|
252
335
|
clearPendingSecureWrite(key);
|
|
@@ -273,6 +356,10 @@ export const storage = {
|
|
|
273
356
|
notifyAllListeners(memoryListeners);
|
|
274
357
|
return;
|
|
275
358
|
}
|
|
359
|
+
if (scope === StorageScope.Disk) {
|
|
360
|
+
flushDiskWrites();
|
|
361
|
+
pendingDiskWrites.clear();
|
|
362
|
+
}
|
|
276
363
|
if (scope === StorageScope.Secure) {
|
|
277
364
|
flushSecureWrites();
|
|
278
365
|
pendingSecureWrites.clear();
|
|
@@ -301,6 +388,9 @@ export const storage = {
|
|
|
301
388
|
return;
|
|
302
389
|
}
|
|
303
390
|
const keyPrefix = prefixKey(namespace, "");
|
|
391
|
+
if (scope === StorageScope.Disk) {
|
|
392
|
+
flushDiskWrites();
|
|
393
|
+
}
|
|
304
394
|
if (scope === StorageScope.Secure) {
|
|
305
395
|
flushSecureWrites();
|
|
306
396
|
}
|
|
@@ -324,6 +414,12 @@ export const storage = {
|
|
|
324
414
|
if (scope === StorageScope.Memory) {
|
|
325
415
|
return memoryStore.has(key);
|
|
326
416
|
}
|
|
417
|
+
if (scope === StorageScope.Disk) {
|
|
418
|
+
flushDiskWrites();
|
|
419
|
+
}
|
|
420
|
+
if (scope === StorageScope.Secure) {
|
|
421
|
+
flushSecureWrites();
|
|
422
|
+
}
|
|
327
423
|
return getStorageModule().has(key, scope);
|
|
328
424
|
});
|
|
329
425
|
},
|
|
@@ -333,6 +429,12 @@ export const storage = {
|
|
|
333
429
|
if (scope === StorageScope.Memory) {
|
|
334
430
|
return Array.from(memoryStore.keys());
|
|
335
431
|
}
|
|
432
|
+
if (scope === StorageScope.Disk) {
|
|
433
|
+
flushDiskWrites();
|
|
434
|
+
}
|
|
435
|
+
if (scope === StorageScope.Secure) {
|
|
436
|
+
flushSecureWrites();
|
|
437
|
+
}
|
|
336
438
|
return getStorageModule().getAllKeys(scope);
|
|
337
439
|
});
|
|
338
440
|
},
|
|
@@ -342,6 +444,12 @@ export const storage = {
|
|
|
342
444
|
if (scope === StorageScope.Memory) {
|
|
343
445
|
return Array.from(memoryStore.keys()).filter(key => key.startsWith(prefix));
|
|
344
446
|
}
|
|
447
|
+
if (scope === StorageScope.Disk) {
|
|
448
|
+
flushDiskWrites();
|
|
449
|
+
}
|
|
450
|
+
if (scope === StorageScope.Secure) {
|
|
451
|
+
flushSecureWrites();
|
|
452
|
+
}
|
|
345
453
|
return getStorageModule().getKeysByPrefix(prefix, scope);
|
|
346
454
|
});
|
|
347
455
|
},
|
|
@@ -361,6 +469,12 @@ export const storage = {
|
|
|
361
469
|
});
|
|
362
470
|
return result;
|
|
363
471
|
}
|
|
472
|
+
if (scope === StorageScope.Disk) {
|
|
473
|
+
flushDiskWrites();
|
|
474
|
+
}
|
|
475
|
+
if (scope === StorageScope.Secure) {
|
|
476
|
+
flushSecureWrites();
|
|
477
|
+
}
|
|
364
478
|
const values = getStorageModule().getBatch(keys, scope);
|
|
365
479
|
keys.forEach((key, idx) => {
|
|
366
480
|
const value = decodeNativeBatchValue(values[idx]);
|
|
@@ -381,6 +495,12 @@ export const storage = {
|
|
|
381
495
|
});
|
|
382
496
|
return result;
|
|
383
497
|
}
|
|
498
|
+
if (scope === StorageScope.Disk) {
|
|
499
|
+
flushDiskWrites();
|
|
500
|
+
}
|
|
501
|
+
if (scope === StorageScope.Secure) {
|
|
502
|
+
flushSecureWrites();
|
|
503
|
+
}
|
|
384
504
|
const keys = getStorageModule().getAllKeys(scope);
|
|
385
505
|
if (keys.length === 0) return result;
|
|
386
506
|
const values = getStorageModule().getBatch(keys, scope);
|
|
@@ -397,6 +517,12 @@ export const storage = {
|
|
|
397
517
|
if (scope === StorageScope.Memory) {
|
|
398
518
|
return memoryStore.size;
|
|
399
519
|
}
|
|
520
|
+
if (scope === StorageScope.Disk) {
|
|
521
|
+
flushDiskWrites();
|
|
522
|
+
}
|
|
523
|
+
if (scope === StorageScope.Secure) {
|
|
524
|
+
flushSecureWrites();
|
|
525
|
+
}
|
|
400
526
|
return getStorageModule().size(scope);
|
|
401
527
|
});
|
|
402
528
|
},
|
|
@@ -411,6 +537,19 @@ export const storage = {
|
|
|
411
537
|
getStorageModule().setSecureWritesAsync(enabled);
|
|
412
538
|
});
|
|
413
539
|
},
|
|
540
|
+
setDiskWritesAsync: enabled => {
|
|
541
|
+
measureOperation("storage:setDiskWritesAsync", StorageScope.Disk, () => {
|
|
542
|
+
diskWritesAsync = enabled;
|
|
543
|
+
if (!enabled) {
|
|
544
|
+
flushDiskWrites();
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
},
|
|
548
|
+
flushDiskWrites: () => {
|
|
549
|
+
measureOperation("storage:flushDiskWrites", StorageScope.Disk, () => {
|
|
550
|
+
flushDiskWrites();
|
|
551
|
+
});
|
|
552
|
+
},
|
|
414
553
|
flushSecureWrites: () => {
|
|
415
554
|
measureOperation("storage:flushSecureWrites", StorageScope.Secure, () => {
|
|
416
555
|
flushSecureWrites();
|
|
@@ -439,6 +578,18 @@ export const storage = {
|
|
|
439
578
|
resetMetrics: () => {
|
|
440
579
|
metricsCounters.clear();
|
|
441
580
|
},
|
|
581
|
+
getCapabilities: () => ({
|
|
582
|
+
platform: "native",
|
|
583
|
+
backend: {
|
|
584
|
+
disk: "platform-preferences",
|
|
585
|
+
secure: "platform-secure-storage"
|
|
586
|
+
},
|
|
587
|
+
writeBuffering: {
|
|
588
|
+
disk: true,
|
|
589
|
+
secure: true
|
|
590
|
+
},
|
|
591
|
+
errorClassification: true
|
|
592
|
+
}),
|
|
442
593
|
getString: (key, scope) => {
|
|
443
594
|
return measureOperation("storage:getString", scope, () => {
|
|
444
595
|
return getRawValue(key, scope);
|
|
@@ -482,6 +633,15 @@ export function setWebSecureStorageBackend(_backend) {
|
|
|
482
633
|
export function getWebSecureStorageBackend() {
|
|
483
634
|
return undefined;
|
|
484
635
|
}
|
|
636
|
+
export function setWebDiskStorageBackend(_backend) {
|
|
637
|
+
// Native platforms do not use web disk backends.
|
|
638
|
+
}
|
|
639
|
+
export function getWebDiskStorageBackend() {
|
|
640
|
+
return undefined;
|
|
641
|
+
}
|
|
642
|
+
export async function flushWebStorageBackends() {
|
|
643
|
+
// Native platforms do not use web storage backends.
|
|
644
|
+
}
|
|
485
645
|
function canUseRawBatchPath(item) {
|
|
486
646
|
return item._hasExpiration === false && item._hasValidation === false && item._isBiometric !== true && item._secureAccessControl === undefined;
|
|
487
647
|
}
|
|
@@ -509,6 +669,7 @@ export function createStorageItem(config) {
|
|
|
509
669
|
const expirationTtlMs = expiration?.ttlMs;
|
|
510
670
|
const memoryExpiration = expiration && isMemory ? new Map() : null;
|
|
511
671
|
const readCache = !isMemory && config.readCache === true;
|
|
672
|
+
const coalesceDiskWrites = config.scope === StorageScope.Disk && config.coalesceDiskWrites === true;
|
|
512
673
|
const coalesceSecureWrites = config.scope === StorageScope.Secure && config.coalesceSecureWrites === true && !isBiometric;
|
|
513
674
|
const defaultValue = config.defaultValue;
|
|
514
675
|
const nonMemoryScope = config.scope === StorageScope.Disk ? StorageScope.Disk : config.scope === StorageScope.Secure ? StorageScope.Secure : null;
|
|
@@ -556,6 +717,12 @@ export function createStorageItem(config) {
|
|
|
556
717
|
}
|
|
557
718
|
return memoryStore.get(storageKey);
|
|
558
719
|
}
|
|
720
|
+
if (nonMemoryScope === StorageScope.Disk) {
|
|
721
|
+
const pending = pendingDiskWrites.get(storageKey);
|
|
722
|
+
if (pending !== undefined) {
|
|
723
|
+
return pending.value;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
559
726
|
if (nonMemoryScope === StorageScope.Secure && !isBiometric) {
|
|
560
727
|
const pending = pendingSecureWrites.get(storageKey);
|
|
561
728
|
if (pending !== undefined) {
|
|
@@ -582,6 +749,13 @@ export function createStorageItem(config) {
|
|
|
582
749
|
return;
|
|
583
750
|
}
|
|
584
751
|
cacheRawValue(nonMemoryScope, storageKey, rawValue);
|
|
752
|
+
if (nonMemoryScope === StorageScope.Disk) {
|
|
753
|
+
if (coalesceDiskWrites || diskWritesAsync) {
|
|
754
|
+
scheduleDiskWrite(storageKey, rawValue);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
clearPendingDiskWrite(storageKey);
|
|
758
|
+
}
|
|
585
759
|
if (coalesceSecureWrites) {
|
|
586
760
|
scheduleSecureWrite(storageKey, rawValue, secureAccessControl ?? secureDefaultAccessControl);
|
|
587
761
|
return;
|
|
@@ -598,6 +772,13 @@ export function createStorageItem(config) {
|
|
|
598
772
|
return;
|
|
599
773
|
}
|
|
600
774
|
cacheRawValue(nonMemoryScope, storageKey, undefined);
|
|
775
|
+
if (nonMemoryScope === StorageScope.Disk) {
|
|
776
|
+
if (coalesceDiskWrites || diskWritesAsync) {
|
|
777
|
+
scheduleDiskWrite(storageKey, undefined);
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
clearPendingDiskWrite(storageKey);
|
|
781
|
+
}
|
|
601
782
|
if (coalesceSecureWrites) {
|
|
602
783
|
scheduleSecureWrite(storageKey, undefined, secureAccessControl ?? secureDefaultAccessControl);
|
|
603
784
|
return;
|
|
@@ -758,6 +939,18 @@ export function createStorageItem(config) {
|
|
|
758
939
|
const hasItem = () => measureOperation("item:has", config.scope, () => {
|
|
759
940
|
if (isMemory) return memoryStore.has(storageKey);
|
|
760
941
|
if (isBiometric) return getStorageModule().hasSecureBiometric(storageKey);
|
|
942
|
+
if (nonMemoryScope === StorageScope.Disk) {
|
|
943
|
+
const pending = pendingDiskWrites.get(storageKey);
|
|
944
|
+
if (pending !== undefined) {
|
|
945
|
+
return pending.value !== undefined;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
if (nonMemoryScope === StorageScope.Secure) {
|
|
949
|
+
const pending = pendingSecureWrites.get(storageKey);
|
|
950
|
+
if (pending !== undefined) {
|
|
951
|
+
return pending.value !== undefined;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
761
954
|
return getStorageModule().has(storageKey, config.scope);
|
|
762
955
|
});
|
|
763
956
|
const subscribe = callback => {
|
|
@@ -820,6 +1013,13 @@ export function getBatch(items, scope) {
|
|
|
820
1013
|
const keysToFetch = [];
|
|
821
1014
|
const keyIndexes = [];
|
|
822
1015
|
items.forEach((item, index) => {
|
|
1016
|
+
if (scope === StorageScope.Disk) {
|
|
1017
|
+
const pending = pendingDiskWrites.get(item.key);
|
|
1018
|
+
if (pending !== undefined) {
|
|
1019
|
+
rawValues[index] = pending.value;
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
823
1023
|
if (scope === StorageScope.Secure) {
|
|
824
1024
|
const pending = pendingSecureWrites.get(item.key);
|
|
825
1025
|
if (pending !== undefined) {
|
|
@@ -938,6 +1138,7 @@ export function setBatch(items, scope) {
|
|
|
938
1138
|
});
|
|
939
1139
|
return;
|
|
940
1140
|
}
|
|
1141
|
+
flushDiskWrites();
|
|
941
1142
|
const useRawBatchPath = items.every(({
|
|
942
1143
|
item
|
|
943
1144
|
}) => canUseRawBatchPath(asInternal(item)));
|
|
@@ -962,6 +1163,9 @@ export function removeBatch(items, scope) {
|
|
|
962
1163
|
return;
|
|
963
1164
|
}
|
|
964
1165
|
const keys = items.map(item => item.key);
|
|
1166
|
+
if (scope === StorageScope.Disk) {
|
|
1167
|
+
flushDiskWrites();
|
|
1168
|
+
}
|
|
965
1169
|
if (scope === StorageScope.Secure) {
|
|
966
1170
|
flushSecureWrites();
|
|
967
1171
|
}
|
|
@@ -1007,6 +1211,9 @@ export function migrateToLatest(scope = StorageScope.Disk) {
|
|
|
1007
1211
|
export function runTransaction(scope, transaction) {
|
|
1008
1212
|
return measureOperation("transaction:run", scope, () => {
|
|
1009
1213
|
assertValidScope(scope);
|
|
1214
|
+
if (scope === StorageScope.Disk) {
|
|
1215
|
+
flushDiskWrites();
|
|
1216
|
+
}
|
|
1010
1217
|
if (scope === StorageScope.Secure) {
|
|
1011
1218
|
flushSecureWrites();
|
|
1012
1219
|
}
|
|
@@ -1073,6 +1280,9 @@ export function runTransaction(scope, transaction) {
|
|
|
1073
1280
|
valuesToSet.push(previousValue);
|
|
1074
1281
|
}
|
|
1075
1282
|
});
|
|
1283
|
+
if (scope === StorageScope.Disk) {
|
|
1284
|
+
flushDiskWrites();
|
|
1285
|
+
}
|
|
1076
1286
|
if (scope === StorageScope.Secure) {
|
|
1077
1287
|
flushSecureWrites();
|
|
1078
1288
|
}
|
|
@@ -1090,9 +1300,7 @@ export function runTransaction(scope, transaction) {
|
|
|
1090
1300
|
});
|
|
1091
1301
|
}
|
|
1092
1302
|
export function isKeychainLockedError(err) {
|
|
1093
|
-
|
|
1094
|
-
const msg = err.message;
|
|
1095
|
-
return msg.includes("errSecInteractionNotAllowed") || msg.includes("UserNotAuthenticatedException") || msg.includes("KeyStoreException") || msg.includes("KeyPermanentlyInvalidatedException") || msg.includes("InvalidKeyException") || msg.includes("android.security.keystore");
|
|
1303
|
+
return isLockedStorageErrorCode(getStorageErrorCode(err));
|
|
1096
1304
|
}
|
|
1097
1305
|
export function createSecureAuthStorage(config, options) {
|
|
1098
1306
|
const ns = options?.namespace ?? "auth";
|