react-native-nitro-storage 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +141 -30
  2. package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +22 -2
  3. package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +3 -0
  4. package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +54 -5
  5. package/cpp/bindings/HybridStorage.cpp +167 -22
  6. package/cpp/bindings/HybridStorage.hpp +12 -1
  7. package/cpp/core/NativeStorageAdapter.hpp +3 -0
  8. package/ios/IOSStorageAdapterCpp.hpp +16 -0
  9. package/ios/IOSStorageAdapterCpp.mm +135 -11
  10. package/lib/commonjs/index.js +466 -275
  11. package/lib/commonjs/index.js.map +1 -1
  12. package/lib/commonjs/index.web.js +564 -270
  13. package/lib/commonjs/index.web.js.map +1 -1
  14. package/lib/commonjs/internal.js +25 -0
  15. package/lib/commonjs/internal.js.map +1 -1
  16. package/lib/module/index.js +466 -277
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/module/index.web.js +564 -272
  19. package/lib/module/index.web.js.map +1 -1
  20. package/lib/module/internal.js +24 -0
  21. package/lib/module/internal.js.map +1 -1
  22. package/lib/typescript/Storage.nitro.d.ts +2 -0
  23. package/lib/typescript/Storage.nitro.d.ts.map +1 -1
  24. package/lib/typescript/index.d.ts +38 -1
  25. package/lib/typescript/index.d.ts.map +1 -1
  26. package/lib/typescript/index.web.d.ts +40 -1
  27. package/lib/typescript/index.web.d.ts.map +1 -1
  28. package/lib/typescript/internal.d.ts +1 -0
  29. package/lib/typescript/internal.d.ts.map +1 -1
  30. package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +2 -0
  31. package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +2 -0
  32. package/package.json +1 -1
  33. package/src/Storage.nitro.ts +2 -0
  34. package/src/index.ts +616 -296
  35. package/src/index.web.ts +728 -288
  36. package/src/internal.ts +28 -0
@@ -24,6 +24,7 @@ Object.defineProperty(exports, "StorageScope", {
24
24
  exports.createSecureAuthStorage = createSecureAuthStorage;
25
25
  exports.createStorageItem = createStorageItem;
26
26
  exports.getBatch = getBatch;
27
+ exports.getWebSecureStorageBackend = getWebSecureStorageBackend;
27
28
  Object.defineProperty(exports, "migrateFromMMKV", {
28
29
  enumerable: true,
29
30
  get: function () {
@@ -35,6 +36,7 @@ exports.registerMigration = registerMigration;
35
36
  exports.removeBatch = removeBatch;
36
37
  exports.runTransaction = runTransaction;
37
38
  exports.setBatch = setBatch;
39
+ exports.setWebSecureStorageBackend = setWebSecureStorageBackend;
38
40
  exports.storage = void 0;
39
41
  Object.defineProperty(exports, "useSetStorage", {
40
42
  enumerable: true,
@@ -82,12 +84,76 @@ let secureFlushScheduled = false;
82
84
  const SECURE_WEB_PREFIX = "__secure_";
83
85
  const BIOMETRIC_WEB_PREFIX = "__bio_";
84
86
  let hasWarnedAboutWebBiometricFallback = false;
87
+ let hasWebStorageEventSubscription = false;
88
+ let metricsObserver;
89
+ const metricsCounters = new Map();
90
+ function recordMetric(operation, scope, durationMs, keysCount = 1) {
91
+ const existing = metricsCounters.get(operation);
92
+ if (!existing) {
93
+ metricsCounters.set(operation, {
94
+ count: 1,
95
+ totalDurationMs: durationMs,
96
+ maxDurationMs: durationMs
97
+ });
98
+ } else {
99
+ existing.count += 1;
100
+ existing.totalDurationMs += durationMs;
101
+ existing.maxDurationMs = Math.max(existing.maxDurationMs, durationMs);
102
+ }
103
+ metricsObserver?.({
104
+ operation,
105
+ scope,
106
+ durationMs,
107
+ keysCount
108
+ });
109
+ }
110
+ function measureOperation(operation, scope, fn, keysCount = 1) {
111
+ const start = Date.now();
112
+ try {
113
+ return fn();
114
+ } finally {
115
+ recordMetric(operation, scope, Date.now() - start, keysCount);
116
+ }
117
+ }
118
+ function createLocalStorageWebSecureBackend() {
119
+ return {
120
+ getItem: key => globalThis.localStorage?.getItem(key) ?? null,
121
+ setItem: (key, value) => globalThis.localStorage?.setItem(key, value),
122
+ removeItem: key => globalThis.localStorage?.removeItem(key),
123
+ clear: () => globalThis.localStorage?.clear(),
124
+ getAllKeys: () => {
125
+ const storage = globalThis.localStorage;
126
+ if (!storage) return [];
127
+ const keys = [];
128
+ for (let index = 0; index < storage.length; index += 1) {
129
+ const key = storage.key(index);
130
+ if (key) {
131
+ keys.push(key);
132
+ }
133
+ }
134
+ return keys;
135
+ }
136
+ };
137
+ }
138
+ let webSecureStorageBackend = createLocalStorageWebSecureBackend();
85
139
  function getBrowserStorage(scope) {
86
140
  if (scope === _Storage.StorageScope.Disk) {
87
141
  return globalThis.localStorage;
88
142
  }
89
143
  if (scope === _Storage.StorageScope.Secure) {
90
- return globalThis.localStorage;
144
+ if (!webSecureStorageBackend) {
145
+ return undefined;
146
+ }
147
+ return {
148
+ setItem: (key, value) => webSecureStorageBackend?.setItem(key, value),
149
+ getItem: key => webSecureStorageBackend?.getItem(key) ?? null,
150
+ removeItem: key => webSecureStorageBackend?.removeItem(key),
151
+ clear: () => webSecureStorageBackend?.clear(),
152
+ key: index => webSecureStorageBackend?.getAllKeys()[index] ?? null,
153
+ get length() {
154
+ return webSecureStorageBackend?.getAllKeys().length ?? 0;
155
+ }
156
+ };
91
157
  }
92
158
  return undefined;
93
159
  }
@@ -140,6 +206,62 @@ function ensureWebScopeKeyIndex(scope) {
140
206
  hydrateWebScopeKeyIndex(scope);
141
207
  return getWebScopeKeyIndex(scope);
142
208
  }
209
+ function handleWebStorageEvent(event) {
210
+ const key = event.key;
211
+ if (key === null) {
212
+ clearScopeRawCache(_Storage.StorageScope.Disk);
213
+ clearScopeRawCache(_Storage.StorageScope.Secure);
214
+ ensureWebScopeKeyIndex(_Storage.StorageScope.Disk).clear();
215
+ ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).clear();
216
+ notifyAllListeners(getScopedListeners(_Storage.StorageScope.Disk));
217
+ notifyAllListeners(getScopedListeners(_Storage.StorageScope.Secure));
218
+ return;
219
+ }
220
+ if (key.startsWith(SECURE_WEB_PREFIX)) {
221
+ const plainKey = fromSecureStorageKey(key);
222
+ if (event.newValue === null) {
223
+ ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).delete(plainKey);
224
+ cacheRawValue(_Storage.StorageScope.Secure, plainKey, undefined);
225
+ } else {
226
+ ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).add(plainKey);
227
+ cacheRawValue(_Storage.StorageScope.Secure, plainKey, event.newValue);
228
+ }
229
+ notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), plainKey);
230
+ return;
231
+ }
232
+ if (key.startsWith(BIOMETRIC_WEB_PREFIX)) {
233
+ const plainKey = fromBiometricStorageKey(key);
234
+ if (event.newValue === null) {
235
+ if (getBrowserStorage(_Storage.StorageScope.Secure)?.getItem(toSecureStorageKey(plainKey)) === null) {
236
+ ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).delete(plainKey);
237
+ }
238
+ cacheRawValue(_Storage.StorageScope.Secure, plainKey, undefined);
239
+ } else {
240
+ ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).add(plainKey);
241
+ cacheRawValue(_Storage.StorageScope.Secure, plainKey, event.newValue);
242
+ }
243
+ notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), plainKey);
244
+ return;
245
+ }
246
+ if (event.newValue === null) {
247
+ ensureWebScopeKeyIndex(_Storage.StorageScope.Disk).delete(key);
248
+ cacheRawValue(_Storage.StorageScope.Disk, key, undefined);
249
+ } else {
250
+ ensureWebScopeKeyIndex(_Storage.StorageScope.Disk).add(key);
251
+ cacheRawValue(_Storage.StorageScope.Disk, key, event.newValue);
252
+ }
253
+ notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Disk), key);
254
+ }
255
+ function ensureWebStorageEventSubscription() {
256
+ if (hasWebStorageEventSubscription) {
257
+ return;
258
+ }
259
+ if (typeof window === "undefined" || typeof window.addEventListener !== "function") {
260
+ return;
261
+ }
262
+ window.addEventListener("storage", handleWebStorageEvent);
263
+ hasWebStorageEventSubscription = true;
264
+ }
143
265
  function getScopedListeners(scope) {
144
266
  return webScopeListeners.get(scope);
145
267
  }
@@ -200,32 +322,46 @@ function flushSecureWrites() {
200
322
  }
201
323
  const writes = Array.from(pendingSecureWrites.values());
202
324
  pendingSecureWrites.clear();
203
- const keysToSet = [];
204
- const valuesToSet = [];
325
+ const groupedSetWrites = new Map();
205
326
  const keysToRemove = [];
206
327
  writes.forEach(({
207
328
  key,
208
- value
329
+ value,
330
+ accessControl
209
331
  }) => {
210
332
  if (value === undefined) {
211
333
  keysToRemove.push(key);
212
334
  } else {
213
- keysToSet.push(key);
214
- valuesToSet.push(value);
335
+ const resolvedAccessControl = accessControl ?? _Storage.AccessControl.WhenUnlocked;
336
+ const existingGroup = groupedSetWrites.get(resolvedAccessControl);
337
+ const group = existingGroup ?? {
338
+ keys: [],
339
+ values: []
340
+ };
341
+ group.keys.push(key);
342
+ group.values.push(value);
343
+ if (!existingGroup) {
344
+ groupedSetWrites.set(resolvedAccessControl, group);
345
+ }
215
346
  }
216
347
  });
217
- if (keysToSet.length > 0) {
218
- WebStorage.setBatch(keysToSet, valuesToSet, _Storage.StorageScope.Secure);
219
- }
348
+ groupedSetWrites.forEach((group, accessControl) => {
349
+ WebStorage.setSecureAccessControl(accessControl);
350
+ WebStorage.setBatch(group.keys, group.values, _Storage.StorageScope.Secure);
351
+ });
220
352
  if (keysToRemove.length > 0) {
221
353
  WebStorage.removeBatch(keysToRemove, _Storage.StorageScope.Secure);
222
354
  }
223
355
  }
224
- function scheduleSecureWrite(key, value) {
225
- pendingSecureWrites.set(key, {
356
+ function scheduleSecureWrite(key, value, accessControl) {
357
+ const pendingWrite = {
226
358
  key,
227
359
  value
228
- });
360
+ };
361
+ if (accessControl !== undefined) {
362
+ pendingWrite.accessControl = accessControl;
363
+ }
364
+ pendingSecureWrites.set(key, pendingWrite);
229
365
  if (secureFlushScheduled) {
230
366
  return;
231
367
  }
@@ -376,6 +512,12 @@ const WebStorage = {
376
512
  }
377
513
  return Array.from(ensureWebScopeKeyIndex(scope));
378
514
  },
515
+ getKeysByPrefix: (prefix, scope) => {
516
+ if (scope !== _Storage.StorageScope.Disk && scope !== _Storage.StorageScope.Secure) {
517
+ return [];
518
+ }
519
+ return Array.from(ensureWebScopeKeyIndex(scope)).filter(key => key.startsWith(prefix));
520
+ },
379
521
  size: scope => {
380
522
  if (scope === _Storage.StorageScope.Disk || scope === _Storage.StorageScope.Secure) {
381
523
  return ensureWebScopeKeyIndex(scope).size;
@@ -386,19 +528,22 @@ const WebStorage = {
386
528
  setSecureWritesAsync: _enabled => {},
387
529
  setKeychainAccessGroup: () => {},
388
530
  setSecureBiometric: (key, value) => {
531
+ WebStorage.setSecureBiometricWithLevel(key, value, _Storage.BiometricLevel.BiometryOnly);
532
+ },
533
+ setSecureBiometricWithLevel: (key, value, _level) => {
389
534
  if (typeof __DEV__ !== "undefined" && __DEV__ && !hasWarnedAboutWebBiometricFallback) {
390
535
  hasWarnedAboutWebBiometricFallback = true;
391
536
  console.warn("[NitroStorage] Biometric storage is not supported on web. Using localStorage.");
392
537
  }
393
- globalThis.localStorage?.setItem(toBiometricStorageKey(key), value);
538
+ getBrowserStorage(_Storage.StorageScope.Secure)?.setItem(toBiometricStorageKey(key), value);
394
539
  ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).add(key);
395
540
  notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), key);
396
541
  },
397
542
  getSecureBiometric: key => {
398
- return globalThis.localStorage?.getItem(toBiometricStorageKey(key)) ?? undefined;
543
+ return getBrowserStorage(_Storage.StorageScope.Secure)?.getItem(toBiometricStorageKey(key)) ?? undefined;
399
544
  },
400
545
  deleteSecureBiometric: key => {
401
- const storage = globalThis.localStorage;
546
+ const storage = getBrowserStorage(_Storage.StorageScope.Secure);
402
547
  storage?.removeItem(toBiometricStorageKey(key));
403
548
  if (storage?.getItem(toSecureStorageKey(key)) === null) {
404
549
  ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).delete(key);
@@ -406,10 +551,10 @@ const WebStorage = {
406
551
  notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), key);
407
552
  },
408
553
  hasSecureBiometric: key => {
409
- return globalThis.localStorage?.getItem(toBiometricStorageKey(key)) !== null;
554
+ return getBrowserStorage(_Storage.StorageScope.Secure)?.getItem(toBiometricStorageKey(key)) !== null;
410
555
  },
411
556
  clearSecureBiometric: () => {
412
- const storage = globalThis.localStorage;
557
+ const storage = getBrowserStorage(_Storage.StorageScope.Secure);
413
558
  if (!storage) return;
414
559
  const keysToNotify = [];
415
560
  const toRemove = [];
@@ -483,82 +628,167 @@ function writeMigrationVersion(scope, version) {
483
628
  }
484
629
  const storage = exports.storage = {
485
630
  clear: scope => {
486
- if (scope === _Storage.StorageScope.Memory) {
487
- memoryStore.clear();
488
- notifyAllListeners(memoryListeners);
489
- return;
490
- }
491
- if (scope === _Storage.StorageScope.Secure) {
492
- flushSecureWrites();
493
- pendingSecureWrites.clear();
494
- }
495
- clearScopeRawCache(scope);
496
- WebStorage.clear(scope);
631
+ measureOperation("storage:clear", scope, () => {
632
+ if (scope === _Storage.StorageScope.Memory) {
633
+ memoryStore.clear();
634
+ notifyAllListeners(memoryListeners);
635
+ return;
636
+ }
637
+ if (scope === _Storage.StorageScope.Secure) {
638
+ flushSecureWrites();
639
+ pendingSecureWrites.clear();
640
+ }
641
+ clearScopeRawCache(scope);
642
+ WebStorage.clear(scope);
643
+ });
497
644
  },
498
645
  clearAll: () => {
499
- storage.clear(_Storage.StorageScope.Memory);
500
- storage.clear(_Storage.StorageScope.Disk);
501
- storage.clear(_Storage.StorageScope.Secure);
646
+ measureOperation("storage:clearAll", _Storage.StorageScope.Memory, () => {
647
+ storage.clear(_Storage.StorageScope.Memory);
648
+ storage.clear(_Storage.StorageScope.Disk);
649
+ storage.clear(_Storage.StorageScope.Secure);
650
+ }, 3);
502
651
  },
503
652
  clearNamespace: (namespace, scope) => {
504
- (0, _internal.assertValidScope)(scope);
505
- if (scope === _Storage.StorageScope.Memory) {
506
- for (const key of memoryStore.keys()) {
507
- if ((0, _internal.isNamespaced)(key, namespace)) {
508
- memoryStore.delete(key);
653
+ measureOperation("storage:clearNamespace", scope, () => {
654
+ (0, _internal.assertValidScope)(scope);
655
+ if (scope === _Storage.StorageScope.Memory) {
656
+ for (const key of memoryStore.keys()) {
657
+ if ((0, _internal.isNamespaced)(key, namespace)) {
658
+ memoryStore.delete(key);
659
+ }
509
660
  }
661
+ notifyAllListeners(memoryListeners);
662
+ return;
510
663
  }
511
- notifyAllListeners(memoryListeners);
512
- return;
513
- }
514
- const keyPrefix = (0, _internal.prefixKey)(namespace, "");
515
- if (scope === _Storage.StorageScope.Secure) {
516
- flushSecureWrites();
517
- }
518
- clearScopeRawCache(scope);
519
- WebStorage.removeByPrefix(keyPrefix, scope);
664
+ const keyPrefix = (0, _internal.prefixKey)(namespace, "");
665
+ if (scope === _Storage.StorageScope.Secure) {
666
+ flushSecureWrites();
667
+ }
668
+ clearScopeRawCache(scope);
669
+ WebStorage.removeByPrefix(keyPrefix, scope);
670
+ });
520
671
  },
521
672
  clearBiometric: () => {
522
- WebStorage.clearSecureBiometric();
673
+ measureOperation("storage:clearBiometric", _Storage.StorageScope.Secure, () => {
674
+ WebStorage.clearSecureBiometric();
675
+ });
523
676
  },
524
677
  has: (key, scope) => {
525
- (0, _internal.assertValidScope)(scope);
526
- if (scope === _Storage.StorageScope.Memory) return memoryStore.has(key);
527
- return WebStorage.has(key, scope);
678
+ return measureOperation("storage:has", scope, () => {
679
+ (0, _internal.assertValidScope)(scope);
680
+ if (scope === _Storage.StorageScope.Memory) return memoryStore.has(key);
681
+ return WebStorage.has(key, scope);
682
+ });
528
683
  },
529
684
  getAllKeys: scope => {
530
- (0, _internal.assertValidScope)(scope);
531
- if (scope === _Storage.StorageScope.Memory) return Array.from(memoryStore.keys());
532
- return WebStorage.getAllKeys(scope);
685
+ return measureOperation("storage:getAllKeys", scope, () => {
686
+ (0, _internal.assertValidScope)(scope);
687
+ if (scope === _Storage.StorageScope.Memory) return Array.from(memoryStore.keys());
688
+ return WebStorage.getAllKeys(scope);
689
+ });
690
+ },
691
+ getKeysByPrefix: (prefix, scope) => {
692
+ return measureOperation("storage:getKeysByPrefix", scope, () => {
693
+ (0, _internal.assertValidScope)(scope);
694
+ if (scope === _Storage.StorageScope.Memory) {
695
+ return Array.from(memoryStore.keys()).filter(key => key.startsWith(prefix));
696
+ }
697
+ return WebStorage.getKeysByPrefix(prefix, scope);
698
+ });
699
+ },
700
+ getByPrefix: (prefix, scope) => {
701
+ return measureOperation("storage:getByPrefix", scope, () => {
702
+ const result = {};
703
+ const keys = storage.getKeysByPrefix(prefix, scope);
704
+ if (keys.length === 0) {
705
+ return result;
706
+ }
707
+ if (scope === _Storage.StorageScope.Memory) {
708
+ keys.forEach(key => {
709
+ const value = memoryStore.get(key);
710
+ if (typeof value === "string") {
711
+ result[key] = value;
712
+ }
713
+ });
714
+ return result;
715
+ }
716
+ const values = WebStorage.getBatch(keys, scope);
717
+ keys.forEach((key, index) => {
718
+ const value = values[index];
719
+ if (value !== undefined) {
720
+ result[key] = value;
721
+ }
722
+ });
723
+ return result;
724
+ });
533
725
  },
534
726
  getAll: scope => {
535
- (0, _internal.assertValidScope)(scope);
536
- const result = {};
537
- if (scope === _Storage.StorageScope.Memory) {
538
- memoryStore.forEach((value, key) => {
539
- if (typeof value === "string") result[key] = value;
727
+ return measureOperation("storage:getAll", scope, () => {
728
+ (0, _internal.assertValidScope)(scope);
729
+ const result = {};
730
+ if (scope === _Storage.StorageScope.Memory) {
731
+ memoryStore.forEach((value, key) => {
732
+ if (typeof value === "string") result[key] = value;
733
+ });
734
+ return result;
735
+ }
736
+ const keys = WebStorage.getAllKeys(scope);
737
+ keys.forEach(key => {
738
+ const val = WebStorage.get(key, scope);
739
+ if (val !== undefined) result[key] = val;
540
740
  });
541
741
  return result;
542
- }
543
- const keys = WebStorage.getAllKeys(scope);
544
- keys.forEach(key => {
545
- const val = WebStorage.get(key, scope);
546
- if (val !== undefined) result[key] = val;
547
742
  });
548
- return result;
549
743
  },
550
744
  size: scope => {
551
- (0, _internal.assertValidScope)(scope);
552
- if (scope === _Storage.StorageScope.Memory) return memoryStore.size;
553
- return WebStorage.size(scope);
745
+ return measureOperation("storage:size", scope, () => {
746
+ (0, _internal.assertValidScope)(scope);
747
+ if (scope === _Storage.StorageScope.Memory) return memoryStore.size;
748
+ return WebStorage.size(scope);
749
+ });
750
+ },
751
+ setAccessControl: _level => {
752
+ recordMetric("storage:setAccessControl", _Storage.StorageScope.Secure, 0);
753
+ },
754
+ setSecureWritesAsync: _enabled => {
755
+ recordMetric("storage:setSecureWritesAsync", _Storage.StorageScope.Secure, 0);
554
756
  },
555
- setAccessControl: _level => {},
556
- setSecureWritesAsync: _enabled => {},
557
757
  flushSecureWrites: () => {
558
- flushSecureWrites();
758
+ measureOperation("storage:flushSecureWrites", _Storage.StorageScope.Secure, () => {
759
+ flushSecureWrites();
760
+ });
761
+ },
762
+ setKeychainAccessGroup: _group => {
763
+ recordMetric("storage:setKeychainAccessGroup", _Storage.StorageScope.Secure, 0);
764
+ },
765
+ setMetricsObserver: observer => {
766
+ metricsObserver = observer;
767
+ },
768
+ getMetricsSnapshot: () => {
769
+ const snapshot = {};
770
+ metricsCounters.forEach((value, key) => {
771
+ snapshot[key] = {
772
+ count: value.count,
773
+ totalDurationMs: value.totalDurationMs,
774
+ avgDurationMs: value.count === 0 ? 0 : value.totalDurationMs / value.count,
775
+ maxDurationMs: value.maxDurationMs
776
+ };
777
+ });
778
+ return snapshot;
559
779
  },
560
- setKeychainAccessGroup: _group => {}
780
+ resetMetrics: () => {
781
+ metricsCounters.clear();
782
+ }
561
783
  };
784
+ function setWebSecureStorageBackend(backend) {
785
+ webSecureStorageBackend = backend ?? createLocalStorageWebSecureBackend();
786
+ hydratedWebScopeKeyIndex.delete(_Storage.StorageScope.Secure);
787
+ clearScopeRawCache(_Storage.StorageScope.Secure);
788
+ }
789
+ function getWebSecureStorageBackend() {
790
+ return webSecureStorageBackend;
791
+ }
562
792
  function canUseRawBatchPath(item) {
563
793
  return item._hasExpiration === false && item._hasValidation === false && item._isBiometric !== true && item._secureAccessControl === undefined;
564
794
  }
@@ -576,7 +806,8 @@ function createStorageItem(config) {
576
806
  const serialize = config.serialize ?? defaultSerialize;
577
807
  const deserialize = config.deserialize ?? defaultDeserialize;
578
808
  const isMemory = config.scope === _Storage.StorageScope.Memory;
579
- const isBiometric = config.biometric === true && config.scope === _Storage.StorageScope.Secure;
809
+ const resolvedBiometricLevel = config.scope === _Storage.StorageScope.Secure ? config.biometricLevel ?? (config.biometric === true ? _Storage.BiometricLevel.BiometryOnly : _Storage.BiometricLevel.None) : _Storage.BiometricLevel.None;
810
+ const isBiometric = resolvedBiometricLevel !== _Storage.BiometricLevel.None;
580
811
  const secureAccessControl = config.accessControl;
581
812
  const validate = config.validate;
582
813
  const onValidationError = config.onValidationError;
@@ -585,7 +816,7 @@ function createStorageItem(config) {
585
816
  const expirationTtlMs = expiration?.ttlMs;
586
817
  const memoryExpiration = expiration && isMemory ? new Map() : null;
587
818
  const readCache = !isMemory && config.readCache === true;
588
- const coalesceSecureWrites = config.scope === _Storage.StorageScope.Secure && config.coalesceSecureWrites === true && !isBiometric && secureAccessControl === undefined;
819
+ const coalesceSecureWrites = config.scope === _Storage.StorageScope.Secure && config.coalesceSecureWrites === true && !isBiometric;
589
820
  const defaultValue = config.defaultValue;
590
821
  const nonMemoryScope = config.scope === _Storage.StorageScope.Disk ? _Storage.StorageScope.Disk : config.scope === _Storage.StorageScope.Secure ? _Storage.StorageScope.Secure : null;
591
822
  if (expiration && expiration.ttlMs <= 0) {
@@ -615,6 +846,7 @@ function createStorageItem(config) {
615
846
  unsubscribe = addKeyListener(memoryListeners, storageKey, listener);
616
847
  return;
617
848
  }
849
+ ensureWebStorageEventSubscription();
618
850
  unsubscribe = addKeyListener(getScopedListeners(nonMemoryScope), storageKey, listener);
619
851
  };
620
852
  const readStoredRaw = () => {
@@ -648,12 +880,12 @@ function createStorageItem(config) {
648
880
  };
649
881
  const writeStoredRaw = rawValue => {
650
882
  if (isBiometric) {
651
- WebStorage.setSecureBiometric(storageKey, rawValue);
883
+ WebStorage.setSecureBiometricWithLevel(storageKey, rawValue, resolvedBiometricLevel);
652
884
  return;
653
885
  }
654
886
  cacheRawValue(nonMemoryScope, storageKey, rawValue);
655
887
  if (coalesceSecureWrites) {
656
- scheduleSecureWrite(storageKey, rawValue);
888
+ scheduleSecureWrite(storageKey, rawValue, secureAccessControl ?? _Storage.AccessControl.WhenUnlocked);
657
889
  return;
658
890
  }
659
891
  if (nonMemoryScope === _Storage.StorageScope.Secure) {
@@ -668,7 +900,7 @@ function createStorageItem(config) {
668
900
  }
669
901
  cacheRawValue(nonMemoryScope, storageKey, undefined);
670
902
  if (coalesceSecureWrites) {
671
- scheduleSecureWrite(storageKey, undefined);
903
+ scheduleSecureWrite(storageKey, undefined, secureAccessControl ?? _Storage.AccessControl.WhenUnlocked);
672
904
  return;
673
905
  }
674
906
  if (nonMemoryScope === _Storage.StorageScope.Secure) {
@@ -716,7 +948,7 @@ function createStorageItem(config) {
716
948
  }
717
949
  return resolved;
718
950
  };
719
- const get = () => {
951
+ const getInternal = () => {
720
952
  const raw = readStoredRaw();
721
953
  if (!memoryExpiration && raw === lastRaw && hasLastValue) {
722
954
  if (!expiration || lastExpiresAt === null) {
@@ -781,31 +1013,52 @@ function createStorageItem(config) {
781
1013
  hasLastValue = true;
782
1014
  return lastValue;
783
1015
  };
1016
+ const getCurrentVersion = () => {
1017
+ const raw = readStoredRaw();
1018
+ return (0, _internal.toVersionToken)(raw);
1019
+ };
1020
+ const get = () => measureOperation("item:get", config.scope, () => getInternal());
1021
+ const getWithVersion = () => measureOperation("item:getWithVersion", config.scope, () => ({
1022
+ value: getInternal(),
1023
+ version: getCurrentVersion()
1024
+ }));
784
1025
  const set = valueOrFn => {
785
- const newValue = isUpdater(valueOrFn) ? valueOrFn(get()) : valueOrFn;
786
- invalidateParsedCache();
787
- if (validate && !validate(newValue)) {
788
- throw new Error(`Validation failed for key "${storageKey}" in scope "${_Storage.StorageScope[config.scope]}".`);
789
- }
790
- writeValueWithoutValidation(newValue);
1026
+ measureOperation("item:set", config.scope, () => {
1027
+ const newValue = isUpdater(valueOrFn) ? valueOrFn(getInternal()) : valueOrFn;
1028
+ invalidateParsedCache();
1029
+ if (validate && !validate(newValue)) {
1030
+ throw new Error(`Validation failed for key "${storageKey}" in scope "${_Storage.StorageScope[config.scope]}".`);
1031
+ }
1032
+ writeValueWithoutValidation(newValue);
1033
+ });
791
1034
  };
1035
+ const setIfVersion = (version, valueOrFn) => measureOperation("item:setIfVersion", config.scope, () => {
1036
+ const currentVersion = getCurrentVersion();
1037
+ if (currentVersion !== version) {
1038
+ return false;
1039
+ }
1040
+ set(valueOrFn);
1041
+ return true;
1042
+ });
792
1043
  const deleteItem = () => {
793
- invalidateParsedCache();
794
- if (isMemory) {
795
- if (memoryExpiration) {
796
- memoryExpiration.delete(storageKey);
1044
+ measureOperation("item:delete", config.scope, () => {
1045
+ invalidateParsedCache();
1046
+ if (isMemory) {
1047
+ if (memoryExpiration) {
1048
+ memoryExpiration.delete(storageKey);
1049
+ }
1050
+ memoryStore.delete(storageKey);
1051
+ notifyKeyListeners(memoryListeners, storageKey);
1052
+ return;
797
1053
  }
798
- memoryStore.delete(storageKey);
799
- notifyKeyListeners(memoryListeners, storageKey);
800
- return;
801
- }
802
- removeStoredRaw();
1054
+ removeStoredRaw();
1055
+ });
803
1056
  };
804
- const hasItem = () => {
1057
+ const hasItem = () => measureOperation("item:has", config.scope, () => {
805
1058
  if (isMemory) return memoryStore.has(storageKey);
806
1059
  if (isBiometric) return WebStorage.hasSecureBiometric(storageKey);
807
1060
  return WebStorage.has(storageKey, config.scope);
808
- };
1061
+ });
809
1062
  const subscribe = callback => {
810
1063
  ensureSubscription();
811
1064
  listeners.add(callback);
@@ -819,7 +1072,9 @@ function createStorageItem(config) {
819
1072
  };
820
1073
  const storageItem = {
821
1074
  get,
1075
+ getWithVersion,
822
1076
  set,
1077
+ setIfVersion,
823
1078
  delete: deleteItem,
824
1079
  has: hasItem,
825
1080
  subscribe,
@@ -833,6 +1088,7 @@ function createStorageItem(config) {
833
1088
  _hasExpiration: expiration !== undefined,
834
1089
  _readCacheEnabled: readCache,
835
1090
  _isBiometric: isBiometric,
1091
+ _defaultValue: defaultValue,
836
1092
  ...(secureAccessControl !== undefined ? {
837
1093
  _secureAccessControl: secureAccessControl
838
1094
  } : {}),
@@ -842,135 +1098,140 @@ function createStorageItem(config) {
842
1098
  return storageItem;
843
1099
  }
844
1100
  function getBatch(items, scope) {
845
- (0, _internal.assertBatchScope)(items, scope);
846
- if (scope === _Storage.StorageScope.Memory) {
847
- return items.map(item => item.get());
848
- }
849
- const useRawBatchPath = items.every(item => scope === _Storage.StorageScope.Secure ? canUseSecureRawBatchPath(item) : canUseRawBatchPath(item));
850
- if (!useRawBatchPath) {
851
- return items.map(item => item.get());
852
- }
853
- const useBatchCache = items.every(item => item._readCacheEnabled === true);
854
- const rawValues = new Array(items.length);
855
- const keysToFetch = [];
856
- const keyIndexes = [];
857
- items.forEach((item, index) => {
858
- if (scope === _Storage.StorageScope.Secure) {
859
- if (hasPendingSecureWrite(item.key)) {
860
- rawValues[index] = readPendingSecureWrite(item.key);
861
- return;
862
- }
1101
+ return measureOperation("batch:get", scope, () => {
1102
+ (0, _internal.assertBatchScope)(items, scope);
1103
+ if (scope === _Storage.StorageScope.Memory) {
1104
+ return items.map(item => item.get());
863
1105
  }
864
- if (useBatchCache) {
865
- if (hasCachedRawValue(scope, item.key)) {
866
- rawValues[index] = readCachedRawValue(scope, item.key);
867
- return;
868
- }
1106
+ const useRawBatchPath = items.every(item => scope === _Storage.StorageScope.Secure ? canUseSecureRawBatchPath(item) : canUseRawBatchPath(item));
1107
+ if (!useRawBatchPath) {
1108
+ return items.map(item => item.get());
869
1109
  }
870
- keysToFetch.push(item.key);
871
- keyIndexes.push(index);
872
- });
873
- if (keysToFetch.length > 0) {
874
- const fetchedValues = WebStorage.getBatch(keysToFetch, scope);
875
- fetchedValues.forEach((value, index) => {
876
- const key = keysToFetch[index];
877
- const targetIndex = keyIndexes[index];
878
- if (key === undefined || targetIndex === undefined) {
879
- return;
1110
+ const rawValues = new Array(items.length);
1111
+ const keysToFetch = [];
1112
+ const keyIndexes = [];
1113
+ items.forEach((item, index) => {
1114
+ if (scope === _Storage.StorageScope.Secure) {
1115
+ if (hasPendingSecureWrite(item.key)) {
1116
+ rawValues[index] = readPendingSecureWrite(item.key);
1117
+ return;
1118
+ }
1119
+ }
1120
+ if (item._readCacheEnabled === true) {
1121
+ if (hasCachedRawValue(scope, item.key)) {
1122
+ rawValues[index] = readCachedRawValue(scope, item.key);
1123
+ return;
1124
+ }
880
1125
  }
881
- rawValues[targetIndex] = value;
882
- cacheRawValue(scope, key, value);
1126
+ keysToFetch.push(item.key);
1127
+ keyIndexes.push(index);
883
1128
  });
884
- }
885
- return items.map((item, index) => {
886
- const raw = rawValues[index];
887
- if (raw === undefined) {
888
- return item.get();
1129
+ if (keysToFetch.length > 0) {
1130
+ const fetchedValues = WebStorage.getBatch(keysToFetch, scope);
1131
+ fetchedValues.forEach((value, index) => {
1132
+ const key = keysToFetch[index];
1133
+ const targetIndex = keyIndexes[index];
1134
+ if (key === undefined || targetIndex === undefined) {
1135
+ return;
1136
+ }
1137
+ rawValues[targetIndex] = value;
1138
+ cacheRawValue(scope, key, value);
1139
+ });
889
1140
  }
890
- return item.deserialize(raw);
891
- });
1141
+ return items.map((item, index) => {
1142
+ const raw = rawValues[index];
1143
+ if (raw === undefined) {
1144
+ return asInternal(item)._defaultValue;
1145
+ }
1146
+ return item.deserialize(raw);
1147
+ });
1148
+ }, items.length);
892
1149
  }
893
1150
  function setBatch(items, scope) {
894
- (0, _internal.assertBatchScope)(items.map(batchEntry => batchEntry.item), scope);
895
- if (scope === _Storage.StorageScope.Memory) {
896
- items.forEach(({
897
- item,
898
- value
899
- }) => item.set(value));
900
- return;
901
- }
902
- if (scope === _Storage.StorageScope.Secure) {
903
- const secureEntries = items.map(({
904
- item,
905
- value
906
- }) => ({
907
- item,
908
- value,
909
- internal: asInternal(item)
910
- }));
911
- const canUseSecureBatchPath = secureEntries.every(({
912
- internal
913
- }) => canUseSecureRawBatchPath(internal));
914
- if (!canUseSecureBatchPath) {
1151
+ measureOperation("batch:set", scope, () => {
1152
+ (0, _internal.assertBatchScope)(items.map(batchEntry => batchEntry.item), scope);
1153
+ if (scope === _Storage.StorageScope.Memory) {
915
1154
  items.forEach(({
916
1155
  item,
917
1156
  value
918
1157
  }) => item.set(value));
919
1158
  return;
920
1159
  }
921
- flushSecureWrites();
922
- const groupedByAccessControl = new Map();
923
- secureEntries.forEach(({
924
- item,
925
- value,
926
- internal
927
- }) => {
928
- const accessControl = internal._secureAccessControl ?? _Storage.AccessControl.WhenUnlocked;
929
- const existingGroup = groupedByAccessControl.get(accessControl);
930
- const group = existingGroup ?? {
931
- keys: [],
932
- values: []
933
- };
934
- group.keys.push(item.key);
935
- group.values.push(item.serialize(value));
936
- if (!existingGroup) {
937
- groupedByAccessControl.set(accessControl, group);
1160
+ if (scope === _Storage.StorageScope.Secure) {
1161
+ const secureEntries = items.map(({
1162
+ item,
1163
+ value
1164
+ }) => ({
1165
+ item,
1166
+ value,
1167
+ internal: asInternal(item)
1168
+ }));
1169
+ const canUseSecureBatchPath = secureEntries.every(({
1170
+ internal
1171
+ }) => canUseSecureRawBatchPath(internal));
1172
+ if (!canUseSecureBatchPath) {
1173
+ items.forEach(({
1174
+ item,
1175
+ value
1176
+ }) => item.set(value));
1177
+ return;
938
1178
  }
939
- });
940
- groupedByAccessControl.forEach((group, accessControl) => {
941
- WebStorage.setSecureAccessControl(accessControl);
942
- WebStorage.setBatch(group.keys, group.values, scope);
943
- group.keys.forEach((key, index) => cacheRawValue(scope, key, group.values[index]));
944
- });
945
- return;
946
- }
947
- const useRawBatchPath = items.every(({
948
- item
949
- }) => canUseRawBatchPath(asInternal(item)));
950
- if (!useRawBatchPath) {
951
- items.forEach(({
952
- item,
953
- value
954
- }) => item.set(value));
955
- return;
956
- }
957
- const keys = items.map(entry => entry.item.key);
958
- const values = items.map(entry => entry.item.serialize(entry.value));
959
- WebStorage.setBatch(keys, values, scope);
960
- keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
1179
+ flushSecureWrites();
1180
+ const groupedByAccessControl = new Map();
1181
+ secureEntries.forEach(({
1182
+ item,
1183
+ value,
1184
+ internal
1185
+ }) => {
1186
+ const accessControl = internal._secureAccessControl ?? _Storage.AccessControl.WhenUnlocked;
1187
+ const existingGroup = groupedByAccessControl.get(accessControl);
1188
+ const group = existingGroup ?? {
1189
+ keys: [],
1190
+ values: []
1191
+ };
1192
+ group.keys.push(item.key);
1193
+ group.values.push(item.serialize(value));
1194
+ if (!existingGroup) {
1195
+ groupedByAccessControl.set(accessControl, group);
1196
+ }
1197
+ });
1198
+ groupedByAccessControl.forEach((group, accessControl) => {
1199
+ WebStorage.setSecureAccessControl(accessControl);
1200
+ WebStorage.setBatch(group.keys, group.values, scope);
1201
+ group.keys.forEach((key, index) => cacheRawValue(scope, key, group.values[index]));
1202
+ });
1203
+ return;
1204
+ }
1205
+ const useRawBatchPath = items.every(({
1206
+ item
1207
+ }) => canUseRawBatchPath(asInternal(item)));
1208
+ if (!useRawBatchPath) {
1209
+ items.forEach(({
1210
+ item,
1211
+ value
1212
+ }) => item.set(value));
1213
+ return;
1214
+ }
1215
+ const keys = items.map(entry => entry.item.key);
1216
+ const values = items.map(entry => entry.item.serialize(entry.value));
1217
+ WebStorage.setBatch(keys, values, scope);
1218
+ keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
1219
+ }, items.length);
961
1220
  }
962
1221
  function removeBatch(items, scope) {
963
- (0, _internal.assertBatchScope)(items, scope);
964
- if (scope === _Storage.StorageScope.Memory) {
965
- items.forEach(item => item.delete());
966
- return;
967
- }
968
- const keys = items.map(item => item.key);
969
- if (scope === _Storage.StorageScope.Secure) {
970
- flushSecureWrites();
971
- }
972
- WebStorage.removeBatch(keys, scope);
973
- keys.forEach(key => cacheRawValue(scope, key, undefined));
1222
+ measureOperation("batch:remove", scope, () => {
1223
+ (0, _internal.assertBatchScope)(items, scope);
1224
+ if (scope === _Storage.StorageScope.Memory) {
1225
+ items.forEach(item => item.delete());
1226
+ return;
1227
+ }
1228
+ const keys = items.map(item => item.key);
1229
+ if (scope === _Storage.StorageScope.Secure) {
1230
+ flushSecureWrites();
1231
+ }
1232
+ WebStorage.removeBatch(keys, scope);
1233
+ keys.forEach(key => cacheRawValue(scope, key, undefined));
1234
+ }, items.length);
974
1235
  }
975
1236
  function registerMigration(version, migration) {
976
1237
  if (!Number.isInteger(version) || version <= 0) {
@@ -982,77 +1243,107 @@ function registerMigration(version, migration) {
982
1243
  registeredMigrations.set(version, migration);
983
1244
  }
984
1245
  function migrateToLatest(scope = _Storage.StorageScope.Disk) {
985
- (0, _internal.assertValidScope)(scope);
986
- const currentVersion = readMigrationVersion(scope);
987
- const versions = Array.from(registeredMigrations.keys()).filter(version => version > currentVersion).sort((a, b) => a - b);
988
- let appliedVersion = currentVersion;
989
- const context = {
990
- scope,
991
- getRaw: key => getRawValue(key, scope),
992
- setRaw: (key, value) => setRawValue(key, value, scope),
993
- removeRaw: key => removeRawValue(key, scope)
994
- };
995
- versions.forEach(version => {
996
- const migration = registeredMigrations.get(version);
997
- if (!migration) {
998
- return;
999
- }
1000
- migration(context);
1001
- writeMigrationVersion(scope, version);
1002
- appliedVersion = version;
1246
+ return measureOperation("migration:run", scope, () => {
1247
+ (0, _internal.assertValidScope)(scope);
1248
+ const currentVersion = readMigrationVersion(scope);
1249
+ const versions = Array.from(registeredMigrations.keys()).filter(version => version > currentVersion).sort((a, b) => a - b);
1250
+ let appliedVersion = currentVersion;
1251
+ const context = {
1252
+ scope,
1253
+ getRaw: key => getRawValue(key, scope),
1254
+ setRaw: (key, value) => setRawValue(key, value, scope),
1255
+ removeRaw: key => removeRawValue(key, scope)
1256
+ };
1257
+ versions.forEach(version => {
1258
+ const migration = registeredMigrations.get(version);
1259
+ if (!migration) {
1260
+ return;
1261
+ }
1262
+ migration(context);
1263
+ writeMigrationVersion(scope, version);
1264
+ appliedVersion = version;
1265
+ });
1266
+ return appliedVersion;
1003
1267
  });
1004
- return appliedVersion;
1005
1268
  }
1006
1269
  function runTransaction(scope, transaction) {
1007
- (0, _internal.assertValidScope)(scope);
1008
- if (scope === _Storage.StorageScope.Secure) {
1009
- flushSecureWrites();
1010
- }
1011
- const rollback = new Map();
1012
- const rememberRollback = key => {
1013
- if (rollback.has(key)) {
1014
- return;
1015
- }
1016
- rollback.set(key, getRawValue(key, scope));
1017
- };
1018
- const tx = {
1019
- scope,
1020
- getRaw: key => getRawValue(key, scope),
1021
- setRaw: (key, value) => {
1022
- rememberRollback(key);
1023
- setRawValue(key, value, scope);
1024
- },
1025
- removeRaw: key => {
1026
- rememberRollback(key);
1027
- removeRawValue(key, scope);
1028
- },
1029
- getItem: item => {
1030
- (0, _internal.assertBatchScope)([item], scope);
1031
- return item.get();
1032
- },
1033
- setItem: (item, value) => {
1034
- (0, _internal.assertBatchScope)([item], scope);
1035
- rememberRollback(item.key);
1036
- item.set(value);
1037
- },
1038
- removeItem: item => {
1039
- (0, _internal.assertBatchScope)([item], scope);
1040
- rememberRollback(item.key);
1041
- item.delete();
1270
+ return measureOperation("transaction:run", scope, () => {
1271
+ (0, _internal.assertValidScope)(scope);
1272
+ if (scope === _Storage.StorageScope.Secure) {
1273
+ flushSecureWrites();
1042
1274
  }
1043
- };
1044
- try {
1045
- return transaction(tx);
1046
- } catch (error) {
1047
- Array.from(rollback.entries()).reverse().forEach(([key, previousValue]) => {
1048
- if (previousValue === undefined) {
1275
+ const rollback = new Map();
1276
+ const rememberRollback = key => {
1277
+ if (rollback.has(key)) {
1278
+ return;
1279
+ }
1280
+ rollback.set(key, getRawValue(key, scope));
1281
+ };
1282
+ const tx = {
1283
+ scope,
1284
+ getRaw: key => getRawValue(key, scope),
1285
+ setRaw: (key, value) => {
1286
+ rememberRollback(key);
1287
+ setRawValue(key, value, scope);
1288
+ },
1289
+ removeRaw: key => {
1290
+ rememberRollback(key);
1049
1291
  removeRawValue(key, scope);
1292
+ },
1293
+ getItem: item => {
1294
+ (0, _internal.assertBatchScope)([item], scope);
1295
+ return item.get();
1296
+ },
1297
+ setItem: (item, value) => {
1298
+ (0, _internal.assertBatchScope)([item], scope);
1299
+ rememberRollback(item.key);
1300
+ item.set(value);
1301
+ },
1302
+ removeItem: item => {
1303
+ (0, _internal.assertBatchScope)([item], scope);
1304
+ rememberRollback(item.key);
1305
+ item.delete();
1306
+ }
1307
+ };
1308
+ try {
1309
+ return transaction(tx);
1310
+ } catch (error) {
1311
+ const rollbackEntries = Array.from(rollback.entries()).reverse();
1312
+ if (scope === _Storage.StorageScope.Memory) {
1313
+ rollbackEntries.forEach(([key, previousValue]) => {
1314
+ if (previousValue === undefined) {
1315
+ removeRawValue(key, scope);
1316
+ } else {
1317
+ setRawValue(key, previousValue, scope);
1318
+ }
1319
+ });
1050
1320
  } else {
1051
- setRawValue(key, previousValue, scope);
1321
+ const keysToSet = [];
1322
+ const valuesToSet = [];
1323
+ const keysToRemove = [];
1324
+ rollbackEntries.forEach(([key, previousValue]) => {
1325
+ if (previousValue === undefined) {
1326
+ keysToRemove.push(key);
1327
+ } else {
1328
+ keysToSet.push(key);
1329
+ valuesToSet.push(previousValue);
1330
+ }
1331
+ });
1332
+ if (scope === _Storage.StorageScope.Secure) {
1333
+ flushSecureWrites();
1334
+ }
1335
+ if (keysToSet.length > 0) {
1336
+ WebStorage.setBatch(keysToSet, valuesToSet, scope);
1337
+ keysToSet.forEach((key, index) => cacheRawValue(scope, key, valuesToSet[index]));
1338
+ }
1339
+ if (keysToRemove.length > 0) {
1340
+ WebStorage.removeBatch(keysToRemove, scope);
1341
+ keysToRemove.forEach(key => cacheRawValue(scope, key, undefined));
1342
+ }
1052
1343
  }
1053
- });
1054
- throw error;
1055
- }
1344
+ throw error;
1345
+ }
1346
+ });
1056
1347
  }
1057
1348
  function createSecureAuthStorage(config, options) {
1058
1349
  const ns = options?.namespace ?? "auth";
@@ -1070,6 +1361,9 @@ function createSecureAuthStorage(config, options) {
1070
1361
  ...(itemConfig.biometric !== undefined ? {
1071
1362
  biometric: itemConfig.biometric
1072
1363
  } : {}),
1364
+ ...(itemConfig.biometricLevel !== undefined ? {
1365
+ biometricLevel: itemConfig.biometricLevel
1366
+ } : {}),
1073
1367
  ...(itemConfig.accessControl !== undefined ? {
1074
1368
  accessControl: itemConfig.accessControl
1075
1369
  } : {}),