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,
@@ -87,6 +89,36 @@ const scopedRawCache = new Map([[_Storage.StorageScope.Disk, new Map()], [_Stora
87
89
  const pendingSecureWrites = new Map();
88
90
  let secureFlushScheduled = false;
89
91
  let secureDefaultAccessControl = _Storage.AccessControl.WhenUnlocked;
92
+ let metricsObserver;
93
+ const metricsCounters = new Map();
94
+ function recordMetric(operation, scope, durationMs, keysCount = 1) {
95
+ const existing = metricsCounters.get(operation);
96
+ if (!existing) {
97
+ metricsCounters.set(operation, {
98
+ count: 1,
99
+ totalDurationMs: durationMs,
100
+ maxDurationMs: durationMs
101
+ });
102
+ } else {
103
+ existing.count += 1;
104
+ existing.totalDurationMs += durationMs;
105
+ existing.maxDurationMs = Math.max(existing.maxDurationMs, durationMs);
106
+ }
107
+ metricsObserver?.({
108
+ operation,
109
+ scope,
110
+ durationMs,
111
+ keysCount
112
+ });
113
+ }
114
+ function measureOperation(operation, scope, fn, keysCount = 1) {
115
+ const start = Date.now();
116
+ try {
117
+ return fn();
118
+ } finally {
119
+ recordMetric(operation, scope, Date.now() - start, keysCount);
120
+ }
121
+ }
90
122
  function getScopedListeners(scope) {
91
123
  return scopedListeners.get(scope);
92
124
  }
@@ -147,34 +179,47 @@ function flushSecureWrites() {
147
179
  }
148
180
  const writes = Array.from(pendingSecureWrites.values());
149
181
  pendingSecureWrites.clear();
150
- const keysToSet = [];
151
- const valuesToSet = [];
182
+ const groupedSetWrites = new Map();
152
183
  const keysToRemove = [];
153
184
  writes.forEach(({
154
185
  key,
155
- value
186
+ value,
187
+ accessControl
156
188
  }) => {
157
189
  if (value === undefined) {
158
190
  keysToRemove.push(key);
159
191
  } else {
160
- keysToSet.push(key);
161
- valuesToSet.push(value);
192
+ const resolvedAccessControl = accessControl ?? secureDefaultAccessControl;
193
+ const existingGroup = groupedSetWrites.get(resolvedAccessControl);
194
+ const group = existingGroup ?? {
195
+ keys: [],
196
+ values: []
197
+ };
198
+ group.keys.push(key);
199
+ group.values.push(value);
200
+ if (!existingGroup) {
201
+ groupedSetWrites.set(resolvedAccessControl, group);
202
+ }
162
203
  }
163
204
  });
164
205
  const storageModule = getStorageModule();
165
- storageModule.setSecureAccessControl(secureDefaultAccessControl);
166
- if (keysToSet.length > 0) {
167
- storageModule.setBatch(keysToSet, valuesToSet, _Storage.StorageScope.Secure);
168
- }
206
+ groupedSetWrites.forEach((group, accessControl) => {
207
+ storageModule.setSecureAccessControl(accessControl);
208
+ storageModule.setBatch(group.keys, group.values, _Storage.StorageScope.Secure);
209
+ });
169
210
  if (keysToRemove.length > 0) {
170
211
  storageModule.removeBatch(keysToRemove, _Storage.StorageScope.Secure);
171
212
  }
172
213
  }
173
- function scheduleSecureWrite(key, value) {
174
- pendingSecureWrites.set(key, {
214
+ function scheduleSecureWrite(key, value, accessControl) {
215
+ const pendingWrite = {
175
216
  key,
176
217
  value
177
- });
218
+ };
219
+ if (accessControl !== undefined) {
220
+ pendingWrite.accessControl = accessControl;
221
+ }
222
+ pendingSecureWrites.set(key, pendingWrite);
178
223
  if (secureFlushScheduled) {
179
224
  return;
180
225
  }
@@ -268,97 +313,180 @@ function writeMigrationVersion(scope, version) {
268
313
  }
269
314
  const storage = exports.storage = {
270
315
  clear: scope => {
271
- if (scope === _Storage.StorageScope.Memory) {
272
- memoryStore.clear();
273
- notifyAllListeners(memoryListeners);
274
- return;
275
- }
276
- if (scope === _Storage.StorageScope.Secure) {
277
- flushSecureWrites();
278
- pendingSecureWrites.clear();
279
- }
280
- clearScopeRawCache(scope);
281
- getStorageModule().clear(scope);
316
+ measureOperation("storage:clear", scope, () => {
317
+ if (scope === _Storage.StorageScope.Memory) {
318
+ memoryStore.clear();
319
+ notifyAllListeners(memoryListeners);
320
+ return;
321
+ }
322
+ if (scope === _Storage.StorageScope.Secure) {
323
+ flushSecureWrites();
324
+ pendingSecureWrites.clear();
325
+ }
326
+ clearScopeRawCache(scope);
327
+ getStorageModule().clear(scope);
328
+ });
282
329
  },
283
330
  clearAll: () => {
284
- storage.clear(_Storage.StorageScope.Memory);
285
- storage.clear(_Storage.StorageScope.Disk);
286
- storage.clear(_Storage.StorageScope.Secure);
331
+ measureOperation("storage:clearAll", _Storage.StorageScope.Memory, () => {
332
+ storage.clear(_Storage.StorageScope.Memory);
333
+ storage.clear(_Storage.StorageScope.Disk);
334
+ storage.clear(_Storage.StorageScope.Secure);
335
+ }, 3);
287
336
  },
288
337
  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);
338
+ measureOperation("storage:clearNamespace", scope, () => {
339
+ (0, _internal.assertValidScope)(scope);
340
+ if (scope === _Storage.StorageScope.Memory) {
341
+ for (const key of memoryStore.keys()) {
342
+ if ((0, _internal.isNamespaced)(key, namespace)) {
343
+ memoryStore.delete(key);
344
+ }
294
345
  }
346
+ notifyAllListeners(memoryListeners);
347
+ return;
295
348
  }
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);
349
+ const keyPrefix = (0, _internal.prefixKey)(namespace, "");
350
+ if (scope === _Storage.StorageScope.Secure) {
351
+ flushSecureWrites();
352
+ }
353
+ clearScopeRawCache(scope);
354
+ getStorageModule().removeByPrefix(keyPrefix, scope);
355
+ });
305
356
  },
306
357
  clearBiometric: () => {
307
- getStorageModule().clearSecureBiometric();
358
+ measureOperation("storage:clearBiometric", _Storage.StorageScope.Secure, () => {
359
+ getStorageModule().clearSecureBiometric();
360
+ });
308
361
  },
309
362
  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);
363
+ return measureOperation("storage:has", scope, () => {
364
+ (0, _internal.assertValidScope)(scope);
365
+ if (scope === _Storage.StorageScope.Memory) {
366
+ return memoryStore.has(key);
367
+ }
368
+ return getStorageModule().has(key, scope);
369
+ });
315
370
  },
316
371
  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);
372
+ return measureOperation("storage:getAllKeys", scope, () => {
373
+ (0, _internal.assertValidScope)(scope);
374
+ if (scope === _Storage.StorageScope.Memory) {
375
+ return Array.from(memoryStore.keys());
376
+ }
377
+ return getStorageModule().getAllKeys(scope);
378
+ });
379
+ },
380
+ getKeysByPrefix: (prefix, scope) => {
381
+ return measureOperation("storage:getKeysByPrefix", scope, () => {
382
+ (0, _internal.assertValidScope)(scope);
383
+ if (scope === _Storage.StorageScope.Memory) {
384
+ return Array.from(memoryStore.keys()).filter(key => key.startsWith(prefix));
385
+ }
386
+ return getStorageModule().getKeysByPrefix(prefix, scope);
387
+ });
388
+ },
389
+ getByPrefix: (prefix, scope) => {
390
+ return measureOperation("storage:getByPrefix", scope, () => {
391
+ const result = {};
392
+ const keys = storage.getKeysByPrefix(prefix, scope);
393
+ if (keys.length === 0) {
394
+ return result;
395
+ }
396
+ if (scope === _Storage.StorageScope.Memory) {
397
+ keys.forEach(key => {
398
+ const value = memoryStore.get(key);
399
+ if (typeof value === "string") {
400
+ result[key] = value;
401
+ }
402
+ });
403
+ return result;
404
+ }
405
+ const values = getStorageModule().getBatch(keys, scope);
406
+ keys.forEach((key, idx) => {
407
+ const value = (0, _internal.decodeNativeBatchValue)(values[idx]);
408
+ if (value !== undefined) {
409
+ result[key] = value;
410
+ }
411
+ });
412
+ return result;
413
+ });
322
414
  },
323
415
  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;
416
+ return measureOperation("storage:getAll", scope, () => {
417
+ (0, _internal.assertValidScope)(scope);
418
+ const result = {};
419
+ if (scope === _Storage.StorageScope.Memory) {
420
+ memoryStore.forEach((value, key) => {
421
+ if (typeof value === "string") result[key] = value;
422
+ });
423
+ return result;
424
+ }
425
+ const keys = getStorageModule().getAllKeys(scope);
426
+ if (keys.length === 0) return result;
427
+ const values = getStorageModule().getBatch(keys, scope);
428
+ keys.forEach((key, idx) => {
429
+ const val = (0, _internal.decodeNativeBatchValue)(values[idx]);
430
+ if (val !== undefined) result[key] = val;
329
431
  });
330
432
  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
433
  });
339
- return result;
340
434
  },
341
435
  size: scope => {
342
- (0, _internal.assertValidScope)(scope);
343
- if (scope === _Storage.StorageScope.Memory) {
344
- return memoryStore.size;
345
- }
346
- return getStorageModule().size(scope);
436
+ return measureOperation("storage:size", scope, () => {
437
+ (0, _internal.assertValidScope)(scope);
438
+ if (scope === _Storage.StorageScope.Memory) {
439
+ return memoryStore.size;
440
+ }
441
+ return getStorageModule().size(scope);
442
+ });
347
443
  },
348
444
  setAccessControl: level => {
349
- secureDefaultAccessControl = level;
350
- getStorageModule().setSecureAccessControl(level);
445
+ measureOperation("storage:setAccessControl", _Storage.StorageScope.Secure, () => {
446
+ secureDefaultAccessControl = level;
447
+ getStorageModule().setSecureAccessControl(level);
448
+ });
351
449
  },
352
450
  setSecureWritesAsync: enabled => {
353
- getStorageModule().setSecureWritesAsync(enabled);
451
+ measureOperation("storage:setSecureWritesAsync", _Storage.StorageScope.Secure, () => {
452
+ getStorageModule().setSecureWritesAsync(enabled);
453
+ });
354
454
  },
355
455
  flushSecureWrites: () => {
356
- flushSecureWrites();
456
+ measureOperation("storage:flushSecureWrites", _Storage.StorageScope.Secure, () => {
457
+ flushSecureWrites();
458
+ });
357
459
  },
358
460
  setKeychainAccessGroup: group => {
359
- getStorageModule().setKeychainAccessGroup(group);
461
+ measureOperation("storage:setKeychainAccessGroup", _Storage.StorageScope.Secure, () => {
462
+ getStorageModule().setKeychainAccessGroup(group);
463
+ });
464
+ },
465
+ setMetricsObserver: observer => {
466
+ metricsObserver = observer;
467
+ },
468
+ getMetricsSnapshot: () => {
469
+ const snapshot = {};
470
+ metricsCounters.forEach((value, key) => {
471
+ snapshot[key] = {
472
+ count: value.count,
473
+ totalDurationMs: value.totalDurationMs,
474
+ avgDurationMs: value.count === 0 ? 0 : value.totalDurationMs / value.count,
475
+ maxDurationMs: value.maxDurationMs
476
+ };
477
+ });
478
+ return snapshot;
479
+ },
480
+ resetMetrics: () => {
481
+ metricsCounters.clear();
360
482
  }
361
483
  };
484
+ function setWebSecureStorageBackend(_backend) {
485
+ // Native platforms do not use web secure backends.
486
+ }
487
+ function getWebSecureStorageBackend() {
488
+ return undefined;
489
+ }
362
490
  function canUseRawBatchPath(item) {
363
491
  return item._hasExpiration === false && item._hasValidation === false && item._isBiometric !== true && item._secureAccessControl === undefined;
364
492
  }
@@ -376,7 +504,8 @@ function createStorageItem(config) {
376
504
  const serialize = config.serialize ?? defaultSerialize;
377
505
  const deserialize = config.deserialize ?? defaultDeserialize;
378
506
  const isMemory = config.scope === _Storage.StorageScope.Memory;
379
- const isBiometric = config.biometric === true && config.scope === _Storage.StorageScope.Secure;
507
+ const resolvedBiometricLevel = config.scope === _Storage.StorageScope.Secure ? config.biometricLevel ?? (config.biometric === true ? _Storage.BiometricLevel.BiometryOnly : _Storage.BiometricLevel.None) : _Storage.BiometricLevel.None;
508
+ const isBiometric = resolvedBiometricLevel !== _Storage.BiometricLevel.None;
380
509
  const secureAccessControl = config.accessControl;
381
510
  const validate = config.validate;
382
511
  const onValidationError = config.onValidationError;
@@ -385,7 +514,7 @@ function createStorageItem(config) {
385
514
  const expirationTtlMs = expiration?.ttlMs;
386
515
  const memoryExpiration = expiration && isMemory ? new Map() : null;
387
516
  const readCache = !isMemory && config.readCache === true;
388
- const coalesceSecureWrites = config.scope === _Storage.StorageScope.Secure && config.coalesceSecureWrites === true && !isBiometric && secureAccessControl === undefined;
517
+ const coalesceSecureWrites = config.scope === _Storage.StorageScope.Secure && config.coalesceSecureWrites === true && !isBiometric;
389
518
  const defaultValue = config.defaultValue;
390
519
  const nonMemoryScope = config.scope === _Storage.StorageScope.Disk ? _Storage.StorageScope.Disk : config.scope === _Storage.StorageScope.Secure ? _Storage.StorageScope.Secure : null;
391
520
  if (expiration && expiration.ttlMs <= 0) {
@@ -449,12 +578,12 @@ function createStorageItem(config) {
449
578
  };
450
579
  const writeStoredRaw = rawValue => {
451
580
  if (isBiometric) {
452
- getStorageModule().setSecureBiometric(storageKey, rawValue);
581
+ getStorageModule().setSecureBiometricWithLevel(storageKey, rawValue, resolvedBiometricLevel);
453
582
  return;
454
583
  }
455
584
  cacheRawValue(nonMemoryScope, storageKey, rawValue);
456
585
  if (coalesceSecureWrites) {
457
- scheduleSecureWrite(storageKey, rawValue);
586
+ scheduleSecureWrite(storageKey, rawValue, secureAccessControl ?? secureDefaultAccessControl);
458
587
  return;
459
588
  }
460
589
  if (nonMemoryScope === _Storage.StorageScope.Secure) {
@@ -470,7 +599,7 @@ function createStorageItem(config) {
470
599
  }
471
600
  cacheRawValue(nonMemoryScope, storageKey, undefined);
472
601
  if (coalesceSecureWrites) {
473
- scheduleSecureWrite(storageKey, undefined);
602
+ scheduleSecureWrite(storageKey, undefined, secureAccessControl ?? secureDefaultAccessControl);
474
603
  return;
475
604
  }
476
605
  if (nonMemoryScope === _Storage.StorageScope.Secure) {
@@ -518,7 +647,7 @@ function createStorageItem(config) {
518
647
  }
519
648
  return resolved;
520
649
  };
521
- const get = () => {
650
+ const getInternal = () => {
522
651
  const raw = readStoredRaw();
523
652
  if (!memoryExpiration && raw === lastRaw && hasLastValue) {
524
653
  if (!expiration || lastExpiresAt === null) {
@@ -583,31 +712,52 @@ function createStorageItem(config) {
583
712
  hasLastValue = true;
584
713
  return lastValue;
585
714
  };
715
+ const getCurrentVersion = () => {
716
+ const raw = readStoredRaw();
717
+ return (0, _internal.toVersionToken)(raw);
718
+ };
719
+ const get = () => measureOperation("item:get", config.scope, () => getInternal());
720
+ const getWithVersion = () => measureOperation("item:getWithVersion", config.scope, () => ({
721
+ value: getInternal(),
722
+ version: getCurrentVersion()
723
+ }));
586
724
  const set = valueOrFn => {
587
- const newValue = isUpdater(valueOrFn) ? valueOrFn(get()) : valueOrFn;
588
- invalidateParsedCache();
589
- if (validate && !validate(newValue)) {
590
- throw new Error(`Validation failed for key "${storageKey}" in scope "${_Storage.StorageScope[config.scope]}".`);
591
- }
592
- writeValueWithoutValidation(newValue);
725
+ measureOperation("item:set", config.scope, () => {
726
+ const newValue = isUpdater(valueOrFn) ? valueOrFn(getInternal()) : valueOrFn;
727
+ invalidateParsedCache();
728
+ if (validate && !validate(newValue)) {
729
+ throw new Error(`Validation failed for key "${storageKey}" in scope "${_Storage.StorageScope[config.scope]}".`);
730
+ }
731
+ writeValueWithoutValidation(newValue);
732
+ });
593
733
  };
734
+ const setIfVersion = (version, valueOrFn) => measureOperation("item:setIfVersion", config.scope, () => {
735
+ const currentVersion = getCurrentVersion();
736
+ if (currentVersion !== version) {
737
+ return false;
738
+ }
739
+ set(valueOrFn);
740
+ return true;
741
+ });
594
742
  const deleteItem = () => {
595
- invalidateParsedCache();
596
- if (isMemory) {
597
- if (memoryExpiration) {
598
- memoryExpiration.delete(storageKey);
743
+ measureOperation("item:delete", config.scope, () => {
744
+ invalidateParsedCache();
745
+ if (isMemory) {
746
+ if (memoryExpiration) {
747
+ memoryExpiration.delete(storageKey);
748
+ }
749
+ memoryStore.delete(storageKey);
750
+ notifyKeyListeners(memoryListeners, storageKey);
751
+ return;
599
752
  }
600
- memoryStore.delete(storageKey);
601
- notifyKeyListeners(memoryListeners, storageKey);
602
- return;
603
- }
604
- removeStoredRaw();
753
+ removeStoredRaw();
754
+ });
605
755
  };
606
- const hasItem = () => {
756
+ const hasItem = () => measureOperation("item:has", config.scope, () => {
607
757
  if (isMemory) return memoryStore.has(storageKey);
608
758
  if (isBiometric) return getStorageModule().hasSecureBiometric(storageKey);
609
759
  return getStorageModule().has(storageKey, config.scope);
610
- };
760
+ });
611
761
  const subscribe = callback => {
612
762
  ensureSubscription();
613
763
  listeners.add(callback);
@@ -624,7 +774,9 @@ function createStorageItem(config) {
624
774
  };
625
775
  const storageItem = {
626
776
  get,
777
+ getWithVersion,
627
778
  set,
779
+ setIfVersion,
628
780
  delete: deleteItem,
629
781
  has: hasItem,
630
782
  subscribe,
@@ -638,6 +790,7 @@ function createStorageItem(config) {
638
790
  _hasExpiration: expiration !== undefined,
639
791
  _readCacheEnabled: readCache,
640
792
  _isBiometric: isBiometric,
793
+ _defaultValue: defaultValue,
641
794
  ...(secureAccessControl !== undefined ? {
642
795
  _secureAccessControl: secureAccessControl
643
796
  } : {}),
@@ -647,136 +800,141 @@ function createStorageItem(config) {
647
800
  return storageItem;
648
801
  }
649
802
  function getBatch(items, scope) {
650
- (0, _internal.assertBatchScope)(items, scope);
651
- if (scope === _Storage.StorageScope.Memory) {
652
- return items.map(item => item.get());
653
- }
654
- const useRawBatchPath = items.every(item => scope === _Storage.StorageScope.Secure ? canUseSecureRawBatchPath(item) : canUseRawBatchPath(item));
655
- if (!useRawBatchPath) {
656
- return items.map(item => item.get());
657
- }
658
- const useBatchCache = items.every(item => item._readCacheEnabled === true);
659
- const rawValues = new Array(items.length);
660
- const keysToFetch = [];
661
- const keyIndexes = [];
662
- items.forEach((item, index) => {
663
- if (scope === _Storage.StorageScope.Secure) {
664
- if (hasPendingSecureWrite(item.key)) {
665
- rawValues[index] = readPendingSecureWrite(item.key);
666
- return;
667
- }
803
+ return measureOperation("batch:get", scope, () => {
804
+ (0, _internal.assertBatchScope)(items, scope);
805
+ if (scope === _Storage.StorageScope.Memory) {
806
+ return items.map(item => item.get());
668
807
  }
669
- if (useBatchCache) {
670
- if (hasCachedRawValue(scope, item.key)) {
671
- rawValues[index] = readCachedRawValue(scope, item.key);
672
- return;
673
- }
808
+ const useRawBatchPath = items.every(item => scope === _Storage.StorageScope.Secure ? canUseSecureRawBatchPath(item) : canUseRawBatchPath(item));
809
+ if (!useRawBatchPath) {
810
+ return items.map(item => item.get());
674
811
  }
675
- keysToFetch.push(item.key);
676
- keyIndexes.push(index);
677
- });
678
- if (keysToFetch.length > 0) {
679
- const fetchedValues = getStorageModule().getBatch(keysToFetch, scope).map(value => (0, _internal.decodeNativeBatchValue)(value));
680
- fetchedValues.forEach((value, index) => {
681
- const key = keysToFetch[index];
682
- const targetIndex = keyIndexes[index];
683
- if (key === undefined || targetIndex === undefined) {
684
- return;
812
+ const rawValues = new Array(items.length);
813
+ const keysToFetch = [];
814
+ const keyIndexes = [];
815
+ items.forEach((item, index) => {
816
+ if (scope === _Storage.StorageScope.Secure) {
817
+ if (hasPendingSecureWrite(item.key)) {
818
+ rawValues[index] = readPendingSecureWrite(item.key);
819
+ return;
820
+ }
685
821
  }
686
- rawValues[targetIndex] = value;
687
- cacheRawValue(scope, key, value);
822
+ if (item._readCacheEnabled === true) {
823
+ if (hasCachedRawValue(scope, item.key)) {
824
+ rawValues[index] = readCachedRawValue(scope, item.key);
825
+ return;
826
+ }
827
+ }
828
+ keysToFetch.push(item.key);
829
+ keyIndexes.push(index);
688
830
  });
689
- }
690
- return items.map((item, index) => {
691
- const raw = rawValues[index];
692
- if (raw === undefined) {
693
- return item.get();
831
+ if (keysToFetch.length > 0) {
832
+ const fetchedValues = getStorageModule().getBatch(keysToFetch, scope).map(value => (0, _internal.decodeNativeBatchValue)(value));
833
+ fetchedValues.forEach((value, index) => {
834
+ const key = keysToFetch[index];
835
+ const targetIndex = keyIndexes[index];
836
+ if (key === undefined || targetIndex === undefined) {
837
+ return;
838
+ }
839
+ rawValues[targetIndex] = value;
840
+ cacheRawValue(scope, key, value);
841
+ });
694
842
  }
695
- return item.deserialize(raw);
696
- });
843
+ return items.map((item, index) => {
844
+ const raw = rawValues[index];
845
+ if (raw === undefined) {
846
+ return asInternal(item)._defaultValue;
847
+ }
848
+ return item.deserialize(raw);
849
+ });
850
+ }, items.length);
697
851
  }
698
852
  function setBatch(items, scope) {
699
- (0, _internal.assertBatchScope)(items.map(batchEntry => batchEntry.item), scope);
700
- if (scope === _Storage.StorageScope.Memory) {
701
- items.forEach(({
702
- item,
703
- value
704
- }) => item.set(value));
705
- return;
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) {
853
+ measureOperation("batch:set", scope, () => {
854
+ (0, _internal.assertBatchScope)(items.map(batchEntry => batchEntry.item), scope);
855
+ if (scope === _Storage.StorageScope.Memory) {
720
856
  items.forEach(({
721
857
  item,
722
858
  value
723
859
  }) => item.set(value));
724
860
  return;
725
861
  }
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);
862
+ if (scope === _Storage.StorageScope.Secure) {
863
+ const secureEntries = items.map(({
864
+ item,
865
+ value
866
+ }) => ({
867
+ item,
868
+ value,
869
+ internal: asInternal(item)
870
+ }));
871
+ const canUseSecureBatchPath = secureEntries.every(({
872
+ internal
873
+ }) => canUseSecureRawBatchPath(internal));
874
+ if (!canUseSecureBatchPath) {
875
+ items.forEach(({
876
+ item,
877
+ value
878
+ }) => item.set(value));
879
+ return;
744
880
  }
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
- }
753
- const useRawBatchPath = items.every(({
754
- item
755
- }) => canUseRawBatchPath(asInternal(item)));
756
- if (!useRawBatchPath) {
757
- items.forEach(({
758
- item,
759
- value
760
- }) => item.set(value));
761
- return;
762
- }
763
- const keys = items.map(entry => entry.item.key);
764
- const values = items.map(entry => entry.item.serialize(entry.value));
765
- getStorageModule().setBatch(keys, values, scope);
766
- keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
881
+ flushSecureWrites();
882
+ const storageModule = getStorageModule();
883
+ const groupedByAccessControl = new Map();
884
+ secureEntries.forEach(({
885
+ item,
886
+ value,
887
+ internal
888
+ }) => {
889
+ const accessControl = internal._secureAccessControl ?? secureDefaultAccessControl;
890
+ const existingGroup = groupedByAccessControl.get(accessControl);
891
+ const group = existingGroup ?? {
892
+ keys: [],
893
+ values: []
894
+ };
895
+ group.keys.push(item.key);
896
+ group.values.push(item.serialize(value));
897
+ if (!existingGroup) {
898
+ groupedByAccessControl.set(accessControl, group);
899
+ }
900
+ });
901
+ groupedByAccessControl.forEach((group, accessControl) => {
902
+ storageModule.setSecureAccessControl(accessControl);
903
+ storageModule.setBatch(group.keys, group.values, scope);
904
+ group.keys.forEach((key, index) => cacheRawValue(scope, key, group.values[index]));
905
+ });
906
+ return;
907
+ }
908
+ const useRawBatchPath = items.every(({
909
+ item
910
+ }) => canUseRawBatchPath(asInternal(item)));
911
+ if (!useRawBatchPath) {
912
+ items.forEach(({
913
+ item,
914
+ value
915
+ }) => item.set(value));
916
+ return;
917
+ }
918
+ const keys = items.map(entry => entry.item.key);
919
+ const values = items.map(entry => entry.item.serialize(entry.value));
920
+ getStorageModule().setBatch(keys, values, scope);
921
+ keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
922
+ }, items.length);
767
923
  }
768
924
  function removeBatch(items, scope) {
769
- (0, _internal.assertBatchScope)(items, scope);
770
- if (scope === _Storage.StorageScope.Memory) {
771
- items.forEach(item => item.delete());
772
- return;
773
- }
774
- const keys = items.map(item => item.key);
775
- if (scope === _Storage.StorageScope.Secure) {
776
- flushSecureWrites();
777
- }
778
- getStorageModule().removeBatch(keys, scope);
779
- keys.forEach(key => cacheRawValue(scope, key, undefined));
925
+ measureOperation("batch:remove", scope, () => {
926
+ (0, _internal.assertBatchScope)(items, scope);
927
+ if (scope === _Storage.StorageScope.Memory) {
928
+ items.forEach(item => item.delete());
929
+ return;
930
+ }
931
+ const keys = items.map(item => item.key);
932
+ if (scope === _Storage.StorageScope.Secure) {
933
+ flushSecureWrites();
934
+ }
935
+ getStorageModule().removeBatch(keys, scope);
936
+ keys.forEach(key => cacheRawValue(scope, key, undefined));
937
+ }, items.length);
780
938
  }
781
939
  function registerMigration(version, migration) {
782
940
  if (!Number.isInteger(version) || version <= 0) {
@@ -788,77 +946,107 @@ function registerMigration(version, migration) {
788
946
  registeredMigrations.set(version, migration);
789
947
  }
790
948
  function migrateToLatest(scope = _Storage.StorageScope.Disk) {
791
- (0, _internal.assertValidScope)(scope);
792
- const currentVersion = readMigrationVersion(scope);
793
- const versions = Array.from(registeredMigrations.keys()).filter(version => version > currentVersion).sort((a, b) => a - b);
794
- let appliedVersion = currentVersion;
795
- const context = {
796
- scope,
797
- getRaw: key => getRawValue(key, scope),
798
- setRaw: (key, value) => setRawValue(key, value, scope),
799
- removeRaw: key => removeRawValue(key, scope)
800
- };
801
- versions.forEach(version => {
802
- const migration = registeredMigrations.get(version);
803
- if (!migration) {
804
- return;
805
- }
806
- migration(context);
807
- writeMigrationVersion(scope, version);
808
- appliedVersion = version;
949
+ return measureOperation("migration:run", scope, () => {
950
+ (0, _internal.assertValidScope)(scope);
951
+ const currentVersion = readMigrationVersion(scope);
952
+ const versions = Array.from(registeredMigrations.keys()).filter(version => version > currentVersion).sort((a, b) => a - b);
953
+ let appliedVersion = currentVersion;
954
+ const context = {
955
+ scope,
956
+ getRaw: key => getRawValue(key, scope),
957
+ setRaw: (key, value) => setRawValue(key, value, scope),
958
+ removeRaw: key => removeRawValue(key, scope)
959
+ };
960
+ versions.forEach(version => {
961
+ const migration = registeredMigrations.get(version);
962
+ if (!migration) {
963
+ return;
964
+ }
965
+ migration(context);
966
+ writeMigrationVersion(scope, version);
967
+ appliedVersion = version;
968
+ });
969
+ return appliedVersion;
809
970
  });
810
- return appliedVersion;
811
971
  }
812
972
  function runTransaction(scope, transaction) {
813
- (0, _internal.assertValidScope)(scope);
814
- if (scope === _Storage.StorageScope.Secure) {
815
- flushSecureWrites();
816
- }
817
- const rollback = new Map();
818
- const rememberRollback = key => {
819
- if (rollback.has(key)) {
820
- return;
821
- }
822
- rollback.set(key, getRawValue(key, scope));
823
- };
824
- const tx = {
825
- scope,
826
- getRaw: key => getRawValue(key, scope),
827
- setRaw: (key, value) => {
828
- rememberRollback(key);
829
- setRawValue(key, value, scope);
830
- },
831
- removeRaw: key => {
832
- rememberRollback(key);
833
- removeRawValue(key, scope);
834
- },
835
- getItem: item => {
836
- (0, _internal.assertBatchScope)([item], scope);
837
- return item.get();
838
- },
839
- setItem: (item, value) => {
840
- (0, _internal.assertBatchScope)([item], scope);
841
- rememberRollback(item.key);
842
- item.set(value);
843
- },
844
- removeItem: item => {
845
- (0, _internal.assertBatchScope)([item], scope);
846
- rememberRollback(item.key);
847
- item.delete();
973
+ return measureOperation("transaction:run", scope, () => {
974
+ (0, _internal.assertValidScope)(scope);
975
+ if (scope === _Storage.StorageScope.Secure) {
976
+ flushSecureWrites();
848
977
  }
849
- };
850
- try {
851
- return transaction(tx);
852
- } catch (error) {
853
- Array.from(rollback.entries()).reverse().forEach(([key, previousValue]) => {
854
- if (previousValue === undefined) {
978
+ const rollback = new Map();
979
+ const rememberRollback = key => {
980
+ if (rollback.has(key)) {
981
+ return;
982
+ }
983
+ rollback.set(key, getRawValue(key, scope));
984
+ };
985
+ const tx = {
986
+ scope,
987
+ getRaw: key => getRawValue(key, scope),
988
+ setRaw: (key, value) => {
989
+ rememberRollback(key);
990
+ setRawValue(key, value, scope);
991
+ },
992
+ removeRaw: key => {
993
+ rememberRollback(key);
855
994
  removeRawValue(key, scope);
995
+ },
996
+ getItem: item => {
997
+ (0, _internal.assertBatchScope)([item], scope);
998
+ return item.get();
999
+ },
1000
+ setItem: (item, value) => {
1001
+ (0, _internal.assertBatchScope)([item], scope);
1002
+ rememberRollback(item.key);
1003
+ item.set(value);
1004
+ },
1005
+ removeItem: item => {
1006
+ (0, _internal.assertBatchScope)([item], scope);
1007
+ rememberRollback(item.key);
1008
+ item.delete();
1009
+ }
1010
+ };
1011
+ try {
1012
+ return transaction(tx);
1013
+ } catch (error) {
1014
+ const rollbackEntries = Array.from(rollback.entries()).reverse();
1015
+ if (scope === _Storage.StorageScope.Memory) {
1016
+ rollbackEntries.forEach(([key, previousValue]) => {
1017
+ if (previousValue === undefined) {
1018
+ removeRawValue(key, scope);
1019
+ } else {
1020
+ setRawValue(key, previousValue, scope);
1021
+ }
1022
+ });
856
1023
  } else {
857
- setRawValue(key, previousValue, scope);
1024
+ const keysToSet = [];
1025
+ const valuesToSet = [];
1026
+ const keysToRemove = [];
1027
+ rollbackEntries.forEach(([key, previousValue]) => {
1028
+ if (previousValue === undefined) {
1029
+ keysToRemove.push(key);
1030
+ } else {
1031
+ keysToSet.push(key);
1032
+ valuesToSet.push(previousValue);
1033
+ }
1034
+ });
1035
+ if (scope === _Storage.StorageScope.Secure) {
1036
+ flushSecureWrites();
1037
+ }
1038
+ if (keysToSet.length > 0) {
1039
+ getStorageModule().setBatch(keysToSet, valuesToSet, scope);
1040
+ keysToSet.forEach((key, index) => cacheRawValue(scope, key, valuesToSet[index]));
1041
+ }
1042
+ if (keysToRemove.length > 0) {
1043
+ getStorageModule().removeBatch(keysToRemove, scope);
1044
+ keysToRemove.forEach(key => cacheRawValue(scope, key, undefined));
1045
+ }
858
1046
  }
859
- });
860
- throw error;
861
- }
1047
+ throw error;
1048
+ }
1049
+ });
862
1050
  }
863
1051
  function createSecureAuthStorage(config, options) {
864
1052
  const ns = options?.namespace ?? "auth";
@@ -876,6 +1064,9 @@ function createSecureAuthStorage(config, options) {
876
1064
  ...(itemConfig.biometric !== undefined ? {
877
1065
  biometric: itemConfig.biometric
878
1066
  } : {}),
1067
+ ...(itemConfig.biometricLevel !== undefined ? {
1068
+ biometricLevel: itemConfig.biometricLevel
1069
+ } : {}),
879
1070
  ...(itemConfig.accessControl !== undefined ? {
880
1071
  accessControl: itemConfig.accessControl
881
1072
  } : {}),