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.
Files changed (53) hide show
  1. package/README.md +594 -247
  2. package/android/CMakeLists.txt +2 -0
  3. package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +102 -11
  4. package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +16 -0
  5. package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +154 -34
  6. package/android/src/main/java/com/nitrostorage/NitroStoragePackage.kt +2 -2
  7. package/cpp/bindings/HybridStorage.cpp +176 -21
  8. package/cpp/bindings/HybridStorage.hpp +29 -2
  9. package/cpp/core/NativeStorageAdapter.hpp +16 -0
  10. package/ios/IOSStorageAdapterCpp.hpp +20 -0
  11. package/ios/IOSStorageAdapterCpp.mm +239 -32
  12. package/lib/commonjs/Storage.types.js +23 -1
  13. package/lib/commonjs/Storage.types.js.map +1 -1
  14. package/lib/commonjs/index.js +292 -75
  15. package/lib/commonjs/index.js.map +1 -1
  16. package/lib/commonjs/index.web.js +473 -86
  17. package/lib/commonjs/index.web.js.map +1 -1
  18. package/lib/commonjs/internal.js +10 -0
  19. package/lib/commonjs/internal.js.map +1 -1
  20. package/lib/commonjs/storage-hooks.js +36 -0
  21. package/lib/commonjs/storage-hooks.js.map +1 -0
  22. package/lib/module/Storage.types.js +22 -0
  23. package/lib/module/Storage.types.js.map +1 -1
  24. package/lib/module/index.js +264 -75
  25. package/lib/module/index.js.map +1 -1
  26. package/lib/module/index.web.js +445 -86
  27. package/lib/module/index.web.js.map +1 -1
  28. package/lib/module/internal.js +8 -0
  29. package/lib/module/internal.js.map +1 -1
  30. package/lib/module/storage-hooks.js +30 -0
  31. package/lib/module/storage-hooks.js.map +1 -0
  32. package/lib/typescript/Storage.nitro.d.ts +12 -0
  33. package/lib/typescript/Storage.nitro.d.ts.map +1 -1
  34. package/lib/typescript/Storage.types.d.ts +20 -0
  35. package/lib/typescript/Storage.types.d.ts.map +1 -1
  36. package/lib/typescript/index.d.ts +33 -10
  37. package/lib/typescript/index.d.ts.map +1 -1
  38. package/lib/typescript/index.web.d.ts +45 -10
  39. package/lib/typescript/index.web.d.ts.map +1 -1
  40. package/lib/typescript/internal.d.ts +2 -0
  41. package/lib/typescript/internal.d.ts.map +1 -1
  42. package/lib/typescript/storage-hooks.d.ts +10 -0
  43. package/lib/typescript/storage-hooks.d.ts.map +1 -0
  44. package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +12 -0
  45. package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +12 -0
  46. package/package.json +8 -3
  47. package/src/Storage.nitro.ts +13 -2
  48. package/src/Storage.types.ts +22 -0
  49. package/src/index.ts +382 -123
  50. package/src/index.web.ts +618 -134
  51. package/src/internal.ts +14 -4
  52. package/src/migration.ts +1 -1
  53. package/src/storage-hooks.ts +48 -0
@@ -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.useSetStorage = useSetStorage;
27
- exports.useStorage = useStorage;
28
- exports.useStorageSelector = useStorageSelector;
29
- var _react = require("react");
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, config.key, listener);
415
+ unsubscribe = addKeyListener(memoryListeners, storageKey, listener);
293
416
  return;
294
417
  }
295
418
  ensureNativeScopeSubscription(nonMemoryScope);
296
- unsubscribe = addKeyListener(getScopedListeners(nonMemoryScope), config.key, listener);
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(config.key);
424
+ const expiresAt = memoryExpiration.get(storageKey);
302
425
  if (expiresAt !== undefined && expiresAt <= Date.now()) {
303
- memoryExpiration.delete(config.key);
304
- memoryStore.delete(config.key);
305
- notifyKeyListeners(memoryListeners, config.key);
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(config.key);
433
+ return memoryStore.get(storageKey);
310
434
  }
311
- if (nonMemoryScope === _Storage.StorageScope.Secure && hasPendingSecureWrite(config.key)) {
312
- return readPendingSecureWrite(config.key);
435
+ if (nonMemoryScope === _Storage.StorageScope.Secure && !isBiometric && hasPendingSecureWrite(storageKey)) {
436
+ return readPendingSecureWrite(storageKey);
313
437
  }
314
438
  if (readCache) {
315
- if (hasCachedRawValue(nonMemoryScope, config.key)) {
316
- return readCachedRawValue(nonMemoryScope, config.key);
439
+ if (hasCachedRawValue(nonMemoryScope, storageKey)) {
440
+ return readCachedRawValue(nonMemoryScope, storageKey);
317
441
  }
318
442
  }
319
- const raw = getStorageModule().get(config.key, config.scope);
320
- cacheRawValue(nonMemoryScope, config.key, raw);
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
- cacheRawValue(nonMemoryScope, config.key, rawValue);
451
+ if (isBiometric) {
452
+ getStorageModule().setSecureBiometric(storageKey, rawValue);
453
+ return;
454
+ }
455
+ cacheRawValue(nonMemoryScope, storageKey, rawValue);
325
456
  if (coalesceSecureWrites) {
326
- scheduleSecureWrite(config.key, rawValue);
457
+ scheduleSecureWrite(storageKey, rawValue);
327
458
  return;
328
459
  }
329
460
  if (nonMemoryScope === _Storage.StorageScope.Secure) {
330
- clearPendingSecureWrite(config.key);
461
+ clearPendingSecureWrite(storageKey);
462
+ getStorageModule().setSecureAccessControl(secureAccessControl ?? secureDefaultAccessControl);
331
463
  }
332
- getStorageModule().set(config.key, rawValue, config.scope);
464
+ getStorageModule().set(storageKey, rawValue, config.scope);
333
465
  };
334
466
  const removeStoredRaw = () => {
335
- cacheRawValue(nonMemoryScope, config.key, undefined);
467
+ if (isBiometric) {
468
+ getStorageModule().deleteSecureBiometric(storageKey);
469
+ return;
470
+ }
471
+ cacheRawValue(nonMemoryScope, storageKey, undefined);
336
472
  if (coalesceSecureWrites) {
337
- scheduleSecureWrite(config.key, undefined);
473
+ scheduleSecureWrite(storageKey, undefined);
338
474
  return;
339
475
  }
340
476
  if (nonMemoryScope === _Storage.StorageScope.Secure) {
341
- clearPendingSecureWrite(config.key);
477
+ clearPendingSecureWrite(storageKey);
342
478
  }
343
- getStorageModule().remove(config.key, config.scope);
479
+ getStorageModule().remove(storageKey, config.scope);
344
480
  };
345
481
  const writeValueWithoutValidation = value => {
346
482
  if (isMemory) {
347
483
  if (memoryExpiration) {
348
- memoryExpiration.set(config.key, Date.now() + (expirationTtlMs ?? 0));
484
+ memoryExpiration.set(storageKey, Date.now() + (expirationTtlMs ?? 0));
349
485
  }
350
- memoryStore.set(config.key, value);
351
- notifyKeyListeners(memoryListeners, config.key);
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 config.defaultValue;
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 config.defaultValue;
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
- const canUseCachedValue = !expiration && !memoryExpiration;
388
- if (canUseCachedValue && raw === lastRaw && hasLastValue) {
389
- return lastValue;
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
- lastValue = ensureValidatedValue(config.defaultValue, false);
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
- lastValue = ensureValidatedValue(config.defaultValue, false);
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 currentValue = get();
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 "${config.key}" in scope "${_Storage.StorageScope[config.scope]}".`);
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(config.key);
598
+ memoryExpiration.delete(storageKey);
438
599
  }
439
- memoryStore.delete(config.key);
440
- notifyKeyListeners(memoryListeners, config.key);
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: config.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