react-native-nitro-storage 0.5.7 → 0.5.8

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.
@@ -27,10 +27,9 @@ Object.defineProperty(exports, "createIndexedDBBackend", {
27
27
  return _indexeddbBackend.createIndexedDBBackend;
28
28
  }
29
29
  });
30
- exports.createSecureAuthStorage = createSecureAuthStorage;
31
- exports.createStorageItem = createStorageItem;
30
+ exports.createStorageItem = exports.createSecureAuthStorage = void 0;
32
31
  exports.flushWebStorageBackends = flushWebStorageBackends;
33
- exports.getBatch = getBatch;
32
+ exports.getBatch = void 0;
34
33
  Object.defineProperty(exports, "getStorageErrorCode", {
35
34
  enumerable: true,
36
35
  get: function () {
@@ -39,18 +38,19 @@ Object.defineProperty(exports, "getStorageErrorCode", {
39
38
  });
40
39
  exports.getWebDiskStorageBackend = getWebDiskStorageBackend;
41
40
  exports.getWebSecureStorageBackend = getWebSecureStorageBackend;
42
- exports.isKeychainLockedError = isKeychainLockedError;
41
+ Object.defineProperty(exports, "isKeychainLockedError", {
42
+ enumerable: true,
43
+ get: function () {
44
+ return _shared.isKeychainLockedError;
45
+ }
46
+ });
43
47
  Object.defineProperty(exports, "migrateFromMMKV", {
44
48
  enumerable: true,
45
49
  get: function () {
46
50
  return _migration.migrateFromMMKV;
47
51
  }
48
52
  });
49
- exports.migrateToLatest = migrateToLatest;
50
- exports.registerMigration = registerMigration;
51
- exports.removeBatch = removeBatch;
52
- exports.runTransaction = runTransaction;
53
- exports.setBatch = setBatch;
53
+ exports.setBatch = exports.runTransaction = exports.removeBatch = exports.registerMigration = exports.migrateToLatest = void 0;
54
54
  exports.setWebDiskStorageBackend = setWebDiskStorageBackend;
55
55
  exports.setWebSecureStorageBackend = setWebSecureStorageBackend;
56
56
  exports.storage = void 0;
@@ -72,94 +72,21 @@ Object.defineProperty(exports, "useStorageSelector", {
72
72
  return _storageHooks.useStorageSelector;
73
73
  }
74
74
  });
75
+ var _shared = require("./shared");
75
76
  var _Storage = require("./Storage.types");
76
- var _internal = require("./internal");
77
77
  var _webStorageBackend = require("./web-storage-backend");
78
- var _storageRuntime = require("./storage-runtime");
79
- var _storageEvents = require("./storage-events");
78
+ var _storageCore = require("./storage-core");
80
79
  var _migration = require("./migration");
80
+ var _storageRuntime = require("./storage-runtime");
81
81
  var _storageHooks = require("./storage-hooks");
82
82
  var _indexeddbBackend = require("./indexeddb-backend");
83
- function asInternal(item) {
84
- return item;
85
- }
86
- function isUpdater(valueOrFn) {
87
- return typeof valueOrFn === "function";
88
- }
89
- function typedKeys(record) {
90
- return Object.keys(record);
91
- }
92
- function assertEnumInteger(value, min, max, label) {
93
- if (!Number.isFinite(value) || value < min || value > max) {
94
- throw new Error(`NitroStorage: Invalid ${label}`);
95
- }
96
- if (value !== Math.trunc(value)) {
97
- throw new Error(`NitroStorage: Invalid ${label}`);
98
- }
99
- }
100
- function assertAccessControlLevel(level) {
101
- assertEnumInteger(level, 0, 4, "access control level");
102
- }
103
- function assertBiometricLevel(level) {
104
- assertEnumInteger(level, 0, 2, "biometric level");
105
- }
106
- const registeredMigrations = new Map();
107
- const runMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : task => {
108
- Promise.resolve().then(task);
109
- };
110
- const now = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
111
- const memoryStore = new Map();
112
- const memoryListeners = new Map();
113
- const webScopeListeners = new Map([[_Storage.StorageScope.Disk, new Map()], [_Storage.StorageScope.Secure, new Map()]]);
114
- const scopedRawCache = new Map([[_Storage.StorageScope.Disk, new Map()], [_Storage.StorageScope.Secure, new Map()]]);
115
83
  const webScopeKeyIndex = new Map([[_Storage.StorageScope.Disk, new Set()], [_Storage.StorageScope.Secure, new Set()]]);
116
84
  const hydratedWebScopeKeyIndex = new Set();
117
- const pendingDiskWrites = new Map();
118
- let diskFlushScheduled = false;
119
- let diskWritesAsync = false;
120
- const pendingSecureWrites = new Map();
121
- let secureFlushScheduled = false;
122
- let secureDefaultAccessControl = _Storage.AccessControl.WhenUnlocked;
123
85
  const SECURE_WEB_PREFIX = "__secure_";
124
86
  const BIOMETRIC_WEB_PREFIX = "__bio_";
125
87
  let hasWarnedAboutWebBiometricFallback = false;
126
88
  let hasWindowStorageEventSubscription = false;
127
- let metricsObserver;
128
- let eventObserver;
129
- let eventObserverRedactSecureValues = true;
130
- const metricsCounters = new Map();
131
- const storageEvents = new _storageEvents.StorageEventRegistry();
132
- function recordMetric(operation, scope, durationMs, keysCount = 1) {
133
- const existing = metricsCounters.get(operation);
134
- if (!existing) {
135
- metricsCounters.set(operation, {
136
- count: 1,
137
- totalDurationMs: durationMs,
138
- maxDurationMs: durationMs
139
- });
140
- } else {
141
- existing.count += 1;
142
- existing.totalDurationMs += durationMs;
143
- existing.maxDurationMs = Math.max(existing.maxDurationMs, durationMs);
144
- }
145
- metricsObserver?.({
146
- operation,
147
- scope,
148
- durationMs,
149
- keysCount
150
- });
151
- }
152
- function measureOperation(operation, scope, fn, keysCount = 1) {
153
- if (!metricsObserver) {
154
- return fn();
155
- }
156
- const start = now();
157
- try {
158
- return fn();
159
- } finally {
160
- recordMetric(operation, scope, now() - start, keysCount);
161
- }
162
- }
89
+ let internals;
163
90
  function createDefaultDiskBackend() {
164
91
  return (0, _webStorageBackend.createLocalStorageWebBackend)({
165
92
  name: "localStorage:disk",
@@ -246,51 +173,51 @@ function ensureWebScopeKeyIndex(scope) {
246
173
  }
247
174
  function applyExternalChangeEvent(scope, key, newValue) {
248
175
  if (key === null) {
249
- clearScopeRawCache(scope);
176
+ internals.clearScopeRawCache(scope);
250
177
  ensureWebScopeKeyIndex(scope).clear();
251
- notifyAllListeners(getScopedListeners(scope));
178
+ (0, _shared.notifyAllListeners)(internals.getScopedListeners(scope));
252
179
  return;
253
180
  }
254
181
  if (scope === _Storage.StorageScope.Secure && key.startsWith(SECURE_WEB_PREFIX)) {
255
182
  const plainKey = fromSecureStorageKey(key);
256
- const oldValue = readCachedRawValue(_Storage.StorageScope.Secure, plainKey);
183
+ const oldValue = internals.readCachedRawValue(_Storage.StorageScope.Secure, plainKey);
257
184
  if (newValue === null) {
258
185
  ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).delete(plainKey);
259
- cacheRawValue(_Storage.StorageScope.Secure, plainKey, undefined);
186
+ internals.cacheRawValue(_Storage.StorageScope.Secure, plainKey, undefined);
260
187
  } else {
261
188
  ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).add(plainKey);
262
- cacheRawValue(_Storage.StorageScope.Secure, plainKey, newValue);
189
+ internals.cacheRawValue(_Storage.StorageScope.Secure, plainKey, newValue);
263
190
  }
264
- notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), plainKey);
265
- emitKeyChange(_Storage.StorageScope.Secure, plainKey, oldValue, newValue ?? undefined, "external", "external");
191
+ (0, _shared.notifyKeyListeners)(internals.getScopedListeners(_Storage.StorageScope.Secure), plainKey);
192
+ internals.emitKeyChange(_Storage.StorageScope.Secure, plainKey, oldValue, newValue ?? undefined, "external", "external");
266
193
  return;
267
194
  }
268
195
  if (scope === _Storage.StorageScope.Secure && key.startsWith(BIOMETRIC_WEB_PREFIX)) {
269
196
  const plainKey = fromBiometricStorageKey(key);
270
- const oldValue = readCachedRawValue(_Storage.StorageScope.Secure, plainKey);
197
+ const oldValue = internals.readCachedRawValue(_Storage.StorageScope.Secure, plainKey);
271
198
  if (newValue === null) {
272
199
  if (withWebBackendOperation(_Storage.StorageScope.Secure, "external-sync:getItem", backend => backend.getItem(toSecureStorageKey(plainKey))) === null) {
273
200
  ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).delete(plainKey);
274
201
  }
275
- cacheRawValue(_Storage.StorageScope.Secure, plainKey, undefined);
202
+ internals.cacheRawValue(_Storage.StorageScope.Secure, plainKey, undefined);
276
203
  } else {
277
204
  ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).add(plainKey);
278
- cacheRawValue(_Storage.StorageScope.Secure, plainKey, newValue);
205
+ internals.cacheRawValue(_Storage.StorageScope.Secure, plainKey, newValue);
279
206
  }
280
- notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), plainKey);
281
- emitKeyChange(_Storage.StorageScope.Secure, plainKey, oldValue, newValue ?? undefined, "external", "external");
207
+ (0, _shared.notifyKeyListeners)(internals.getScopedListeners(_Storage.StorageScope.Secure), plainKey);
208
+ internals.emitKeyChange(_Storage.StorageScope.Secure, plainKey, oldValue, newValue ?? undefined, "external", "external");
282
209
  return;
283
210
  }
284
- const oldValue = readCachedRawValue(scope, key);
211
+ const oldValue = internals.readCachedRawValue(scope, key);
285
212
  if (newValue === null) {
286
213
  ensureWebScopeKeyIndex(scope).delete(key);
287
- cacheRawValue(scope, key, undefined);
214
+ internals.cacheRawValue(scope, key, undefined);
288
215
  } else {
289
216
  ensureWebScopeKeyIndex(scope).add(key);
290
- cacheRawValue(scope, key, newValue);
217
+ internals.cacheRawValue(scope, key, newValue);
291
218
  }
292
- notifyKeyListeners(getScopedListeners(scope), key);
293
- emitKeyChange(scope, key, oldValue, newValue ?? undefined, "external", "external");
219
+ (0, _shared.notifyKeyListeners)(internals.getScopedListeners(scope), key);
220
+ internals.emitKeyChange(scope, key, oldValue, newValue ?? undefined, "external", "external");
294
221
  }
295
222
  function handleWebStorageEvent(event) {
296
223
  const key = event.key;
@@ -340,239 +267,6 @@ function ensureExternalSyncSubscriptions() {
340
267
  subscribeToBackendChanges(_Storage.StorageScope.Disk);
341
268
  subscribeToBackendChanges(_Storage.StorageScope.Secure);
342
269
  }
343
- function getScopedListeners(scope) {
344
- return webScopeListeners.get(scope);
345
- }
346
- function getScopeRawCache(scope) {
347
- return scopedRawCache.get(scope);
348
- }
349
- function cacheRawValue(scope, key, value) {
350
- getScopeRawCache(scope).set(key, value);
351
- }
352
- function readCachedRawValue(scope, key) {
353
- return getScopeRawCache(scope).get(key);
354
- }
355
- function hasCachedRawValue(scope, key) {
356
- return getScopeRawCache(scope).has(key);
357
- }
358
- function clearScopeRawCache(scope) {
359
- getScopeRawCache(scope).clear();
360
- }
361
- function notifyKeyListeners(registry, key) {
362
- const listeners = registry.get(key);
363
- if (listeners) {
364
- for (const listener of listeners) {
365
- listener();
366
- }
367
- }
368
- }
369
- function notifyAllListeners(registry) {
370
- for (const listeners of registry.values()) {
371
- for (const listener of listeners) {
372
- listener();
373
- }
374
- }
375
- }
376
- function addKeyListener(registry, key, listener) {
377
- let listeners = registry.get(key);
378
- if (!listeners) {
379
- listeners = new Set();
380
- registry.set(key, listeners);
381
- }
382
- listeners.add(listener);
383
- return () => {
384
- const scopedListeners = registry.get(key);
385
- if (!scopedListeners) {
386
- return;
387
- }
388
- scopedListeners.delete(listener);
389
- if (scopedListeners.size === 0) {
390
- registry.delete(key);
391
- }
392
- };
393
- }
394
- function getEventRawValue(scope, key) {
395
- if (scope === _Storage.StorageScope.Memory) {
396
- const value = memoryStore.get(key);
397
- return typeof value === "string" ? value : undefined;
398
- }
399
- return getRawValue(key, scope);
400
- }
401
- function createKeyChange(scope, key, oldValue, newValue, operation, source) {
402
- return {
403
- type: "key",
404
- scope,
405
- key,
406
- oldValue,
407
- newValue,
408
- operation,
409
- source
410
- };
411
- }
412
- function hasStorageChangeObservers(scope) {
413
- return storageEvents.hasListeners(scope) || eventObserver !== undefined;
414
- }
415
- function shouldReadPreviousEventValues(scope) {
416
- if (storageEvents.hasListeners(scope)) {
417
- return true;
418
- }
419
- if (!eventObserver) {
420
- return false;
421
- }
422
- return scope !== _Storage.StorageScope.Secure || !eventObserverRedactSecureValues;
423
- }
424
- const SECURE_EVENT_REDACTED_VALUE = "[secure]";
425
- function redactSecureKeyChange(event) {
426
- if (event.scope !== _Storage.StorageScope.Secure) {
427
- return event;
428
- }
429
- return {
430
- ...event,
431
- oldValue: event.oldValue === undefined ? undefined : SECURE_EVENT_REDACTED_VALUE,
432
- newValue: event.newValue === undefined ? undefined : SECURE_EVENT_REDACTED_VALUE
433
- };
434
- }
435
- function eventForGlobalObserver(event) {
436
- if (!eventObserverRedactSecureValues || event.scope !== _Storage.StorageScope.Secure) {
437
- return event;
438
- }
439
- if (event.type === "key") {
440
- return redactSecureKeyChange(event);
441
- }
442
- return {
443
- ...event,
444
- changes: event.changes.map(redactSecureKeyChange)
445
- };
446
- }
447
- function emitKeyChange(scope, key, oldValue, newValue, operation, source) {
448
- const event = createKeyChange(scope, key, oldValue, newValue, operation, source);
449
- storageEvents.emitKey(event);
450
- eventObserver?.(eventForGlobalObserver(event));
451
- }
452
- function emitBatchChange(scope, operation, source, changes) {
453
- if (changes.length === 0) {
454
- return;
455
- }
456
- const event = {
457
- type: "batch",
458
- scope,
459
- operation,
460
- source,
461
- changes
462
- };
463
- storageEvents.emitBatch(event);
464
- eventObserver?.(eventForGlobalObserver(event));
465
- }
466
- function readPendingSecureWrite(key) {
467
- return pendingSecureWrites.get(key)?.value;
468
- }
469
- function readPendingDiskWrite(key) {
470
- return pendingDiskWrites.get(key)?.value;
471
- }
472
- function hasPendingDiskWrite(key) {
473
- return pendingDiskWrites.has(key);
474
- }
475
- function hasPendingSecureWrite(key) {
476
- return pendingSecureWrites.has(key);
477
- }
478
- function clearPendingDiskWrite(key) {
479
- pendingDiskWrites.delete(key);
480
- }
481
- function clearPendingSecureWrite(key) {
482
- pendingSecureWrites.delete(key);
483
- }
484
- function flushDiskWrites() {
485
- diskFlushScheduled = false;
486
- if (pendingDiskWrites.size === 0) {
487
- return;
488
- }
489
- const writes = Array.from(pendingDiskWrites.values());
490
- pendingDiskWrites.clear();
491
- const keysToSet = [];
492
- const valuesToSet = [];
493
- const keysToRemove = [];
494
- writes.forEach(({
495
- key,
496
- value
497
- }) => {
498
- if (value === undefined) {
499
- keysToRemove.push(key);
500
- return;
501
- }
502
- keysToSet.push(key);
503
- valuesToSet.push(value);
504
- });
505
- if (keysToSet.length > 0) {
506
- WebStorage.setBatch(keysToSet, valuesToSet, _Storage.StorageScope.Disk);
507
- }
508
- if (keysToRemove.length > 0) {
509
- WebStorage.removeBatch(keysToRemove, _Storage.StorageScope.Disk);
510
- }
511
- }
512
- function flushSecureWrites() {
513
- secureFlushScheduled = false;
514
- if (pendingSecureWrites.size === 0) {
515
- return;
516
- }
517
- const writes = Array.from(pendingSecureWrites.values());
518
- pendingSecureWrites.clear();
519
- const groupedSetWrites = new Map();
520
- const keysToRemove = [];
521
- writes.forEach(({
522
- key,
523
- value,
524
- accessControl
525
- }) => {
526
- if (value === undefined) {
527
- keysToRemove.push(key);
528
- } else {
529
- const resolvedAccessControl = accessControl ?? secureDefaultAccessControl;
530
- const existingGroup = groupedSetWrites.get(resolvedAccessControl);
531
- const group = existingGroup ?? {
532
- keys: [],
533
- values: []
534
- };
535
- group.keys.push(key);
536
- group.values.push(value);
537
- if (!existingGroup) {
538
- groupedSetWrites.set(resolvedAccessControl, group);
539
- }
540
- }
541
- });
542
- groupedSetWrites.forEach((group, accessControl) => {
543
- WebStorage.setSecureAccessControl(accessControl);
544
- WebStorage.setBatch(group.keys, group.values, _Storage.StorageScope.Secure);
545
- });
546
- if (keysToRemove.length > 0) {
547
- WebStorage.removeBatch(keysToRemove, _Storage.StorageScope.Secure);
548
- }
549
- }
550
- function scheduleDiskWrite(key, value) {
551
- pendingDiskWrites.set(key, {
552
- key,
553
- value
554
- });
555
- if (diskFlushScheduled) {
556
- return;
557
- }
558
- diskFlushScheduled = true;
559
- runMicrotask(flushDiskWrites);
560
- }
561
- function scheduleSecureWrite(key, value, accessControl) {
562
- const pendingWrite = {
563
- key,
564
- value
565
- };
566
- if (accessControl !== undefined) {
567
- pendingWrite.accessControl = accessControl;
568
- }
569
- pendingSecureWrites.set(key, pendingWrite);
570
- if (secureFlushScheduled) {
571
- return;
572
- }
573
- secureFlushScheduled = true;
574
- runMicrotask(flushSecureWrites);
575
- }
576
270
  const WebStorage = {
577
271
  name: "Storage",
578
272
  equals: other => other === WebStorage,
@@ -586,7 +280,7 @@ const WebStorage = {
586
280
  backend.setItem(storageKey, value);
587
281
  });
588
282
  ensureWebScopeKeyIndex(scope).add(key);
589
- notifyKeyListeners(getScopedListeners(scope), key);
283
+ (0, _shared.notifyKeyListeners)(internals.getScopedListeners(scope), key);
590
284
  },
591
285
  get: (key, scope) => {
592
286
  if (scope !== _Storage.StorageScope.Disk && scope !== _Storage.StorageScope.Secure) {
@@ -615,7 +309,7 @@ const WebStorage = {
615
309
  });
616
310
  }
617
311
  ensureWebScopeKeyIndex(scope).delete(key);
618
- notifyKeyListeners(getScopedListeners(scope), key);
312
+ (0, _shared.notifyKeyListeners)(internals.getScopedListeners(scope), key);
619
313
  },
620
314
  clear: scope => {
621
315
  if (scope !== _Storage.StorageScope.Disk && scope !== _Storage.StorageScope.Secure) {
@@ -625,7 +319,7 @@ const WebStorage = {
625
319
  backend.clear();
626
320
  });
627
321
  ensureWebScopeKeyIndex(scope).clear();
628
- notifyAllListeners(getScopedListeners(scope));
322
+ (0, _shared.notifyAllListeners)(internals.getScopedListeners(scope));
629
323
  },
630
324
  setBatch: (keys, values, scope) => {
631
325
  if (scope !== _Storage.StorageScope.Disk && scope !== _Storage.StorageScope.Secure) {
@@ -653,8 +347,8 @@ const WebStorage = {
653
347
  });
654
348
  const keyIndex = ensureWebScopeKeyIndex(scope);
655
349
  entries.forEach(([storageKey]) => keyIndex.add(scope === _Storage.StorageScope.Secure ? storageKey.slice(SECURE_WEB_PREFIX.length) : storageKey));
656
- const listeners = getScopedListeners(scope);
657
- keys.forEach(key => notifyKeyListeners(listeners, key));
350
+ const listeners = internals.getScopedListeners(scope);
351
+ keys.forEach(key => (0, _shared.notifyKeyListeners)(listeners, key));
658
352
  },
659
353
  getBatch: (keys, scope) => {
660
354
  if (scope !== _Storage.StorageScope.Disk && scope !== _Storage.StorageScope.Secure) {
@@ -697,8 +391,8 @@ const WebStorage = {
697
391
  }
698
392
  const keyIndex = ensureWebScopeKeyIndex(scope);
699
393
  keys.forEach(key => keyIndex.delete(key));
700
- const listeners = getScopedListeners(scope);
701
- keys.forEach(key => notifyKeyListeners(listeners, key));
394
+ const listeners = internals.getScopedListeners(scope);
395
+ keys.forEach(key => (0, _shared.notifyKeyListeners)(listeners, key));
702
396
  },
703
397
  removeByPrefix: (prefix, scope) => {
704
398
  if (scope !== _Storage.StorageScope.Disk && scope !== _Storage.StorageScope.Secure) {
@@ -739,7 +433,7 @@ const WebStorage = {
739
433
  return 0;
740
434
  },
741
435
  setSecureAccessControl: level => {
742
- assertAccessControlLevel(level);
436
+ (0, _shared.assertAccessControlLevel)(level);
743
437
  },
744
438
  setSecureWritesAsync: _enabled => {},
745
439
  setKeychainAccessGroup: () => {},
@@ -747,14 +441,14 @@ const WebStorage = {
747
441
  WebStorage.setSecureBiometricWithLevel(key, value, _Storage.BiometricLevel.BiometryOnly);
748
442
  },
749
443
  setSecureBiometricWithLevel: (key, value, level) => {
750
- assertBiometricLevel(level);
444
+ (0, _shared.assertBiometricLevel)(level);
751
445
  if (level === _Storage.BiometricLevel.None) {
752
446
  withWebBackendOperation(_Storage.StorageScope.Secure, "setSecure", backend => {
753
447
  backend.removeItem(toBiometricStorageKey(key));
754
448
  backend.setItem(toSecureStorageKey(key), value);
755
449
  });
756
450
  ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).add(key);
757
- notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), key);
451
+ (0, _shared.notifyKeyListeners)(internals.getScopedListeners(_Storage.StorageScope.Secure), key);
758
452
  return;
759
453
  }
760
454
  if (typeof __DEV__ !== "undefined" && __DEV__ && !hasWarnedAboutWebBiometricFallback) {
@@ -763,7 +457,7 @@ const WebStorage = {
763
457
  }
764
458
  withWebBackendOperation(_Storage.StorageScope.Secure, "setSecureBiometric", backend => backend.setItem(toBiometricStorageKey(key), value));
765
459
  ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).add(key);
766
- notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), key);
460
+ (0, _shared.notifyKeyListeners)(internals.getScopedListeners(_Storage.StorageScope.Secure), key);
767
461
  },
768
462
  getSecureBiometric: key => {
769
463
  const value = withWebBackendOperation(_Storage.StorageScope.Secure, "getSecureBiometric", backend => backend.getItem(toBiometricStorageKey(key)));
@@ -774,7 +468,7 @@ const WebStorage = {
774
468
  if (withWebBackendOperation(_Storage.StorageScope.Secure, "deleteSecureBiometric:getItem", backend => backend.getItem(toSecureStorageKey(key))) === null) {
775
469
  ensureWebScopeKeyIndex(_Storage.StorageScope.Secure).delete(key);
776
470
  }
777
- notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), key);
471
+ (0, _shared.notifyKeyListeners)(internals.getScopedListeners(_Storage.StorageScope.Secure), key);
778
472
  },
779
473
  hasSecureBiometric: key => {
780
474
  return withWebBackendOperation(_Storage.StorageScope.Secure, "hasSecureBiometric", backend => backend.getItem(toBiometricStorageKey(key))) !== null;
@@ -801,364 +495,42 @@ const WebStorage = {
801
495
  keyIndex.delete(key);
802
496
  }
803
497
  });
804
- const listeners = getScopedListeners(_Storage.StorageScope.Secure);
805
- keysToNotify.forEach(key => notifyKeyListeners(listeners, key));
498
+ const listeners = internals.getScopedListeners(_Storage.StorageScope.Secure);
499
+ keysToNotify.forEach(key => (0, _shared.notifyKeyListeners)(listeners, key));
806
500
  }
807
501
  };
808
- function getRawValue(key, scope) {
809
- (0, _internal.assertValidScope)(scope);
810
- if (scope === _Storage.StorageScope.Memory) {
811
- const value = memoryStore.get(key);
812
- return typeof value === "string" ? value : undefined;
813
- }
814
- if (scope === _Storage.StorageScope.Disk && hasPendingDiskWrite(key)) {
815
- return readPendingDiskWrite(key);
816
- }
817
- if (scope === _Storage.StorageScope.Secure && hasPendingSecureWrite(key)) {
818
- return readPendingSecureWrite(key);
819
- }
820
- return WebStorage.get(key, scope);
821
- }
822
- function setRawValue(key, value, scope) {
823
- (0, _internal.assertValidScope)(scope);
824
- const oldValue = scope === _Storage.StorageScope.Memory ? getEventRawValue(scope, key) : undefined;
825
- if (scope === _Storage.StorageScope.Memory) {
826
- memoryStore.set(key, value);
827
- notifyKeyListeners(memoryListeners, key);
828
- emitKeyChange(scope, key, oldValue, value, "set", "memory");
829
- return;
830
- }
831
- if (scope === _Storage.StorageScope.Disk) {
832
- cacheRawValue(scope, key, value);
833
- if (diskWritesAsync) {
834
- scheduleDiskWrite(key, value);
835
- emitKeyChange(scope, key, oldValue, value, "set", "web");
836
- return;
837
- }
838
- flushDiskWrites();
839
- clearPendingDiskWrite(key);
840
- }
841
- if (scope === _Storage.StorageScope.Secure) {
842
- flushSecureWrites();
843
- clearPendingSecureWrite(key);
844
- }
845
- WebStorage.set(key, value, scope);
846
- cacheRawValue(scope, key, value);
847
- emitKeyChange(scope, key, oldValue, value, "set", "web");
848
- }
849
- function removeRawValue(key, scope) {
850
- (0, _internal.assertValidScope)(scope);
851
- const oldValue = getEventRawValue(scope, key);
852
- if (scope === _Storage.StorageScope.Memory) {
853
- memoryStore.delete(key);
854
- notifyKeyListeners(memoryListeners, key);
855
- emitKeyChange(scope, key, oldValue, undefined, "remove", "memory");
856
- return;
857
- }
858
- if (scope === _Storage.StorageScope.Disk) {
859
- cacheRawValue(scope, key, undefined);
860
- if (diskWritesAsync) {
861
- scheduleDiskWrite(key, undefined);
862
- emitKeyChange(scope, key, oldValue, undefined, "remove", "web");
863
- return;
864
- }
865
- flushDiskWrites();
866
- clearPendingDiskWrite(key);
867
- }
868
- if (scope === _Storage.StorageScope.Secure) {
869
- flushSecureWrites();
870
- clearPendingSecureWrite(key);
871
- }
872
- WebStorage.remove(key, scope);
873
- cacheRawValue(scope, key, undefined);
874
- emitKeyChange(scope, key, oldValue, undefined, "remove", "web");
875
- }
876
- function readMigrationVersion(scope) {
877
- const raw = getRawValue(_internal.MIGRATION_VERSION_KEY, scope);
878
- if (raw === undefined) {
879
- return 0;
880
- }
881
- const parsed = Number.parseInt(raw, 10);
882
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
883
- }
884
- function writeMigrationVersion(scope, version) {
885
- setRawValue(_internal.MIGRATION_VERSION_KEY, String(version), scope);
502
+ function buildWebAdapter(coreInternals) {
503
+ internals = coreInternals;
504
+ return {
505
+ backend: WebStorage,
506
+ changeSource: "web",
507
+ applyAccessControlOnSecureRawWrite: false,
508
+ flushDiskWritesOnImport: true,
509
+ ensureScopeSubscription: () => {
510
+ ensureExternalSyncSubscriptions();
511
+ },
512
+ maybeCleanupScopeSubscription: () => {},
513
+ onWillEmitChanges: () => {},
514
+ getSecureMetadataProfile: () => ({
515
+ backend: getBackendName(_Storage.StorageScope.Secure, webSecureStorageBackend),
516
+ encrypted: getWebSecureEncryptionStatus(webSecureStorageBackend),
517
+ hardwareBacked: "unavailable"
518
+ })
519
+ };
886
520
  }
521
+ const core = (0, _storageCore.createStorageCore)(buildWebAdapter);
887
522
  const storage = exports.storage = {
888
- subscribe: (scope, listener) => {
889
- (0, _internal.assertValidScope)(scope);
890
- if (scope !== _Storage.StorageScope.Memory) {
891
- ensureExternalSyncSubscriptions();
892
- }
893
- return storageEvents.subscribe(scope, listener);
894
- },
895
- subscribeKey: (scope, key, listener) => {
896
- (0, _internal.assertValidScope)(scope);
897
- if (scope !== _Storage.StorageScope.Memory) {
898
- ensureExternalSyncSubscriptions();
899
- }
900
- return storageEvents.subscribeKey(scope, key, listener);
901
- },
902
- subscribePrefix: (scope, prefix, listener) => {
903
- (0, _internal.assertValidScope)(scope);
904
- if (scope !== _Storage.StorageScope.Memory) {
905
- ensureExternalSyncSubscriptions();
906
- }
907
- return storageEvents.subscribePrefix(scope, prefix, listener);
908
- },
909
- subscribeNamespace: (namespace, scope, listener) => {
910
- return storage.subscribePrefix(scope, (0, _internal.prefixKey)(namespace, ""), listener);
911
- },
912
- setEventObserver: (observer, options = {}) => {
913
- eventObserver = observer;
914
- eventObserverRedactSecureValues = options.redactSecureValues !== false;
915
- if (observer) {
916
- ensureExternalSyncSubscriptions();
917
- }
918
- },
919
- clear: scope => {
920
- measureOperation("storage:clear", scope, () => {
921
- const previousValues = shouldReadPreviousEventValues(scope) ? storage.getAll(scope) : {};
922
- if (scope === _Storage.StorageScope.Memory) {
923
- memoryStore.clear();
924
- notifyAllListeners(memoryListeners);
925
- emitBatchChange(scope, "clear", "memory", Object.keys(previousValues).map(key => createKeyChange(scope, key, previousValues[key], undefined, "clear", "memory")));
926
- return;
927
- }
928
- if (scope === _Storage.StorageScope.Disk) {
929
- flushDiskWrites();
930
- pendingDiskWrites.clear();
931
- }
932
- if (scope === _Storage.StorageScope.Secure) {
933
- flushSecureWrites();
934
- pendingSecureWrites.clear();
935
- }
936
- clearScopeRawCache(scope);
937
- WebStorage.clear(scope);
938
- emitBatchChange(scope, "clear", "web", Object.keys(previousValues).map(key => createKeyChange(scope, key, previousValues[key], undefined, "clear", "web")));
939
- });
940
- },
941
- clearAll: () => {
942
- measureOperation("storage:clearAll", _Storage.StorageScope.Memory, () => {
943
- storage.clear(_Storage.StorageScope.Memory);
944
- storage.clear(_Storage.StorageScope.Disk);
945
- storage.clear(_Storage.StorageScope.Secure);
946
- }, 3);
947
- },
948
- clearNamespace: (namespace, scope) => {
949
- measureOperation("storage:clearNamespace", scope, () => {
950
- (0, _internal.assertValidScope)(scope);
951
- if (scope === _Storage.StorageScope.Memory) {
952
- const affectedKeys = Array.from(memoryStore.keys()).filter(key => (0, _internal.isNamespaced)(key, namespace));
953
- const previousValues = affectedKeys.map(key => ({
954
- key,
955
- value: getEventRawValue(scope, key)
956
- }));
957
- if (affectedKeys.length === 0) {
958
- return;
959
- }
960
- affectedKeys.forEach(key => {
961
- memoryStore.delete(key);
962
- });
963
- affectedKeys.forEach(key => notifyKeyListeners(memoryListeners, key));
964
- emitBatchChange(scope, "clearNamespace", "memory", previousValues.map(({
965
- key,
966
- value
967
- }) => createKeyChange(scope, key, value, undefined, "clearNamespace", "memory")));
968
- return;
969
- }
970
- const keyPrefix = (0, _internal.prefixKey)(namespace, "");
971
- const previousValues = shouldReadPreviousEventValues(scope) ? storage.getByPrefix(keyPrefix, scope) : {};
972
- if (scope === _Storage.StorageScope.Disk) {
973
- flushDiskWrites();
974
- }
975
- if (scope === _Storage.StorageScope.Secure) {
976
- flushSecureWrites();
977
- }
978
- const scopeCache = getScopeRawCache(scope);
979
- for (const key of scopeCache.keys()) {
980
- if ((0, _internal.isNamespaced)(key, namespace)) {
981
- scopeCache.delete(key);
982
- }
983
- }
984
- WebStorage.removeByPrefix(keyPrefix, scope);
985
- emitBatchChange(scope, "clearNamespace", "web", Object.keys(previousValues).map(key => createKeyChange(scope, key, previousValues[key], undefined, "clearNamespace", "web")));
986
- });
987
- },
988
- clearBiometric: () => {
989
- measureOperation("storage:clearBiometric", _Storage.StorageScope.Secure, () => {
990
- WebStorage.clearSecureBiometric();
991
- });
992
- },
993
- has: (key, scope) => {
994
- return measureOperation("storage:has", scope, () => {
995
- (0, _internal.assertValidScope)(scope);
996
- if (scope === _Storage.StorageScope.Memory) return memoryStore.has(key);
997
- if (scope === _Storage.StorageScope.Disk) {
998
- flushDiskWrites();
999
- }
1000
- if (scope === _Storage.StorageScope.Secure) {
1001
- flushSecureWrites();
1002
- }
1003
- return WebStorage.has(key, scope);
1004
- });
1005
- },
1006
- getAllKeys: scope => {
1007
- return measureOperation("storage:getAllKeys", scope, () => {
1008
- (0, _internal.assertValidScope)(scope);
1009
- if (scope === _Storage.StorageScope.Memory) return Array.from(memoryStore.keys());
1010
- if (scope === _Storage.StorageScope.Disk) {
1011
- flushDiskWrites();
1012
- }
1013
- if (scope === _Storage.StorageScope.Secure) {
1014
- flushSecureWrites();
1015
- }
1016
- return WebStorage.getAllKeys(scope);
1017
- });
1018
- },
1019
- getKeysByPrefix: (prefix, scope) => {
1020
- return measureOperation("storage:getKeysByPrefix", scope, () => {
1021
- (0, _internal.assertValidScope)(scope);
1022
- if (scope === _Storage.StorageScope.Memory) {
1023
- return Array.from(memoryStore.keys()).filter(key => key.startsWith(prefix));
1024
- }
1025
- if (scope === _Storage.StorageScope.Disk) {
1026
- flushDiskWrites();
1027
- }
1028
- if (scope === _Storage.StorageScope.Secure) {
1029
- flushSecureWrites();
1030
- }
1031
- return WebStorage.getKeysByPrefix(prefix, scope);
1032
- });
1033
- },
1034
- getByPrefix: (prefix, scope) => {
1035
- return measureOperation("storage:getByPrefix", scope, () => {
1036
- const result = {};
1037
- const keys = storage.getKeysByPrefix(prefix, scope);
1038
- if (keys.length === 0) {
1039
- return result;
1040
- }
1041
- if (scope === _Storage.StorageScope.Memory) {
1042
- keys.forEach(key => {
1043
- const value = memoryStore.get(key);
1044
- if (typeof value === "string") {
1045
- result[key] = value;
1046
- }
1047
- });
1048
- return result;
1049
- }
1050
- if (scope === _Storage.StorageScope.Disk) {
1051
- flushDiskWrites();
1052
- }
1053
- if (scope === _Storage.StorageScope.Secure) {
1054
- flushSecureWrites();
1055
- }
1056
- const values = WebStorage.getBatch(keys, scope);
1057
- keys.forEach((key, index) => {
1058
- const value = values[index];
1059
- if (value !== undefined) {
1060
- result[key] = value;
1061
- }
1062
- });
1063
- return result;
1064
- });
1065
- },
1066
- getAll: scope => {
1067
- return measureOperation("storage:getAll", scope, () => {
1068
- (0, _internal.assertValidScope)(scope);
1069
- const result = {};
1070
- if (scope === _Storage.StorageScope.Memory) {
1071
- memoryStore.forEach((value, key) => {
1072
- if (typeof value === "string") result[key] = value;
1073
- });
1074
- return result;
1075
- }
1076
- if (scope === _Storage.StorageScope.Disk) {
1077
- flushDiskWrites();
1078
- }
1079
- if (scope === _Storage.StorageScope.Secure) {
1080
- flushSecureWrites();
1081
- }
1082
- const keys = WebStorage.getAllKeys(scope);
1083
- if (keys.length === 0) return {};
1084
- const values = WebStorage.getBatch(keys, scope);
1085
- keys.forEach((key, index) => {
1086
- const val = values[index];
1087
- if (val !== undefined && val !== null) {
1088
- result[key] = val;
1089
- }
1090
- });
1091
- return result;
1092
- });
1093
- },
1094
- export: (scope, options = {}) => {
1095
- if (scope === _Storage.StorageScope.Secure && options.includeSecureValues !== true) {
1096
- throw new Error("NitroStorage: exporting Secure scope exposes raw secret values. Pass { includeSecureValues: true } or use exportSecureUnsafe().");
1097
- }
1098
- return measureOperation("storage:export", scope, () => storage.getAll(scope));
1099
- },
1100
- exportSecureUnsafe: () => {
1101
- return measureOperation("storage:exportSecureUnsafe", _Storage.StorageScope.Secure, () => storage.getAll(_Storage.StorageScope.Secure));
1102
- },
1103
- size: scope => {
1104
- return measureOperation("storage:size", scope, () => {
1105
- (0, _internal.assertValidScope)(scope);
1106
- if (scope === _Storage.StorageScope.Memory) return memoryStore.size;
1107
- if (scope === _Storage.StorageScope.Disk) {
1108
- flushDiskWrites();
1109
- }
1110
- if (scope === _Storage.StorageScope.Secure) {
1111
- flushSecureWrites();
1112
- }
1113
- return WebStorage.size(scope);
1114
- });
1115
- },
523
+ ...core.storage,
1116
524
  setAccessControl: level => {
1117
- assertAccessControlLevel(level);
1118
- secureDefaultAccessControl = level;
1119
- recordMetric("storage:setAccessControl", _Storage.StorageScope.Secure, 0);
525
+ (0, _shared.assertAccessControlLevel)(level);
526
+ internals.setSecureDefaultAccessControl(level);
527
+ internals.recordMetric("storage:setAccessControl", _Storage.StorageScope.Secure, 0);
1120
528
  },
1121
529
  setSecureWritesAsync: _enabled => {
1122
- recordMetric("storage:setSecureWritesAsync", _Storage.StorageScope.Secure, 0);
1123
- },
1124
- setDiskWritesAsync: enabled => {
1125
- measureOperation("storage:setDiskWritesAsync", _Storage.StorageScope.Disk, () => {
1126
- diskWritesAsync = enabled;
1127
- if (!enabled) {
1128
- flushDiskWrites();
1129
- }
1130
- });
1131
- },
1132
- flushDiskWrites: () => {
1133
- measureOperation("storage:flushDiskWrites", _Storage.StorageScope.Disk, () => {
1134
- flushDiskWrites();
1135
- });
1136
- },
1137
- flushSecureWrites: () => {
1138
- measureOperation("storage:flushSecureWrites", _Storage.StorageScope.Secure, () => {
1139
- flushSecureWrites();
1140
- });
530
+ internals.recordMetric("storage:setSecureWritesAsync", _Storage.StorageScope.Secure, 0);
1141
531
  },
1142
532
  setKeychainAccessGroup: _group => {
1143
- recordMetric("storage:setKeychainAccessGroup", _Storage.StorageScope.Secure, 0);
1144
- },
1145
- setMetricsObserver: observer => {
1146
- metricsObserver = observer;
1147
- },
1148
- getMetricsSnapshot: () => {
1149
- const snapshot = {};
1150
- metricsCounters.forEach((value, key) => {
1151
- snapshot[key] = {
1152
- count: value.count,
1153
- totalDurationMs: value.totalDurationMs,
1154
- avgDurationMs: value.count === 0 ? 0 : value.totalDurationMs / value.count,
1155
- maxDurationMs: value.maxDurationMs
1156
- };
1157
- });
1158
- return snapshot;
1159
- },
1160
- resetMetrics: () => {
1161
- metricsCounters.clear();
533
+ internals.recordMetric("storage:setKeychainAccessGroup", _Storage.StorageScope.Secure, 0);
1162
534
  },
1163
535
  getCapabilities: () => ({
1164
536
  platform: "web",
@@ -1195,85 +567,24 @@ const storage = exports.storage = {
1195
567
  persistsTimestamps: false
1196
568
  }
1197
569
  };
1198
- },
1199
- getSecureMetadata: key => {
1200
- return measureOperation("storage:getSecureMetadata", _Storage.StorageScope.Secure, () => {
1201
- flushSecureWrites();
1202
- const biometricProtected = WebStorage.hasSecureBiometric(key);
1203
- const exists = biometricProtected || WebStorage.has(key, _Storage.StorageScope.Secure);
1204
- let kind = "missing";
1205
- if (exists) {
1206
- kind = biometricProtected ? "biometric" : "secure";
1207
- }
1208
- return {
1209
- key,
1210
- exists,
1211
- kind,
1212
- backend: getBackendName(_Storage.StorageScope.Secure, webSecureStorageBackend),
1213
- encrypted: getWebSecureEncryptionStatus(webSecureStorageBackend),
1214
- hardwareBacked: "unavailable",
1215
- biometricProtected,
1216
- valueExposed: false
1217
- };
1218
- });
1219
- },
1220
- getAllSecureMetadata: () => {
1221
- return measureOperation("storage:getAllSecureMetadata", _Storage.StorageScope.Secure, () => {
1222
- flushSecureWrites();
1223
- return WebStorage.getAllKeys(_Storage.StorageScope.Secure).map(key => storage.getSecureMetadata(key));
1224
- });
1225
- },
1226
- getString: (key, scope) => {
1227
- return measureOperation("storage:getString", scope, () => {
1228
- return getRawValue(key, scope);
1229
- });
1230
- },
1231
- setString: (key, value, scope) => {
1232
- measureOperation("storage:setString", scope, () => {
1233
- setRawValue(key, value, scope);
1234
- });
1235
- },
1236
- deleteString: (key, scope) => {
1237
- measureOperation("storage:deleteString", scope, () => {
1238
- removeRawValue(key, scope);
1239
- });
1240
- },
1241
- import: (data, scope) => {
1242
- const keys = Object.keys(data);
1243
- measureOperation("storage:import", scope, () => {
1244
- (0, _internal.assertValidScope)(scope);
1245
- if (keys.length === 0) return;
1246
- const values = keys.map(k => data[k]);
1247
- const changes = keys.map((key, index) => createKeyChange(scope, key, getEventRawValue(scope, key), values[index], "import", scope === _Storage.StorageScope.Memory ? "memory" : "web"));
1248
- if (scope === _Storage.StorageScope.Memory) {
1249
- keys.forEach((key, index) => {
1250
- memoryStore.set(key, values[index]);
1251
- });
1252
- keys.forEach(key => notifyKeyListeners(memoryListeners, key));
1253
- emitBatchChange(scope, "import", "memory", changes);
1254
- return;
1255
- }
1256
- if (scope === _Storage.StorageScope.Secure) {
1257
- flushSecureWrites();
1258
- WebStorage.setSecureAccessControl(secureDefaultAccessControl);
1259
- }
1260
- if (scope === _Storage.StorageScope.Disk) {
1261
- flushDiskWrites();
1262
- }
1263
- WebStorage.setBatch(keys, values, scope);
1264
- keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
1265
- emitBatchChange(scope, "import", "web", changes);
1266
- }, keys.length);
1267
570
  }
1268
571
  };
572
+ const createStorageItem = exports.createStorageItem = core.createStorageItem;
573
+ const getBatch = exports.getBatch = core.getBatch;
574
+ const setBatch = exports.setBatch = core.setBatch;
575
+ const removeBatch = exports.removeBatch = core.removeBatch;
576
+ const registerMigration = exports.registerMigration = core.registerMigration;
577
+ const migrateToLatest = exports.migrateToLatest = core.migrateToLatest;
578
+ const runTransaction = exports.runTransaction = core.runTransaction;
579
+ const createSecureAuthStorage = exports.createSecureAuthStorage = core.createSecureAuthStorage;
1269
580
  function setWebSecureStorageBackend(backend) {
1270
581
  const previousBackend = webSecureStorageBackend;
1271
582
  const nextBackend = backend ?? createDefaultSecureBackend();
1272
- pendingSecureWrites.clear();
583
+ internals.clearAllPendingSecureWrites();
1273
584
  resetBackendChangeSubscription(_Storage.StorageScope.Secure);
1274
585
  webSecureStorageBackend = nextBackend;
1275
586
  hydratedWebScopeKeyIndex.delete(_Storage.StorageScope.Secure);
1276
- clearScopeRawCache(_Storage.StorageScope.Secure);
587
+ internals.clearScopeRawCache(_Storage.StorageScope.Secure);
1277
588
  ensureExternalSyncSubscriptions();
1278
589
  if (previousBackend !== nextBackend) {
1279
590
  closeWebBackend(_Storage.StorageScope.Secure, previousBackend);
@@ -1285,11 +596,11 @@ function getWebSecureStorageBackend() {
1285
596
  function setWebDiskStorageBackend(backend) {
1286
597
  const previousBackend = webDiskStorageBackend;
1287
598
  const nextBackend = backend ?? createDefaultDiskBackend();
1288
- pendingDiskWrites.clear();
599
+ internals.clearAllPendingDiskWrites();
1289
600
  resetBackendChangeSubscription(_Storage.StorageScope.Disk);
1290
601
  webDiskStorageBackend = nextBackend;
1291
602
  hydratedWebScopeKeyIndex.delete(_Storage.StorageScope.Disk);
1292
- clearScopeRawCache(_Storage.StorageScope.Disk);
603
+ internals.clearScopeRawCache(_Storage.StorageScope.Disk);
1293
604
  ensureExternalSyncSubscriptions();
1294
605
  if (previousBackend !== nextBackend) {
1295
606
  closeWebBackend(_Storage.StorageScope.Disk, previousBackend);
@@ -1299,8 +610,8 @@ function getWebDiskStorageBackend() {
1299
610
  return webDiskStorageBackend;
1300
611
  }
1301
612
  async function flushWebStorageBackends() {
1302
- flushDiskWrites();
1303
- flushSecureWrites();
613
+ internals.flushDiskWrites();
614
+ internals.flushSecureWrites();
1304
615
  const flushes = [];
1305
616
  const diskFlush = webDiskStorageBackend?.flush;
1306
617
  const secureFlush = webSecureStorageBackend?.flush;
@@ -1312,782 +623,4 @@ async function flushWebStorageBackends() {
1312
623
  }
1313
624
  await Promise.all(flushes);
1314
625
  }
1315
- function canUseRawBatchPath(item) {
1316
- return item._hasExpiration === false && item._hasValidation === false && item._isBiometric !== true && item._secureAccessControl === undefined;
1317
- }
1318
- function canUseSecureRawBatchPath(item) {
1319
- return item._hasExpiration === false && item._hasValidation === false && item._isBiometric !== true;
1320
- }
1321
- function defaultSerialize(value) {
1322
- return (0, _internal.serializeWithPrimitiveFastPath)(value);
1323
- }
1324
- function defaultDeserialize(value) {
1325
- return (0, _internal.deserializeWithPrimitiveFastPath)(value);
1326
- }
1327
- function createStorageItem(config) {
1328
- const storageKey = (0, _internal.prefixKey)(config.namespace, config.key);
1329
- const serialize = config.serialize ?? defaultSerialize;
1330
- const deserialize = config.deserialize ?? defaultDeserialize;
1331
- const isMemory = config.scope === _Storage.StorageScope.Memory;
1332
- const resolvedBiometricLevel = config.scope === _Storage.StorageScope.Secure ? config.biometricLevel ?? (config.biometric === true ? _Storage.BiometricLevel.BiometryOnly : _Storage.BiometricLevel.None) : _Storage.BiometricLevel.None;
1333
- const isBiometric = resolvedBiometricLevel !== _Storage.BiometricLevel.None;
1334
- const secureAccessControl = config.accessControl;
1335
- const validate = config.validate;
1336
- const onValidationError = config.onValidationError;
1337
- const expiration = config.expiration;
1338
- const onExpired = config.onExpired;
1339
- const expirationTtlMs = expiration?.ttlMs;
1340
- const memoryExpiration = expiration && isMemory ? new Map() : null;
1341
- const readCache = !isMemory && config.readCache === true;
1342
- const coalesceDiskWrites = config.scope === _Storage.StorageScope.Disk && config.coalesceDiskWrites === true;
1343
- const coalesceSecureWrites = config.scope === _Storage.StorageScope.Secure && config.coalesceSecureWrites === true && !isBiometric;
1344
- const defaultValue = config.defaultValue;
1345
- const nonMemoryScope = config.scope === _Storage.StorageScope.Disk ? _Storage.StorageScope.Disk : config.scope === _Storage.StorageScope.Secure ? _Storage.StorageScope.Secure : null;
1346
- if (expiration && expiration.ttlMs <= 0) {
1347
- throw new Error("expiration.ttlMs must be greater than 0.");
1348
- }
1349
- if (config.scope === _Storage.StorageScope.Secure) {
1350
- assertBiometricLevel(resolvedBiometricLevel);
1351
- if (secureAccessControl !== undefined) {
1352
- assertAccessControlLevel(secureAccessControl);
1353
- }
1354
- }
1355
- const listeners = new Set();
1356
- let unsubscribe = null;
1357
- let lastRaw = undefined;
1358
- let lastValue;
1359
- let hasLastValue = false;
1360
- let lastExpiresAt = undefined;
1361
- const invalidateParsedCache = () => {
1362
- lastRaw = undefined;
1363
- lastValue = undefined;
1364
- hasLastValue = false;
1365
- lastExpiresAt = undefined;
1366
- };
1367
- const ensureSubscription = () => {
1368
- if (unsubscribe) {
1369
- return;
1370
- }
1371
- const listener = () => {
1372
- invalidateParsedCache();
1373
- listeners.forEach(callback => callback());
1374
- };
1375
- if (isMemory) {
1376
- unsubscribe = addKeyListener(memoryListeners, storageKey, listener);
1377
- return;
1378
- }
1379
- ensureExternalSyncSubscriptions();
1380
- unsubscribe = addKeyListener(getScopedListeners(nonMemoryScope), storageKey, listener);
1381
- };
1382
- const readStoredRaw = () => {
1383
- if (isMemory) {
1384
- if (memoryExpiration) {
1385
- const expiresAt = memoryExpiration.get(storageKey);
1386
- if (expiresAt !== undefined && expiresAt <= Date.now()) {
1387
- memoryExpiration.delete(storageKey);
1388
- memoryStore.delete(storageKey);
1389
- notifyKeyListeners(memoryListeners, storageKey);
1390
- onExpired?.(storageKey);
1391
- return undefined;
1392
- }
1393
- }
1394
- return memoryStore.get(storageKey);
1395
- }
1396
- if (nonMemoryScope === _Storage.StorageScope.Disk) {
1397
- const pending = pendingDiskWrites.get(storageKey);
1398
- if (pending !== undefined) {
1399
- return pending.value;
1400
- }
1401
- }
1402
- if (nonMemoryScope === _Storage.StorageScope.Secure && !isBiometric) {
1403
- const pending = pendingSecureWrites.get(storageKey);
1404
- if (pending !== undefined) {
1405
- return pending.value;
1406
- }
1407
- }
1408
- if (readCache) {
1409
- const cache = getScopeRawCache(nonMemoryScope);
1410
- const cached = cache.get(storageKey);
1411
- if (cached !== undefined || cache.has(storageKey)) {
1412
- return cached;
1413
- }
1414
- }
1415
- if (isBiometric) {
1416
- return WebStorage.getSecureBiometric(storageKey);
1417
- }
1418
- const raw = WebStorage.get(storageKey, config.scope);
1419
- cacheRawValue(nonMemoryScope, storageKey, raw);
1420
- return raw;
1421
- };
1422
- const writeStoredRaw = rawValue => {
1423
- const oldValue = config.scope === _Storage.StorageScope.Memory ? getEventRawValue(config.scope, storageKey) : undefined;
1424
- if (isBiometric) {
1425
- WebStorage.setSecureBiometricWithLevel(storageKey, rawValue, resolvedBiometricLevel);
1426
- emitKeyChange(config.scope, storageKey, oldValue, rawValue, "set", "web");
1427
- return;
1428
- }
1429
- cacheRawValue(nonMemoryScope, storageKey, rawValue);
1430
- if (nonMemoryScope === _Storage.StorageScope.Disk) {
1431
- if (coalesceDiskWrites || diskWritesAsync) {
1432
- scheduleDiskWrite(storageKey, rawValue);
1433
- emitKeyChange(config.scope, storageKey, oldValue, rawValue, "set", "web");
1434
- return;
1435
- }
1436
- clearPendingDiskWrite(storageKey);
1437
- }
1438
- if (coalesceSecureWrites) {
1439
- scheduleSecureWrite(storageKey, rawValue, secureAccessControl ?? secureDefaultAccessControl);
1440
- emitKeyChange(config.scope, storageKey, oldValue, rawValue, "set", "web");
1441
- return;
1442
- }
1443
- if (nonMemoryScope === _Storage.StorageScope.Secure) {
1444
- clearPendingSecureWrite(storageKey);
1445
- }
1446
- WebStorage.set(storageKey, rawValue, config.scope);
1447
- emitKeyChange(config.scope, storageKey, oldValue, rawValue, "set", "web");
1448
- };
1449
- const removeStoredRaw = () => {
1450
- const oldValue = getEventRawValue(config.scope, storageKey);
1451
- if (isBiometric) {
1452
- WebStorage.deleteSecureBiometric(storageKey);
1453
- emitKeyChange(config.scope, storageKey, oldValue, undefined, "remove", "web");
1454
- return;
1455
- }
1456
- cacheRawValue(nonMemoryScope, storageKey, undefined);
1457
- if (nonMemoryScope === _Storage.StorageScope.Disk) {
1458
- if (coalesceDiskWrites || diskWritesAsync) {
1459
- scheduleDiskWrite(storageKey, undefined);
1460
- emitKeyChange(config.scope, storageKey, oldValue, undefined, "remove", "web");
1461
- return;
1462
- }
1463
- clearPendingDiskWrite(storageKey);
1464
- }
1465
- if (coalesceSecureWrites) {
1466
- scheduleSecureWrite(storageKey, undefined, secureAccessControl ?? secureDefaultAccessControl);
1467
- emitKeyChange(config.scope, storageKey, oldValue, undefined, "remove", "web");
1468
- return;
1469
- }
1470
- if (nonMemoryScope === _Storage.StorageScope.Secure) {
1471
- clearPendingSecureWrite(storageKey);
1472
- }
1473
- WebStorage.remove(storageKey, config.scope);
1474
- emitKeyChange(config.scope, storageKey, oldValue, undefined, "remove", "web");
1475
- };
1476
- const writeValueWithoutValidation = value => {
1477
- if (isMemory) {
1478
- const oldValue = getEventRawValue(config.scope, storageKey);
1479
- if (memoryExpiration) {
1480
- memoryExpiration.set(storageKey, Date.now() + (expirationTtlMs ?? 0));
1481
- }
1482
- memoryStore.set(storageKey, value);
1483
- notifyKeyListeners(memoryListeners, storageKey);
1484
- emitKeyChange(config.scope, storageKey, oldValue, typeof value === "string" ? value : undefined, "set", "memory");
1485
- return;
1486
- }
1487
- const serialized = serialize(value);
1488
- if (expiration) {
1489
- const envelope = {
1490
- __nitroStorageEnvelope: true,
1491
- expiresAt: Date.now() + expiration.ttlMs,
1492
- payload: serialized
1493
- };
1494
- writeStoredRaw(JSON.stringify(envelope));
1495
- return;
1496
- }
1497
- writeStoredRaw(serialized);
1498
- };
1499
- const resolveInvalidValue = invalidValue => {
1500
- if (onValidationError) {
1501
- return onValidationError(invalidValue);
1502
- }
1503
- return defaultValue;
1504
- };
1505
- const ensureValidatedValue = (candidate, hadStoredValue) => {
1506
- if (!validate || validate(candidate)) {
1507
- return candidate;
1508
- }
1509
- const resolved = resolveInvalidValue(candidate);
1510
- if (validate && !validate(resolved)) {
1511
- return defaultValue;
1512
- }
1513
- if (hadStoredValue) {
1514
- writeValueWithoutValidation(resolved);
1515
- }
1516
- return resolved;
1517
- };
1518
- const getInternal = () => {
1519
- const raw = readStoredRaw();
1520
- if (!memoryExpiration && raw === lastRaw && hasLastValue) {
1521
- if (!expiration || lastExpiresAt === null) {
1522
- return lastValue;
1523
- }
1524
- if (typeof lastExpiresAt === "number") {
1525
- if (lastExpiresAt > Date.now()) {
1526
- return lastValue;
1527
- }
1528
- removeStoredRaw();
1529
- invalidateParsedCache();
1530
- onExpired?.(storageKey);
1531
- lastValue = ensureValidatedValue(defaultValue, false);
1532
- hasLastValue = true;
1533
- listeners.forEach(cb => cb());
1534
- return lastValue;
1535
- }
1536
- }
1537
- lastRaw = raw;
1538
- if (raw === undefined) {
1539
- lastExpiresAt = undefined;
1540
- lastValue = ensureValidatedValue(defaultValue, false);
1541
- hasLastValue = true;
1542
- return lastValue;
1543
- }
1544
- if (isMemory) {
1545
- lastExpiresAt = undefined;
1546
- lastValue = ensureValidatedValue(raw, true);
1547
- hasLastValue = true;
1548
- return lastValue;
1549
- }
1550
- if (typeof raw !== "string") {
1551
- lastExpiresAt = undefined;
1552
- lastValue = ensureValidatedValue(defaultValue, false);
1553
- hasLastValue = true;
1554
- return lastValue;
1555
- }
1556
- let deserializableRaw = raw;
1557
- if (expiration) {
1558
- let envelopeExpiresAt = null;
1559
- try {
1560
- const parsed = JSON.parse(raw);
1561
- if ((0, _internal.isStoredEnvelope)(parsed)) {
1562
- envelopeExpiresAt = parsed.expiresAt;
1563
- if (parsed.expiresAt <= Date.now()) {
1564
- removeStoredRaw();
1565
- invalidateParsedCache();
1566
- onExpired?.(storageKey);
1567
- lastValue = ensureValidatedValue(defaultValue, false);
1568
- hasLastValue = true;
1569
- listeners.forEach(cb => cb());
1570
- return lastValue;
1571
- }
1572
- deserializableRaw = parsed.payload;
1573
- }
1574
- } catch {
1575
- // Keep backward compatibility with legacy raw values.
1576
- }
1577
- lastExpiresAt = envelopeExpiresAt;
1578
- } else {
1579
- lastExpiresAt = undefined;
1580
- }
1581
- lastValue = ensureValidatedValue(deserialize(deserializableRaw), true);
1582
- hasLastValue = true;
1583
- return lastValue;
1584
- };
1585
- const getCurrentVersion = () => {
1586
- const raw = readStoredRaw();
1587
- return (0, _internal.toVersionToken)(raw);
1588
- };
1589
- const get = () => measureOperation("item:get", config.scope, () => getInternal());
1590
- const getWithVersion = () => measureOperation("item:getWithVersion", config.scope, () => ({
1591
- value: getInternal(),
1592
- version: getCurrentVersion()
1593
- }));
1594
- const set = valueOrFn => {
1595
- measureOperation("item:set", config.scope, () => {
1596
- const newValue = isUpdater(valueOrFn) ? valueOrFn(getInternal()) : valueOrFn;
1597
- if (validate && !validate(newValue)) {
1598
- throw new Error(`Validation failed for key "${storageKey}" in scope "${_Storage.StorageScope[config.scope]}".`);
1599
- }
1600
- invalidateParsedCache();
1601
- writeValueWithoutValidation(newValue);
1602
- });
1603
- };
1604
- const setIfVersion = (version, valueOrFn) => measureOperation("item:setIfVersion", config.scope, () => {
1605
- const currentVersion = getCurrentVersion();
1606
- if (currentVersion !== version) {
1607
- return false;
1608
- }
1609
- set(valueOrFn);
1610
- return true;
1611
- });
1612
- const deleteItem = () => {
1613
- measureOperation("item:delete", config.scope, () => {
1614
- invalidateParsedCache();
1615
- if (isMemory) {
1616
- const oldValue = getEventRawValue(config.scope, storageKey);
1617
- if (memoryExpiration) {
1618
- memoryExpiration.delete(storageKey);
1619
- }
1620
- memoryStore.delete(storageKey);
1621
- notifyKeyListeners(memoryListeners, storageKey);
1622
- emitKeyChange(config.scope, storageKey, oldValue, undefined, "remove", "memory");
1623
- return;
1624
- }
1625
- removeStoredRaw();
1626
- });
1627
- };
1628
- const hasItem = () => measureOperation("item:has", config.scope, () => {
1629
- if (isMemory) return memoryStore.has(storageKey);
1630
- if (isBiometric) return WebStorage.hasSecureBiometric(storageKey);
1631
- if (nonMemoryScope === _Storage.StorageScope.Disk) {
1632
- const pending = pendingDiskWrites.get(storageKey);
1633
- if (pending !== undefined) {
1634
- return pending.value !== undefined;
1635
- }
1636
- }
1637
- if (nonMemoryScope === _Storage.StorageScope.Secure) {
1638
- const pending = pendingSecureWrites.get(storageKey);
1639
- if (pending !== undefined) {
1640
- return pending.value !== undefined;
1641
- }
1642
- }
1643
- return WebStorage.has(storageKey, config.scope);
1644
- });
1645
- const subscribe = callback => {
1646
- ensureSubscription();
1647
- listeners.add(callback);
1648
- return () => {
1649
- listeners.delete(callback);
1650
- if (listeners.size === 0 && unsubscribe) {
1651
- unsubscribe();
1652
- unsubscribe = null;
1653
- }
1654
- };
1655
- };
1656
- const subscribeSelector = (selector, listener, options = {}) => {
1657
- const isEqual = options.isEqual ?? Object.is;
1658
- let currentValue = selector(getInternal());
1659
- if (options.fireImmediately === true) {
1660
- listener(currentValue, currentValue);
1661
- }
1662
- return subscribe(() => {
1663
- const nextValue = selector(getInternal());
1664
- if (isEqual(currentValue, nextValue)) {
1665
- return;
1666
- }
1667
- const previousValue = currentValue;
1668
- currentValue = nextValue;
1669
- listener(nextValue, previousValue);
1670
- });
1671
- };
1672
- const storageItem = {
1673
- get,
1674
- getWithVersion,
1675
- set,
1676
- setIfVersion,
1677
- delete: deleteItem,
1678
- has: hasItem,
1679
- subscribe,
1680
- subscribeSelector,
1681
- serialize,
1682
- deserialize,
1683
- _triggerListeners: () => {
1684
- invalidateParsedCache();
1685
- listeners.forEach(listener => listener());
1686
- },
1687
- _invalidateParsedCacheOnly: () => {
1688
- invalidateParsedCache();
1689
- },
1690
- _hasValidation: validate !== undefined,
1691
- _hasExpiration: expiration !== undefined,
1692
- _readCacheEnabled: readCache,
1693
- _isBiometric: isBiometric,
1694
- _biometricLevel: resolvedBiometricLevel,
1695
- _defaultValue: defaultValue,
1696
- ...(secureAccessControl !== undefined ? {
1697
- _secureAccessControl: secureAccessControl
1698
- } : {}),
1699
- scope: config.scope,
1700
- key: storageKey
1701
- };
1702
- return storageItem;
1703
- }
1704
- function getBatch(items, scope) {
1705
- return measureOperation("batch:get", scope, () => {
1706
- (0, _internal.assertBatchScope)(items, scope);
1707
- if (scope === _Storage.StorageScope.Memory) {
1708
- return items.map(item => item.get());
1709
- }
1710
- const useRawBatchPath = items.every(item => scope === _Storage.StorageScope.Secure ? canUseSecureRawBatchPath(item) : canUseRawBatchPath(item));
1711
- if (!useRawBatchPath) {
1712
- return items.map(item => item.get());
1713
- }
1714
- const rawValues = new Array(items.length);
1715
- const keysToFetch = [];
1716
- const keyIndexes = [];
1717
- items.forEach((item, index) => {
1718
- if (scope === _Storage.StorageScope.Disk) {
1719
- const pending = pendingDiskWrites.get(item.key);
1720
- if (pending !== undefined) {
1721
- rawValues[index] = pending.value;
1722
- return;
1723
- }
1724
- }
1725
- if (scope === _Storage.StorageScope.Secure) {
1726
- const pending = pendingSecureWrites.get(item.key);
1727
- if (pending !== undefined) {
1728
- rawValues[index] = pending.value;
1729
- return;
1730
- }
1731
- }
1732
- if (item._readCacheEnabled === true) {
1733
- const cache = getScopeRawCache(scope);
1734
- const cached = cache.get(item.key);
1735
- if (cached !== undefined || cache.has(item.key)) {
1736
- rawValues[index] = cached;
1737
- return;
1738
- }
1739
- }
1740
- keysToFetch.push(item.key);
1741
- keyIndexes.push(index);
1742
- });
1743
- if (keysToFetch.length > 0) {
1744
- const fetchedValues = WebStorage.getBatch(keysToFetch, scope);
1745
- fetchedValues.forEach((value, index) => {
1746
- const key = keysToFetch[index];
1747
- const targetIndex = keyIndexes[index];
1748
- if (key === undefined || targetIndex === undefined) {
1749
- return;
1750
- }
1751
- rawValues[targetIndex] = value;
1752
- cacheRawValue(scope, key, value);
1753
- });
1754
- }
1755
- return items.map((item, index) => {
1756
- const raw = rawValues[index];
1757
- if (raw === undefined) {
1758
- return asInternal(item)._defaultValue;
1759
- }
1760
- return item.deserialize(raw);
1761
- });
1762
- }, items.length);
1763
- }
1764
- function setBatch(items, scope) {
1765
- measureOperation("batch:set", scope, () => {
1766
- (0, _internal.assertBatchScope)(items.map(batchEntry => batchEntry.item), scope);
1767
- if (scope === _Storage.StorageScope.Memory) {
1768
- // Determine if any item needs per-item handling (validation or TTL)
1769
- const needsIndividualSets = items.some(({
1770
- item
1771
- }) => {
1772
- const internal = asInternal(item);
1773
- return internal._hasValidation || internal._hasExpiration;
1774
- });
1775
- if (needsIndividualSets) {
1776
- items.forEach(({
1777
- item,
1778
- value
1779
- }) => item.set(value));
1780
- return;
1781
- }
1782
- const changes = items.map(({
1783
- item,
1784
- value
1785
- }) => createKeyChange(scope, item.key, getEventRawValue(scope, item.key), typeof value === "string" ? value : undefined, "setBatch", "memory"));
1786
-
1787
- // Atomic write: update all values in memoryStore, invalidate caches, then batch-notify
1788
- items.forEach(({
1789
- item,
1790
- value
1791
- }) => {
1792
- memoryStore.set(item.key, value);
1793
- asInternal(item)._invalidateParsedCacheOnly();
1794
- });
1795
- items.forEach(({
1796
- item
1797
- }) => notifyKeyListeners(memoryListeners, item.key));
1798
- emitBatchChange(scope, "setBatch", "memory", changes);
1799
- return;
1800
- }
1801
- if (scope === _Storage.StorageScope.Secure) {
1802
- const secureEntries = items.map(({
1803
- item,
1804
- value
1805
- }) => ({
1806
- item,
1807
- value,
1808
- internal: asInternal(item)
1809
- }));
1810
- const canUseSecureBatchPath = secureEntries.every(({
1811
- internal
1812
- }) => canUseSecureRawBatchPath(internal));
1813
- if (!canUseSecureBatchPath) {
1814
- items.forEach(({
1815
- item,
1816
- value
1817
- }) => item.set(value));
1818
- return;
1819
- }
1820
- flushSecureWrites();
1821
- const keys = secureEntries.map(({
1822
- item
1823
- }) => item.key);
1824
- const oldValues = shouldReadPreviousEventValues(scope) ? WebStorage.getBatch(keys, scope) : [];
1825
- const groupedByAccessControl = new Map();
1826
- secureEntries.forEach(({
1827
- item,
1828
- value,
1829
- internal
1830
- }) => {
1831
- const accessControl = internal._secureAccessControl ?? secureDefaultAccessControl;
1832
- const existingGroup = groupedByAccessControl.get(accessControl);
1833
- const group = existingGroup ?? {
1834
- keys: [],
1835
- values: []
1836
- };
1837
- group.keys.push(item.key);
1838
- group.values.push(item.serialize(value));
1839
- if (!existingGroup) {
1840
- groupedByAccessControl.set(accessControl, group);
1841
- }
1842
- });
1843
- groupedByAccessControl.forEach((group, accessControl) => {
1844
- WebStorage.setSecureAccessControl(accessControl);
1845
- WebStorage.setBatch(group.keys, group.values, scope);
1846
- group.keys.forEach((key, index) => cacheRawValue(scope, key, group.values[index]));
1847
- });
1848
- emitBatchChange(scope, "setBatch", "web", secureEntries.map(({
1849
- item,
1850
- value
1851
- }, index) => createKeyChange(scope, item.key, oldValues[index], item.serialize(value), "setBatch", "web")));
1852
- return;
1853
- }
1854
- flushDiskWrites();
1855
- const useRawBatchPath = items.every(({
1856
- item
1857
- }) => canUseRawBatchPath(asInternal(item)));
1858
- if (!useRawBatchPath) {
1859
- items.forEach(({
1860
- item,
1861
- value
1862
- }) => item.set(value));
1863
- return;
1864
- }
1865
- const keys = items.map(entry => entry.item.key);
1866
- const values = items.map(entry => entry.item.serialize(entry.value));
1867
- const oldValues = shouldReadPreviousEventValues(scope) ? WebStorage.getBatch(keys, scope) : [];
1868
- WebStorage.setBatch(keys, values, scope);
1869
- keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
1870
- emitBatchChange(scope, "setBatch", "web", keys.map((key, index) => createKeyChange(scope, key, oldValues[index], values[index], "setBatch", "web")));
1871
- }, items.length);
1872
- }
1873
- function removeBatch(items, scope) {
1874
- measureOperation("batch:remove", scope, () => {
1875
- (0, _internal.assertBatchScope)(items, scope);
1876
- if (scope === _Storage.StorageScope.Memory) {
1877
- const changes = items.map(item => createKeyChange(scope, item.key, getEventRawValue(scope, item.key), undefined, "removeBatch", "memory"));
1878
- items.forEach(item => item.delete());
1879
- emitBatchChange(scope, "removeBatch", "memory", changes);
1880
- return;
1881
- }
1882
- const keys = items.map(item => item.key);
1883
- if (scope === _Storage.StorageScope.Disk) {
1884
- flushDiskWrites();
1885
- }
1886
- if (scope === _Storage.StorageScope.Secure) {
1887
- flushSecureWrites();
1888
- }
1889
- const oldValues = shouldReadPreviousEventValues(scope) ? WebStorage.getBatch(keys, scope) : [];
1890
- WebStorage.removeBatch(keys, scope);
1891
- keys.forEach(key => cacheRawValue(scope, key, undefined));
1892
- emitBatchChange(scope, "removeBatch", "web", keys.map((key, index) => createKeyChange(scope, key, oldValues[index], undefined, "removeBatch", "web")));
1893
- }, items.length);
1894
- }
1895
- function registerMigration(version, migration) {
1896
- if (!Number.isInteger(version) || version <= 0) {
1897
- throw new Error("Migration version must be a positive integer.");
1898
- }
1899
- if (registeredMigrations.has(version)) {
1900
- throw new Error(`Migration version ${version} is already registered.`);
1901
- }
1902
- registeredMigrations.set(version, migration);
1903
- }
1904
- function migrateToLatest(scope = _Storage.StorageScope.Disk) {
1905
- return measureOperation("migration:run", scope, () => {
1906
- (0, _internal.assertValidScope)(scope);
1907
- const currentVersion = readMigrationVersion(scope);
1908
- const versions = Array.from(registeredMigrations.keys()).filter(version => version > currentVersion).sort((a, b) => a - b);
1909
- let appliedVersion = currentVersion;
1910
- const context = {
1911
- scope,
1912
- getRaw: key => getRawValue(key, scope),
1913
- setRaw: (key, value) => setRawValue(key, value, scope),
1914
- removeRaw: key => removeRawValue(key, scope)
1915
- };
1916
- versions.forEach(version => {
1917
- const migration = registeredMigrations.get(version);
1918
- if (!migration) {
1919
- return;
1920
- }
1921
- migration(context);
1922
- appliedVersion = version;
1923
- });
1924
- if (appliedVersion !== currentVersion) {
1925
- writeMigrationVersion(scope, appliedVersion);
1926
- }
1927
- return appliedVersion;
1928
- });
1929
- }
1930
- function runTransaction(scope, transaction) {
1931
- return measureOperation("transaction:run", scope, () => {
1932
- (0, _internal.assertValidScope)(scope);
1933
- if (scope === _Storage.StorageScope.Disk) {
1934
- flushDiskWrites();
1935
- }
1936
- if (scope === _Storage.StorageScope.Secure) {
1937
- flushSecureWrites();
1938
- }
1939
- const NOT_SET = Symbol();
1940
- const rollback = new Map();
1941
- const rememberRollback = (key, item) => {
1942
- if (rollback.has(key)) {
1943
- return;
1944
- }
1945
- if (scope === _Storage.StorageScope.Memory) {
1946
- rollback.set(key, {
1947
- kind: "memory",
1948
- value: memoryStore.has(key) ? memoryStore.get(key) : NOT_SET
1949
- });
1950
- } else {
1951
- const internal = item ? item : undefined;
1952
- if (scope === _Storage.StorageScope.Secure && internal?._isBiometric === true) {
1953
- rollback.set(key, {
1954
- kind: "biometric",
1955
- value: WebStorage.getSecureBiometric(key),
1956
- level: internal._biometricLevel
1957
- });
1958
- return;
1959
- }
1960
- rollback.set(key, {
1961
- kind: "raw",
1962
- value: getRawValue(key, scope),
1963
- ...(scope === _Storage.StorageScope.Secure && internal?._secureAccessControl !== undefined ? {
1964
- accessControl: internal._secureAccessControl
1965
- } : {})
1966
- });
1967
- }
1968
- };
1969
- const tx = {
1970
- scope,
1971
- getRaw: key => getRawValue(key, scope),
1972
- setRaw: (key, value) => {
1973
- rememberRollback(key);
1974
- setRawValue(key, value, scope);
1975
- },
1976
- removeRaw: key => {
1977
- rememberRollback(key);
1978
- removeRawValue(key, scope);
1979
- },
1980
- getItem: item => {
1981
- (0, _internal.assertBatchScope)([item], scope);
1982
- return item.get();
1983
- },
1984
- setItem: (item, value) => {
1985
- (0, _internal.assertBatchScope)([item], scope);
1986
- rememberRollback(item.key, item);
1987
- item.set(value);
1988
- },
1989
- removeItem: item => {
1990
- (0, _internal.assertBatchScope)([item], scope);
1991
- rememberRollback(item.key, item);
1992
- item.delete();
1993
- }
1994
- };
1995
- try {
1996
- return transaction(tx);
1997
- } catch (error) {
1998
- const rollbackEntries = Array.from(rollback.entries()).reverse();
1999
- if (scope === _Storage.StorageScope.Memory) {
2000
- rollbackEntries.forEach(([key, record]) => {
2001
- if (record.value === NOT_SET) {
2002
- memoryStore.delete(key);
2003
- } else {
2004
- memoryStore.set(key, record.value);
2005
- }
2006
- notifyKeyListeners(memoryListeners, key);
2007
- });
2008
- } else {
2009
- const groupedKeysToSet = new Map();
2010
- const keysToRemove = [];
2011
- rollbackEntries.forEach(([key, record]) => {
2012
- if (record.kind === "biometric") {
2013
- if (record.value === undefined) {
2014
- WebStorage.deleteSecureBiometric(key);
2015
- } else {
2016
- WebStorage.setSecureBiometricWithLevel(key, record.value, record.level);
2017
- }
2018
- return;
2019
- }
2020
- if (record.kind !== "raw") {
2021
- return;
2022
- }
2023
- if (record.value === undefined) {
2024
- keysToRemove.push(key);
2025
- } else {
2026
- const accessControl = record.accessControl ?? secureDefaultAccessControl;
2027
- const existingGroup = groupedKeysToSet.get(accessControl);
2028
- const group = existingGroup ?? {
2029
- keys: [],
2030
- values: []
2031
- };
2032
- group.keys.push(key);
2033
- group.values.push(record.value);
2034
- if (!existingGroup) {
2035
- groupedKeysToSet.set(accessControl, group);
2036
- }
2037
- }
2038
- });
2039
- if (scope === _Storage.StorageScope.Disk) {
2040
- flushDiskWrites();
2041
- }
2042
- if (scope === _Storage.StorageScope.Secure) {
2043
- flushSecureWrites();
2044
- }
2045
- groupedKeysToSet.forEach((group, accessControl) => {
2046
- if (scope === _Storage.StorageScope.Secure) {
2047
- WebStorage.setSecureAccessControl(accessControl);
2048
- }
2049
- WebStorage.setBatch(group.keys, group.values, scope);
2050
- group.keys.forEach((key, index) => cacheRawValue(scope, key, group.values[index]));
2051
- });
2052
- if (keysToRemove.length > 0) {
2053
- WebStorage.removeBatch(keysToRemove, scope);
2054
- keysToRemove.forEach(key => cacheRawValue(scope, key, undefined));
2055
- }
2056
- }
2057
- throw error;
2058
- }
2059
- });
2060
- }
2061
- function createSecureAuthStorage(config, options) {
2062
- const ns = options?.namespace ?? "auth";
2063
- const result = {};
2064
- for (const key of typedKeys(config)) {
2065
- const itemConfig = config[key];
2066
- const expirationConfig = itemConfig.ttlMs !== undefined ? {
2067
- ttlMs: itemConfig.ttlMs
2068
- } : undefined;
2069
- result[key] = createStorageItem({
2070
- key,
2071
- scope: _Storage.StorageScope.Secure,
2072
- defaultValue: "",
2073
- namespace: ns,
2074
- ...(itemConfig.biometric !== undefined ? {
2075
- biometric: itemConfig.biometric
2076
- } : {}),
2077
- ...(itemConfig.biometricLevel !== undefined ? {
2078
- biometricLevel: itemConfig.biometricLevel
2079
- } : {}),
2080
- ...(itemConfig.accessControl !== undefined ? {
2081
- accessControl: itemConfig.accessControl
2082
- } : {}),
2083
- ...(expirationConfig !== undefined ? {
2084
- expiration: expirationConfig
2085
- } : {})
2086
- });
2087
- }
2088
- return result;
2089
- }
2090
- function isKeychainLockedError(err) {
2091
- return (0, _storageRuntime.isLockedStorageErrorCode)((0, _storageRuntime.getStorageErrorCode)(err));
2092
- }
2093
626
  //# sourceMappingURL=index.web.js.map