react-native-nitro-storage 0.3.0 → 0.3.2
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 +594 -247
- package/android/CMakeLists.txt +2 -0
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +102 -11
- package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +16 -0
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +154 -34
- package/android/src/main/java/com/nitrostorage/NitroStoragePackage.kt +2 -2
- package/cpp/bindings/HybridStorage.cpp +176 -21
- package/cpp/bindings/HybridStorage.hpp +29 -2
- package/cpp/core/NativeStorageAdapter.hpp +16 -0
- package/ios/IOSStorageAdapterCpp.hpp +20 -0
- package/ios/IOSStorageAdapterCpp.mm +239 -32
- package/lib/commonjs/Storage.types.js +23 -1
- package/lib/commonjs/Storage.types.js.map +1 -1
- package/lib/commonjs/index.js +292 -75
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +473 -86
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/internal.js +10 -0
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/commonjs/storage-hooks.js +36 -0
- package/lib/commonjs/storage-hooks.js.map +1 -0
- package/lib/module/Storage.types.js +22 -0
- package/lib/module/Storage.types.js.map +1 -1
- package/lib/module/index.js +264 -75
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +445 -86
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/internal.js +8 -0
- package/lib/module/internal.js.map +1 -1
- package/lib/module/storage-hooks.js +30 -0
- package/lib/module/storage-hooks.js.map +1 -0
- package/lib/typescript/Storage.nitro.d.ts +12 -0
- package/lib/typescript/Storage.nitro.d.ts.map +1 -1
- package/lib/typescript/Storage.types.d.ts +20 -0
- package/lib/typescript/Storage.types.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +33 -10
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +45 -10
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/internal.d.ts +2 -0
- package/lib/typescript/internal.d.ts.map +1 -1
- package/lib/typescript/storage-hooks.d.ts +10 -0
- package/lib/typescript/storage-hooks.d.ts.map +1 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +12 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +12 -0
- package/package.json +8 -3
- package/src/Storage.nitro.ts +13 -2
- package/src/Storage.types.ts +22 -0
- package/src/index.ts +382 -123
- package/src/index.web.ts +618 -134
- package/src/internal.ts +14 -4
- package/src/migration.ts +1 -1
- package/src/storage-hooks.ts +48 -0
package/lib/commonjs/index.js
CHANGED
|
@@ -3,12 +3,25 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
Object.defineProperty(exports, "AccessControl", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _Storage.AccessControl;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "BiometricLevel", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _Storage.BiometricLevel;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
6
18
|
Object.defineProperty(exports, "StorageScope", {
|
|
7
19
|
enumerable: true,
|
|
8
20
|
get: function () {
|
|
9
21
|
return _Storage.StorageScope;
|
|
10
22
|
}
|
|
11
23
|
});
|
|
24
|
+
exports.createSecureAuthStorage = createSecureAuthStorage;
|
|
12
25
|
exports.createStorageItem = createStorageItem;
|
|
13
26
|
exports.getBatch = getBatch;
|
|
14
27
|
Object.defineProperty(exports, "migrateFromMMKV", {
|
|
@@ -23,14 +36,38 @@ exports.removeBatch = removeBatch;
|
|
|
23
36
|
exports.runTransaction = runTransaction;
|
|
24
37
|
exports.setBatch = setBatch;
|
|
25
38
|
exports.storage = void 0;
|
|
26
|
-
exports
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
Object.defineProperty(exports, "useSetStorage", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
get: function () {
|
|
42
|
+
return _storageHooks.useSetStorage;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
Object.defineProperty(exports, "useStorage", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
get: function () {
|
|
48
|
+
return _storageHooks.useStorage;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
Object.defineProperty(exports, "useStorageSelector", {
|
|
52
|
+
enumerable: true,
|
|
53
|
+
get: function () {
|
|
54
|
+
return _storageHooks.useStorageSelector;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
30
57
|
var _reactNativeNitroModules = require("react-native-nitro-modules");
|
|
31
58
|
var _Storage = require("./Storage.types");
|
|
32
59
|
var _internal = require("./internal");
|
|
33
60
|
var _migration = require("./migration");
|
|
61
|
+
var _storageHooks = require("./storage-hooks");
|
|
62
|
+
function asInternal(item) {
|
|
63
|
+
return item;
|
|
64
|
+
}
|
|
65
|
+
function isUpdater(valueOrFn) {
|
|
66
|
+
return typeof valueOrFn === "function";
|
|
67
|
+
}
|
|
68
|
+
function typedKeys(record) {
|
|
69
|
+
return Object.keys(record);
|
|
70
|
+
}
|
|
34
71
|
const registeredMigrations = new Map();
|
|
35
72
|
const runMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : task => {
|
|
36
73
|
Promise.resolve().then(task);
|
|
@@ -49,6 +86,7 @@ const scopedUnsubscribers = new Map();
|
|
|
49
86
|
const scopedRawCache = new Map([[_Storage.StorageScope.Disk, new Map()], [_Storage.StorageScope.Secure, new Map()]]);
|
|
50
87
|
const pendingSecureWrites = new Map();
|
|
51
88
|
let secureFlushScheduled = false;
|
|
89
|
+
let secureDefaultAccessControl = _Storage.AccessControl.WhenUnlocked;
|
|
52
90
|
function getScopedListeners(scope) {
|
|
53
91
|
return scopedListeners.get(scope);
|
|
54
92
|
}
|
|
@@ -124,6 +162,7 @@ function flushSecureWrites() {
|
|
|
124
162
|
}
|
|
125
163
|
});
|
|
126
164
|
const storageModule = getStorageModule();
|
|
165
|
+
storageModule.setSecureAccessControl(secureDefaultAccessControl);
|
|
127
166
|
if (keysToSet.length > 0) {
|
|
128
167
|
storageModule.setBatch(keysToSet, valuesToSet, _Storage.StorageScope.Secure);
|
|
129
168
|
}
|
|
@@ -197,6 +236,7 @@ function setRawValue(key, value, scope) {
|
|
|
197
236
|
if (scope === _Storage.StorageScope.Secure) {
|
|
198
237
|
flushSecureWrites();
|
|
199
238
|
clearPendingSecureWrite(key);
|
|
239
|
+
getStorageModule().setSecureAccessControl(secureDefaultAccessControl);
|
|
200
240
|
}
|
|
201
241
|
getStorageModule().set(key, value, scope);
|
|
202
242
|
cacheRawValue(scope, key, value);
|
|
@@ -244,10 +284,86 @@ const storage = exports.storage = {
|
|
|
244
284
|
storage.clear(_Storage.StorageScope.Memory);
|
|
245
285
|
storage.clear(_Storage.StorageScope.Disk);
|
|
246
286
|
storage.clear(_Storage.StorageScope.Secure);
|
|
287
|
+
},
|
|
288
|
+
clearNamespace: (namespace, scope) => {
|
|
289
|
+
(0, _internal.assertValidScope)(scope);
|
|
290
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
291
|
+
for (const key of memoryStore.keys()) {
|
|
292
|
+
if ((0, _internal.isNamespaced)(key, namespace)) {
|
|
293
|
+
memoryStore.delete(key);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
notifyAllListeners(memoryListeners);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const keyPrefix = (0, _internal.prefixKey)(namespace, "");
|
|
300
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
301
|
+
flushSecureWrites();
|
|
302
|
+
}
|
|
303
|
+
clearScopeRawCache(scope);
|
|
304
|
+
getStorageModule().removeByPrefix(keyPrefix, scope);
|
|
305
|
+
},
|
|
306
|
+
clearBiometric: () => {
|
|
307
|
+
getStorageModule().clearSecureBiometric();
|
|
308
|
+
},
|
|
309
|
+
has: (key, scope) => {
|
|
310
|
+
(0, _internal.assertValidScope)(scope);
|
|
311
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
312
|
+
return memoryStore.has(key);
|
|
313
|
+
}
|
|
314
|
+
return getStorageModule().has(key, scope);
|
|
315
|
+
},
|
|
316
|
+
getAllKeys: scope => {
|
|
317
|
+
(0, _internal.assertValidScope)(scope);
|
|
318
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
319
|
+
return Array.from(memoryStore.keys());
|
|
320
|
+
}
|
|
321
|
+
return getStorageModule().getAllKeys(scope);
|
|
322
|
+
},
|
|
323
|
+
getAll: scope => {
|
|
324
|
+
(0, _internal.assertValidScope)(scope);
|
|
325
|
+
const result = {};
|
|
326
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
327
|
+
memoryStore.forEach((value, key) => {
|
|
328
|
+
if (typeof value === "string") result[key] = value;
|
|
329
|
+
});
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
const keys = getStorageModule().getAllKeys(scope);
|
|
333
|
+
if (keys.length === 0) return result;
|
|
334
|
+
const values = getStorageModule().getBatch(keys, scope);
|
|
335
|
+
keys.forEach((key, idx) => {
|
|
336
|
+
const val = (0, _internal.decodeNativeBatchValue)(values[idx]);
|
|
337
|
+
if (val !== undefined) result[key] = val;
|
|
338
|
+
});
|
|
339
|
+
return result;
|
|
340
|
+
},
|
|
341
|
+
size: scope => {
|
|
342
|
+
(0, _internal.assertValidScope)(scope);
|
|
343
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
344
|
+
return memoryStore.size;
|
|
345
|
+
}
|
|
346
|
+
return getStorageModule().size(scope);
|
|
347
|
+
},
|
|
348
|
+
setAccessControl: level => {
|
|
349
|
+
secureDefaultAccessControl = level;
|
|
350
|
+
getStorageModule().setSecureAccessControl(level);
|
|
351
|
+
},
|
|
352
|
+
setSecureWritesAsync: enabled => {
|
|
353
|
+
getStorageModule().setSecureWritesAsync(enabled);
|
|
354
|
+
},
|
|
355
|
+
flushSecureWrites: () => {
|
|
356
|
+
flushSecureWrites();
|
|
357
|
+
},
|
|
358
|
+
setKeychainAccessGroup: group => {
|
|
359
|
+
getStorageModule().setKeychainAccessGroup(group);
|
|
247
360
|
}
|
|
248
361
|
};
|
|
249
362
|
function canUseRawBatchPath(item) {
|
|
250
|
-
return item._hasExpiration === false && item._hasValidation === false;
|
|
363
|
+
return item._hasExpiration === false && item._hasValidation === false && item._isBiometric !== true && item._secureAccessControl === undefined;
|
|
364
|
+
}
|
|
365
|
+
function canUseSecureRawBatchPath(item) {
|
|
366
|
+
return item._hasExpiration === false && item._hasValidation === false && item._isBiometric !== true;
|
|
251
367
|
}
|
|
252
368
|
function defaultSerialize(value) {
|
|
253
369
|
return (0, _internal.serializeWithPrimitiveFastPath)(value);
|
|
@@ -256,16 +372,21 @@ function defaultDeserialize(value) {
|
|
|
256
372
|
return (0, _internal.deserializeWithPrimitiveFastPath)(value);
|
|
257
373
|
}
|
|
258
374
|
function createStorageItem(config) {
|
|
375
|
+
const storageKey = (0, _internal.prefixKey)(config.namespace, config.key);
|
|
259
376
|
const serialize = config.serialize ?? defaultSerialize;
|
|
260
377
|
const deserialize = config.deserialize ?? defaultDeserialize;
|
|
261
378
|
const isMemory = config.scope === _Storage.StorageScope.Memory;
|
|
379
|
+
const isBiometric = config.biometric === true && config.scope === _Storage.StorageScope.Secure;
|
|
380
|
+
const secureAccessControl = config.accessControl;
|
|
262
381
|
const validate = config.validate;
|
|
263
382
|
const onValidationError = config.onValidationError;
|
|
264
383
|
const expiration = config.expiration;
|
|
384
|
+
const onExpired = config.onExpired;
|
|
265
385
|
const expirationTtlMs = expiration?.ttlMs;
|
|
266
386
|
const memoryExpiration = expiration && isMemory ? new Map() : null;
|
|
267
387
|
const readCache = !isMemory && config.readCache === true;
|
|
268
|
-
const coalesceSecureWrites = config.scope === _Storage.StorageScope.Secure && config.coalesceSecureWrites === true;
|
|
388
|
+
const coalesceSecureWrites = config.scope === _Storage.StorageScope.Secure && config.coalesceSecureWrites === true && !isBiometric && secureAccessControl === undefined;
|
|
389
|
+
const defaultValue = config.defaultValue;
|
|
269
390
|
const nonMemoryScope = config.scope === _Storage.StorageScope.Disk ? _Storage.StorageScope.Disk : config.scope === _Storage.StorageScope.Secure ? _Storage.StorageScope.Secure : null;
|
|
270
391
|
if (expiration && expiration.ttlMs <= 0) {
|
|
271
392
|
throw new Error("expiration.ttlMs must be greater than 0.");
|
|
@@ -275,10 +396,12 @@ function createStorageItem(config) {
|
|
|
275
396
|
let lastRaw = undefined;
|
|
276
397
|
let lastValue;
|
|
277
398
|
let hasLastValue = false;
|
|
399
|
+
let lastExpiresAt = undefined;
|
|
278
400
|
const invalidateParsedCache = () => {
|
|
279
401
|
lastRaw = undefined;
|
|
280
402
|
lastValue = undefined;
|
|
281
403
|
hasLastValue = false;
|
|
404
|
+
lastExpiresAt = undefined;
|
|
282
405
|
};
|
|
283
406
|
const ensureSubscription = () => {
|
|
284
407
|
if (unsubscribe) {
|
|
@@ -289,66 +412,79 @@ function createStorageItem(config) {
|
|
|
289
412
|
listeners.forEach(callback => callback());
|
|
290
413
|
};
|
|
291
414
|
if (isMemory) {
|
|
292
|
-
unsubscribe = addKeyListener(memoryListeners,
|
|
415
|
+
unsubscribe = addKeyListener(memoryListeners, storageKey, listener);
|
|
293
416
|
return;
|
|
294
417
|
}
|
|
295
418
|
ensureNativeScopeSubscription(nonMemoryScope);
|
|
296
|
-
unsubscribe = addKeyListener(getScopedListeners(nonMemoryScope),
|
|
419
|
+
unsubscribe = addKeyListener(getScopedListeners(nonMemoryScope), storageKey, listener);
|
|
297
420
|
};
|
|
298
421
|
const readStoredRaw = () => {
|
|
299
422
|
if (isMemory) {
|
|
300
423
|
if (memoryExpiration) {
|
|
301
|
-
const expiresAt = memoryExpiration.get(
|
|
424
|
+
const expiresAt = memoryExpiration.get(storageKey);
|
|
302
425
|
if (expiresAt !== undefined && expiresAt <= Date.now()) {
|
|
303
|
-
memoryExpiration.delete(
|
|
304
|
-
memoryStore.delete(
|
|
305
|
-
notifyKeyListeners(memoryListeners,
|
|
426
|
+
memoryExpiration.delete(storageKey);
|
|
427
|
+
memoryStore.delete(storageKey);
|
|
428
|
+
notifyKeyListeners(memoryListeners, storageKey);
|
|
429
|
+
onExpired?.(storageKey);
|
|
306
430
|
return undefined;
|
|
307
431
|
}
|
|
308
432
|
}
|
|
309
|
-
return memoryStore.get(
|
|
433
|
+
return memoryStore.get(storageKey);
|
|
310
434
|
}
|
|
311
|
-
if (nonMemoryScope === _Storage.StorageScope.Secure && hasPendingSecureWrite(
|
|
312
|
-
return readPendingSecureWrite(
|
|
435
|
+
if (nonMemoryScope === _Storage.StorageScope.Secure && !isBiometric && hasPendingSecureWrite(storageKey)) {
|
|
436
|
+
return readPendingSecureWrite(storageKey);
|
|
313
437
|
}
|
|
314
438
|
if (readCache) {
|
|
315
|
-
if (hasCachedRawValue(nonMemoryScope,
|
|
316
|
-
return readCachedRawValue(nonMemoryScope,
|
|
439
|
+
if (hasCachedRawValue(nonMemoryScope, storageKey)) {
|
|
440
|
+
return readCachedRawValue(nonMemoryScope, storageKey);
|
|
317
441
|
}
|
|
318
442
|
}
|
|
319
|
-
|
|
320
|
-
|
|
443
|
+
if (isBiometric) {
|
|
444
|
+
return getStorageModule().getSecureBiometric(storageKey);
|
|
445
|
+
}
|
|
446
|
+
const raw = getStorageModule().get(storageKey, config.scope);
|
|
447
|
+
cacheRawValue(nonMemoryScope, storageKey, raw);
|
|
321
448
|
return raw;
|
|
322
449
|
};
|
|
323
450
|
const writeStoredRaw = rawValue => {
|
|
324
|
-
|
|
451
|
+
if (isBiometric) {
|
|
452
|
+
getStorageModule().setSecureBiometric(storageKey, rawValue);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
cacheRawValue(nonMemoryScope, storageKey, rawValue);
|
|
325
456
|
if (coalesceSecureWrites) {
|
|
326
|
-
scheduleSecureWrite(
|
|
457
|
+
scheduleSecureWrite(storageKey, rawValue);
|
|
327
458
|
return;
|
|
328
459
|
}
|
|
329
460
|
if (nonMemoryScope === _Storage.StorageScope.Secure) {
|
|
330
|
-
clearPendingSecureWrite(
|
|
461
|
+
clearPendingSecureWrite(storageKey);
|
|
462
|
+
getStorageModule().setSecureAccessControl(secureAccessControl ?? secureDefaultAccessControl);
|
|
331
463
|
}
|
|
332
|
-
getStorageModule().set(
|
|
464
|
+
getStorageModule().set(storageKey, rawValue, config.scope);
|
|
333
465
|
};
|
|
334
466
|
const removeStoredRaw = () => {
|
|
335
|
-
|
|
467
|
+
if (isBiometric) {
|
|
468
|
+
getStorageModule().deleteSecureBiometric(storageKey);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
cacheRawValue(nonMemoryScope, storageKey, undefined);
|
|
336
472
|
if (coalesceSecureWrites) {
|
|
337
|
-
scheduleSecureWrite(
|
|
473
|
+
scheduleSecureWrite(storageKey, undefined);
|
|
338
474
|
return;
|
|
339
475
|
}
|
|
340
476
|
if (nonMemoryScope === _Storage.StorageScope.Secure) {
|
|
341
|
-
clearPendingSecureWrite(
|
|
477
|
+
clearPendingSecureWrite(storageKey);
|
|
342
478
|
}
|
|
343
|
-
getStorageModule().remove(
|
|
479
|
+
getStorageModule().remove(storageKey, config.scope);
|
|
344
480
|
};
|
|
345
481
|
const writeValueWithoutValidation = value => {
|
|
346
482
|
if (isMemory) {
|
|
347
483
|
if (memoryExpiration) {
|
|
348
|
-
memoryExpiration.set(
|
|
484
|
+
memoryExpiration.set(storageKey, Date.now() + (expirationTtlMs ?? 0));
|
|
349
485
|
}
|
|
350
|
-
memoryStore.set(
|
|
351
|
-
notifyKeyListeners(memoryListeners,
|
|
486
|
+
memoryStore.set(storageKey, value);
|
|
487
|
+
notifyKeyListeners(memoryListeners, storageKey);
|
|
352
488
|
return;
|
|
353
489
|
}
|
|
354
490
|
const serialized = serialize(value);
|
|
@@ -367,7 +503,7 @@ function createStorageItem(config) {
|
|
|
367
503
|
if (onValidationError) {
|
|
368
504
|
return onValidationError(invalidValue);
|
|
369
505
|
}
|
|
370
|
-
return
|
|
506
|
+
return defaultValue;
|
|
371
507
|
};
|
|
372
508
|
const ensureValidatedValue = (candidate, hadStoredValue) => {
|
|
373
509
|
if (!validate || validate(candidate)) {
|
|
@@ -375,7 +511,7 @@ function createStorageItem(config) {
|
|
|
375
511
|
}
|
|
376
512
|
const resolved = resolveInvalidValue(candidate);
|
|
377
513
|
if (validate && !validate(resolved)) {
|
|
378
|
-
return
|
|
514
|
+
return defaultValue;
|
|
379
515
|
}
|
|
380
516
|
if (hadStoredValue) {
|
|
381
517
|
writeValueWithoutValidation(resolved);
|
|
@@ -384,30 +520,53 @@ function createStorageItem(config) {
|
|
|
384
520
|
};
|
|
385
521
|
const get = () => {
|
|
386
522
|
const raw = readStoredRaw();
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
523
|
+
if (!memoryExpiration && raw === lastRaw && hasLastValue) {
|
|
524
|
+
if (!expiration || lastExpiresAt === null) {
|
|
525
|
+
return lastValue;
|
|
526
|
+
}
|
|
527
|
+
if (typeof lastExpiresAt === "number") {
|
|
528
|
+
if (lastExpiresAt > Date.now()) {
|
|
529
|
+
return lastValue;
|
|
530
|
+
}
|
|
531
|
+
removeStoredRaw();
|
|
532
|
+
invalidateParsedCache();
|
|
533
|
+
onExpired?.(storageKey);
|
|
534
|
+
lastValue = ensureValidatedValue(defaultValue, false);
|
|
535
|
+
hasLastValue = true;
|
|
536
|
+
return lastValue;
|
|
537
|
+
}
|
|
390
538
|
}
|
|
391
539
|
lastRaw = raw;
|
|
392
540
|
if (raw === undefined) {
|
|
393
|
-
|
|
541
|
+
lastExpiresAt = undefined;
|
|
542
|
+
lastValue = ensureValidatedValue(defaultValue, false);
|
|
394
543
|
hasLastValue = true;
|
|
395
544
|
return lastValue;
|
|
396
545
|
}
|
|
397
546
|
if (isMemory) {
|
|
547
|
+
lastExpiresAt = undefined;
|
|
398
548
|
lastValue = ensureValidatedValue(raw, true);
|
|
399
549
|
hasLastValue = true;
|
|
400
550
|
return lastValue;
|
|
401
551
|
}
|
|
552
|
+
if (typeof raw !== "string") {
|
|
553
|
+
lastExpiresAt = undefined;
|
|
554
|
+
lastValue = ensureValidatedValue(defaultValue, false);
|
|
555
|
+
hasLastValue = true;
|
|
556
|
+
return lastValue;
|
|
557
|
+
}
|
|
402
558
|
let deserializableRaw = raw;
|
|
403
559
|
if (expiration) {
|
|
560
|
+
let envelopeExpiresAt = null;
|
|
404
561
|
try {
|
|
405
562
|
const parsed = JSON.parse(raw);
|
|
406
563
|
if ((0, _internal.isStoredEnvelope)(parsed)) {
|
|
564
|
+
envelopeExpiresAt = parsed.expiresAt;
|
|
407
565
|
if (parsed.expiresAt <= Date.now()) {
|
|
408
566
|
removeStoredRaw();
|
|
409
567
|
invalidateParsedCache();
|
|
410
|
-
|
|
568
|
+
onExpired?.(storageKey);
|
|
569
|
+
lastValue = ensureValidatedValue(defaultValue, false);
|
|
411
570
|
hasLastValue = true;
|
|
412
571
|
return lastValue;
|
|
413
572
|
}
|
|
@@ -416,17 +575,19 @@ function createStorageItem(config) {
|
|
|
416
575
|
} catch {
|
|
417
576
|
// Keep backward compatibility with legacy raw values.
|
|
418
577
|
}
|
|
578
|
+
lastExpiresAt = envelopeExpiresAt;
|
|
579
|
+
} else {
|
|
580
|
+
lastExpiresAt = undefined;
|
|
419
581
|
}
|
|
420
582
|
lastValue = ensureValidatedValue(deserialize(deserializableRaw), true);
|
|
421
583
|
hasLastValue = true;
|
|
422
584
|
return lastValue;
|
|
423
585
|
};
|
|
424
586
|
const set = valueOrFn => {
|
|
425
|
-
const
|
|
426
|
-
const newValue = typeof valueOrFn === "function" ? valueOrFn(currentValue) : valueOrFn;
|
|
587
|
+
const newValue = isUpdater(valueOrFn) ? valueOrFn(get()) : valueOrFn;
|
|
427
588
|
invalidateParsedCache();
|
|
428
589
|
if (validate && !validate(newValue)) {
|
|
429
|
-
throw new Error(`Validation failed for key "${
|
|
590
|
+
throw new Error(`Validation failed for key "${storageKey}" in scope "${_Storage.StorageScope[config.scope]}".`);
|
|
430
591
|
}
|
|
431
592
|
writeValueWithoutValidation(newValue);
|
|
432
593
|
};
|
|
@@ -434,14 +595,19 @@ function createStorageItem(config) {
|
|
|
434
595
|
invalidateParsedCache();
|
|
435
596
|
if (isMemory) {
|
|
436
597
|
if (memoryExpiration) {
|
|
437
|
-
memoryExpiration.delete(
|
|
598
|
+
memoryExpiration.delete(storageKey);
|
|
438
599
|
}
|
|
439
|
-
memoryStore.delete(
|
|
440
|
-
notifyKeyListeners(memoryListeners,
|
|
600
|
+
memoryStore.delete(storageKey);
|
|
601
|
+
notifyKeyListeners(memoryListeners, storageKey);
|
|
441
602
|
return;
|
|
442
603
|
}
|
|
443
604
|
removeStoredRaw();
|
|
444
605
|
};
|
|
606
|
+
const hasItem = () => {
|
|
607
|
+
if (isMemory) return memoryStore.has(storageKey);
|
|
608
|
+
if (isBiometric) return getStorageModule().hasSecureBiometric(storageKey);
|
|
609
|
+
return getStorageModule().has(storageKey, config.scope);
|
|
610
|
+
};
|
|
445
611
|
const subscribe = callback => {
|
|
446
612
|
ensureSubscription();
|
|
447
613
|
listeners.add(callback);
|
|
@@ -460,6 +626,7 @@ function createStorageItem(config) {
|
|
|
460
626
|
get,
|
|
461
627
|
set,
|
|
462
628
|
delete: deleteItem,
|
|
629
|
+
has: hasItem,
|
|
463
630
|
subscribe,
|
|
464
631
|
serialize,
|
|
465
632
|
deserialize,
|
|
@@ -470,43 +637,21 @@ function createStorageItem(config) {
|
|
|
470
637
|
_hasValidation: validate !== undefined,
|
|
471
638
|
_hasExpiration: expiration !== undefined,
|
|
472
639
|
_readCacheEnabled: readCache,
|
|
640
|
+
_isBiometric: isBiometric,
|
|
641
|
+
...(secureAccessControl !== undefined ? {
|
|
642
|
+
_secureAccessControl: secureAccessControl
|
|
643
|
+
} : {}),
|
|
473
644
|
scope: config.scope,
|
|
474
|
-
key:
|
|
645
|
+
key: storageKey
|
|
475
646
|
};
|
|
476
647
|
return storageItem;
|
|
477
648
|
}
|
|
478
|
-
function useStorage(item) {
|
|
479
|
-
const value = (0, _react.useSyncExternalStore)(item.subscribe, item.get, item.get);
|
|
480
|
-
return [value, item.set];
|
|
481
|
-
}
|
|
482
|
-
function useStorageSelector(item, selector, isEqual = Object.is) {
|
|
483
|
-
const selectedRef = (0, _react.useRef)({
|
|
484
|
-
hasValue: false
|
|
485
|
-
});
|
|
486
|
-
const getSelectedSnapshot = () => {
|
|
487
|
-
const nextSelected = selector(item.get());
|
|
488
|
-
const current = selectedRef.current;
|
|
489
|
-
if (current.hasValue && isEqual(current.value, nextSelected)) {
|
|
490
|
-
return current.value;
|
|
491
|
-
}
|
|
492
|
-
selectedRef.current = {
|
|
493
|
-
hasValue: true,
|
|
494
|
-
value: nextSelected
|
|
495
|
-
};
|
|
496
|
-
return nextSelected;
|
|
497
|
-
};
|
|
498
|
-
const selectedValue = (0, _react.useSyncExternalStore)(item.subscribe, getSelectedSnapshot, getSelectedSnapshot);
|
|
499
|
-
return [selectedValue, item.set];
|
|
500
|
-
}
|
|
501
|
-
function useSetStorage(item) {
|
|
502
|
-
return item.set;
|
|
503
|
-
}
|
|
504
649
|
function getBatch(items, scope) {
|
|
505
650
|
(0, _internal.assertBatchScope)(items, scope);
|
|
506
651
|
if (scope === _Storage.StorageScope.Memory) {
|
|
507
652
|
return items.map(item => item.get());
|
|
508
653
|
}
|
|
509
|
-
const useRawBatchPath = items.every(item => canUseRawBatchPath(item));
|
|
654
|
+
const useRawBatchPath = items.every(item => scope === _Storage.StorageScope.Secure ? canUseSecureRawBatchPath(item) : canUseRawBatchPath(item));
|
|
510
655
|
if (!useRawBatchPath) {
|
|
511
656
|
return items.map(item => item.get());
|
|
512
657
|
}
|
|
@@ -535,6 +680,9 @@ function getBatch(items, scope) {
|
|
|
535
680
|
fetchedValues.forEach((value, index) => {
|
|
536
681
|
const key = keysToFetch[index];
|
|
537
682
|
const targetIndex = keyIndexes[index];
|
|
683
|
+
if (key === undefined || targetIndex === undefined) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
538
686
|
rawValues[targetIndex] = value;
|
|
539
687
|
cacheRawValue(scope, key, value);
|
|
540
688
|
});
|
|
@@ -556,9 +704,55 @@ function setBatch(items, scope) {
|
|
|
556
704
|
}) => item.set(value));
|
|
557
705
|
return;
|
|
558
706
|
}
|
|
707
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
708
|
+
const secureEntries = items.map(({
|
|
709
|
+
item,
|
|
710
|
+
value
|
|
711
|
+
}) => ({
|
|
712
|
+
item,
|
|
713
|
+
value,
|
|
714
|
+
internal: asInternal(item)
|
|
715
|
+
}));
|
|
716
|
+
const canUseSecureBatchPath = secureEntries.every(({
|
|
717
|
+
internal
|
|
718
|
+
}) => canUseSecureRawBatchPath(internal));
|
|
719
|
+
if (!canUseSecureBatchPath) {
|
|
720
|
+
items.forEach(({
|
|
721
|
+
item,
|
|
722
|
+
value
|
|
723
|
+
}) => item.set(value));
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
flushSecureWrites();
|
|
727
|
+
const storageModule = getStorageModule();
|
|
728
|
+
const groupedByAccessControl = new Map();
|
|
729
|
+
secureEntries.forEach(({
|
|
730
|
+
item,
|
|
731
|
+
value,
|
|
732
|
+
internal
|
|
733
|
+
}) => {
|
|
734
|
+
const accessControl = internal._secureAccessControl ?? secureDefaultAccessControl;
|
|
735
|
+
const existingGroup = groupedByAccessControl.get(accessControl);
|
|
736
|
+
const group = existingGroup ?? {
|
|
737
|
+
keys: [],
|
|
738
|
+
values: []
|
|
739
|
+
};
|
|
740
|
+
group.keys.push(item.key);
|
|
741
|
+
group.values.push(item.serialize(value));
|
|
742
|
+
if (!existingGroup) {
|
|
743
|
+
groupedByAccessControl.set(accessControl, group);
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
groupedByAccessControl.forEach((group, accessControl) => {
|
|
747
|
+
storageModule.setSecureAccessControl(accessControl);
|
|
748
|
+
storageModule.setBatch(group.keys, group.values, scope);
|
|
749
|
+
group.keys.forEach((key, index) => cacheRawValue(scope, key, group.values[index]));
|
|
750
|
+
});
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
559
753
|
const useRawBatchPath = items.every(({
|
|
560
754
|
item
|
|
561
|
-
}) => canUseRawBatchPath(item));
|
|
755
|
+
}) => canUseRawBatchPath(asInternal(item)));
|
|
562
756
|
if (!useRawBatchPath) {
|
|
563
757
|
items.forEach(({
|
|
564
758
|
item,
|
|
@@ -568,9 +762,6 @@ function setBatch(items, scope) {
|
|
|
568
762
|
}
|
|
569
763
|
const keys = items.map(entry => entry.item.key);
|
|
570
764
|
const values = items.map(entry => entry.item.serialize(entry.value));
|
|
571
|
-
if (scope === _Storage.StorageScope.Secure) {
|
|
572
|
-
flushSecureWrites();
|
|
573
|
-
}
|
|
574
765
|
getStorageModule().setBatch(keys, values, scope);
|
|
575
766
|
keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
|
|
576
767
|
}
|
|
@@ -669,4 +860,30 @@ function runTransaction(scope, transaction) {
|
|
|
669
860
|
throw error;
|
|
670
861
|
}
|
|
671
862
|
}
|
|
863
|
+
function createSecureAuthStorage(config, options) {
|
|
864
|
+
const ns = options?.namespace ?? "auth";
|
|
865
|
+
const result = {};
|
|
866
|
+
for (const key of typedKeys(config)) {
|
|
867
|
+
const itemConfig = config[key];
|
|
868
|
+
const expirationConfig = itemConfig.ttlMs !== undefined ? {
|
|
869
|
+
ttlMs: itemConfig.ttlMs
|
|
870
|
+
} : undefined;
|
|
871
|
+
result[key] = createStorageItem({
|
|
872
|
+
key,
|
|
873
|
+
scope: _Storage.StorageScope.Secure,
|
|
874
|
+
defaultValue: "",
|
|
875
|
+
namespace: ns,
|
|
876
|
+
...(itemConfig.biometric !== undefined ? {
|
|
877
|
+
biometric: itemConfig.biometric
|
|
878
|
+
} : {}),
|
|
879
|
+
...(itemConfig.accessControl !== undefined ? {
|
|
880
|
+
accessControl: itemConfig.accessControl
|
|
881
|
+
} : {}),
|
|
882
|
+
...(expirationConfig !== undefined ? {
|
|
883
|
+
expiration: expirationConfig
|
|
884
|
+
} : {})
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
return result;
|
|
888
|
+
}
|
|
672
889
|
//# sourceMappingURL=index.js.map
|