react-native-nitro-storage 0.7.0 → 0.8.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 (106) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +20 -9
  3. package/SECURITY.md +2 -2
  4. package/docs/api-reference.md +11 -1
  5. package/docs/batch-transactions-migrations.md +8 -2
  6. package/docs/benchmarks.md +19 -15
  7. package/docs/web-backends.md +18 -1
  8. package/ios/IOSStorageAdapterCpp.mm +162 -17
  9. package/lib/commonjs/capabilities.js +26 -0
  10. package/lib/commonjs/capabilities.js.map +1 -0
  11. package/lib/commonjs/core/durability.js +137 -0
  12. package/lib/commonjs/core/durability.js.map +1 -0
  13. package/lib/commonjs/core/metrics.js +91 -0
  14. package/lib/commonjs/core/metrics.js.map +1 -0
  15. package/lib/commonjs/index.js +47 -22
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/commonjs/index.web.js +118 -66
  18. package/lib/commonjs/index.web.js.map +1 -1
  19. package/lib/commonjs/indexeddb-backend.js +73 -23
  20. package/lib/commonjs/indexeddb-backend.js.map +1 -1
  21. package/lib/commonjs/internal.js +27 -4
  22. package/lib/commonjs/internal.js.map +1 -1
  23. package/lib/commonjs/shared.js +2 -2
  24. package/lib/commonjs/shared.js.map +1 -1
  25. package/lib/commonjs/storage-core.js +217 -233
  26. package/lib/commonjs/storage-core.js.map +1 -1
  27. package/lib/commonjs/storage-events.js.map +1 -1
  28. package/lib/commonjs/storage-platform.js +6 -0
  29. package/lib/commonjs/storage-platform.js.map +1 -0
  30. package/lib/commonjs/storage-runtime.js +3 -21
  31. package/lib/commonjs/storage-runtime.js.map +1 -1
  32. package/lib/commonjs/testing.js +19 -4
  33. package/lib/commonjs/testing.js.map +1 -1
  34. package/lib/commonjs/web-backend-contract.js +27 -0
  35. package/lib/commonjs/web-backend-contract.js.map +1 -0
  36. package/lib/module/capabilities.js +21 -0
  37. package/lib/module/capabilities.js.map +1 -0
  38. package/lib/module/core/durability.js +133 -0
  39. package/lib/module/core/durability.js.map +1 -0
  40. package/lib/module/core/metrics.js +87 -0
  41. package/lib/module/core/metrics.js.map +1 -0
  42. package/lib/module/index.js +48 -23
  43. package/lib/module/index.js.map +1 -1
  44. package/lib/module/index.web.js +103 -64
  45. package/lib/module/index.web.js.map +1 -1
  46. package/lib/module/indexeddb-backend.js +73 -23
  47. package/lib/module/indexeddb-backend.js.map +1 -1
  48. package/lib/module/internal.js +24 -3
  49. package/lib/module/internal.js.map +1 -1
  50. package/lib/module/shared.js +2 -2
  51. package/lib/module/shared.js.map +1 -1
  52. package/lib/module/storage-core.js +218 -234
  53. package/lib/module/storage-core.js.map +1 -1
  54. package/lib/module/storage-events.js.map +1 -1
  55. package/lib/module/storage-platform.js +4 -0
  56. package/lib/module/storage-platform.js.map +1 -0
  57. package/lib/module/storage-runtime.js +3 -21
  58. package/lib/module/storage-runtime.js.map +1 -1
  59. package/lib/module/testing.js +5 -3
  60. package/lib/module/testing.js.map +1 -1
  61. package/lib/module/web-backend-contract.js +22 -0
  62. package/lib/module/web-backend-contract.js.map +1 -0
  63. package/lib/typescript/capabilities.d.ts +11 -0
  64. package/lib/typescript/capabilities.d.ts.map +1 -0
  65. package/lib/typescript/core/durability.d.ts +27 -0
  66. package/lib/typescript/core/durability.d.ts.map +1 -0
  67. package/lib/typescript/core/metrics.d.ts +12 -0
  68. package/lib/typescript/core/metrics.d.ts.map +1 -0
  69. package/lib/typescript/index.d.ts +5 -2
  70. package/lib/typescript/index.d.ts.map +1 -1
  71. package/lib/typescript/index.web.d.ts +8 -4
  72. package/lib/typescript/index.web.d.ts.map +1 -1
  73. package/lib/typescript/indexeddb-backend.d.ts.map +1 -1
  74. package/lib/typescript/internal.d.ts +3 -0
  75. package/lib/typescript/internal.d.ts.map +1 -1
  76. package/lib/typescript/shared.d.ts +2 -1
  77. package/lib/typescript/shared.d.ts.map +1 -1
  78. package/lib/typescript/storage-core.d.ts +3 -3
  79. package/lib/typescript/storage-core.d.ts.map +1 -1
  80. package/lib/typescript/storage-events.d.ts +2 -2
  81. package/lib/typescript/storage-events.d.ts.map +1 -1
  82. package/lib/typescript/storage-platform.d.ts +12 -0
  83. package/lib/typescript/storage-platform.d.ts.map +1 -0
  84. package/lib/typescript/storage-runtime.d.ts.map +1 -1
  85. package/lib/typescript/testing.d.ts +5 -1
  86. package/lib/typescript/testing.d.ts.map +1 -1
  87. package/lib/typescript/web-backend-contract.d.ts +10 -0
  88. package/lib/typescript/web-backend-contract.d.ts.map +1 -0
  89. package/lib/typescript/web-storage-backend.d.ts +1 -1
  90. package/lib/typescript/web-storage-backend.d.ts.map +1 -1
  91. package/package.json +3 -2
  92. package/src/capabilities.ts +36 -0
  93. package/src/core/durability.ts +170 -0
  94. package/src/core/metrics.ts +133 -0
  95. package/src/index.ts +66 -40
  96. package/src/index.web.ts +158 -86
  97. package/src/indexeddb-backend.ts +91 -20
  98. package/src/internal.ts +50 -4
  99. package/src/shared.ts +7 -6
  100. package/src/storage-core.ts +317 -285
  101. package/src/storage-events.ts +2 -1
  102. package/src/storage-platform.ts +16 -0
  103. package/src/storage-runtime.ts +13 -47
  104. package/src/testing.ts +14 -7
  105. package/src/web-backend-contract.ts +34 -0
  106. package/src/web-storage-backend.ts +1 -1
@@ -4,10 +4,12 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.createStorageCore = createStorageCore;
7
- var _shared = require("./shared");
8
- var _Storage = require("./Storage.types");
7
+ var _durability = require("./core/durability");
8
+ var _metrics = require("./core/metrics");
9
9
  var _internal = require("./internal");
10
+ var _shared = require("./shared");
10
11
  var _storageEvents = require("./storage-events");
12
+ var _Storage = require("./Storage.types");
11
13
  const EMPTY_KEYS = Object.freeze([]);
12
14
  function asInternal(item) {
13
15
  return item;
@@ -18,55 +20,38 @@ function createStorageCore(buildAdapter) {
18
20
  const registeredKeyCounts = new Map();
19
21
  const memoryStore = new Map();
20
22
  const memoryListeners = new Map();
21
- const scopedListeners = new Map([[_Storage.StorageScope.Disk, new Map()], [_Storage.StorageScope.Secure, new Map()]]);
22
- const scopedRawCache = new Map([[_Storage.StorageScope.Disk, new Map()], [_Storage.StorageScope.Secure, new Map()]]);
23
- const pendingDiskWrites = new Map();
24
- let diskFlushScheduled = false;
25
- let diskWritesAsync = false;
26
- const pendingSecureWrites = new Map();
27
- let secureFlushScheduled = false;
23
+ const scopedListeners = {
24
+ [_Storage.StorageScope.Disk]: new Map(),
25
+ [_Storage.StorageScope.Secure]: new Map()
26
+ };
27
+ const scopedRawCache = {
28
+ [_Storage.StorageScope.Disk]: new Map(),
29
+ [_Storage.StorageScope.Secure]: new Map()
30
+ };
28
31
  let secureDefaultAccessControl = _Storage.AccessControl.WhenUnlocked;
29
- let metricsObserver;
30
32
  let eventObserver;
31
33
  let eventObserverRedactSecureValues = true;
32
- const metricsCounters = new Map();
33
34
  const storageEvents = new _storageEvents.StorageEventRegistry();
34
- function recordMetric(operation, scope, durationMs, keysCount = 1) {
35
- const existing = metricsCounters.get(operation);
36
- if (!existing) {
37
- metricsCounters.set(operation, {
38
- count: 1,
39
- totalDurationMs: durationMs,
40
- maxDurationMs: durationMs
41
- });
42
- } else {
43
- existing.count += 1;
44
- existing.totalDurationMs += durationMs;
45
- existing.maxDurationMs = Math.max(existing.maxDurationMs, durationMs);
46
- }
47
- metricsObserver?.({
48
- operation,
49
- scope,
50
- durationMs,
51
- keysCount
52
- });
53
- }
54
- function measureOperation(operation, scope, fn, keysCount = 1) {
55
- if (!metricsObserver) {
56
- return fn();
57
- }
58
- const start = (0, _shared.now)();
59
- try {
60
- return fn();
61
- } finally {
62
- recordMetric(operation, scope, (0, _shared.now)() - start, keysCount);
63
- }
64
- }
35
+ const metrics = (0, _metrics.createMetricsRegistry)();
36
+ const durability = (0, _durability.createDurabilityCoordinator)({
37
+ backend: {
38
+ setBatch: (keys, values, scope) => {
39
+ adapter.backend.setBatch(keys, values, scope);
40
+ },
41
+ removeBatch: (keys, scope) => {
42
+ adapter.backend.removeBatch(keys, scope);
43
+ },
44
+ setSecureAccessControl: level => {
45
+ adapter.backend.setSecureAccessControl(level);
46
+ }
47
+ },
48
+ resolveSecureDefaultAccessControl: () => secureDefaultAccessControl
49
+ });
65
50
  function getScopedListeners(scope) {
66
- return scopedListeners.get(scope);
51
+ return scopedListeners[scope];
67
52
  }
68
53
  function getScopeRawCache(scope) {
69
- return scopedRawCache.get(scope);
54
+ return scopedRawCache[scope];
70
55
  }
71
56
  function cacheRawValue(scope, key, value) {
72
57
  getScopeRawCache(scope).set(key, value);
@@ -98,7 +83,7 @@ function createStorageCore(buildAdapter) {
98
83
  function getEventRawValue(scope, key) {
99
84
  if (scope === _Storage.StorageScope.Memory) {
100
85
  const value = memoryStore.get(key);
101
- return typeof value === "string" ? value : undefined;
86
+ return typeof value === "string" ? (0, _internal.unescapeCollidingRawValue)(value) : undefined;
102
87
  }
103
88
  return getRawValue(key, scope);
104
89
  }
@@ -144,118 +129,49 @@ function createStorageCore(buildAdapter) {
144
129
  storageEvents.emitBatch(event);
145
130
  eventObserver?.(eventForGlobalObserver(event));
146
131
  }
132
+ function measureOperation(operation, scope, fn, keysCount = 1) {
133
+ return metrics.measure(operation, scope, fn, keysCount);
134
+ }
135
+ function recordMetric(operation, scope, durationMs, keysCount = 1) {
136
+ metrics.record(operation, scope, durationMs, keysCount);
137
+ }
147
138
  function readPendingSecureWrite(key) {
148
- return pendingSecureWrites.get(key)?.value;
139
+ return durability.readPendingSecureWrite(key);
149
140
  }
150
141
  function readPendingDiskWrite(key) {
151
- return pendingDiskWrites.get(key)?.value;
142
+ return durability.readPendingDiskWrite(key);
152
143
  }
153
144
  function hasPendingDiskWrite(key) {
154
- return pendingDiskWrites.has(key);
145
+ return durability.hasPendingDiskWrite(key);
155
146
  }
156
147
  function hasPendingSecureWrite(key) {
157
- return pendingSecureWrites.has(key);
148
+ return durability.hasPendingSecureWrite(key);
158
149
  }
159
150
  function clearPendingDiskWrite(key) {
160
- pendingDiskWrites.delete(key);
151
+ durability.clearPendingDiskWrite(key);
161
152
  }
162
153
  function clearPendingSecureWrite(key) {
163
- pendingSecureWrites.delete(key);
154
+ durability.clearPendingSecureWrite(key);
164
155
  }
165
156
  function flushDiskWrites() {
166
- diskFlushScheduled = false;
167
- if (pendingDiskWrites.size === 0) {
168
- return;
169
- }
170
- const writes = Array.from(pendingDiskWrites.values());
171
- pendingDiskWrites.clear();
172
- const keysToSet = [];
173
- const valuesToSet = [];
174
- const keysToRemove = [];
175
- writes.forEach(({
176
- key,
177
- value
178
- }) => {
179
- if (value === undefined) {
180
- keysToRemove.push(key);
181
- return;
182
- }
183
- keysToSet.push(key);
184
- valuesToSet.push(value);
185
- });
186
- if (keysToSet.length > 0) {
187
- adapter.backend.setBatch(keysToSet, valuesToSet, _Storage.StorageScope.Disk);
188
- }
189
- if (keysToRemove.length > 0) {
190
- adapter.backend.removeBatch(keysToRemove, _Storage.StorageScope.Disk);
191
- }
157
+ durability.flushDiskWrites();
192
158
  }
193
159
  function flushSecureWrites() {
194
- secureFlushScheduled = false;
195
- if (pendingSecureWrites.size === 0) {
196
- return;
197
- }
198
- const writes = Array.from(pendingSecureWrites.values());
199
- pendingSecureWrites.clear();
200
- const groupedSetWrites = new Map();
201
- const keysToRemove = [];
202
- writes.forEach(({
203
- key,
204
- value,
205
- accessControl
206
- }) => {
207
- if (value === undefined) {
208
- keysToRemove.push(key);
209
- } else {
210
- const resolvedAccessControl = accessControl ?? secureDefaultAccessControl;
211
- const existingGroup = groupedSetWrites.get(resolvedAccessControl);
212
- const group = existingGroup ?? {
213
- keys: [],
214
- values: []
215
- };
216
- group.keys.push(key);
217
- group.values.push(value);
218
- if (!existingGroup) {
219
- groupedSetWrites.set(resolvedAccessControl, group);
220
- }
221
- }
222
- });
223
- groupedSetWrites.forEach((group, accessControl) => {
224
- adapter.backend.setSecureAccessControl(accessControl);
225
- adapter.backend.setBatch(group.keys, group.values, _Storage.StorageScope.Secure);
226
- });
227
- if (keysToRemove.length > 0) {
228
- adapter.backend.removeBatch(keysToRemove, _Storage.StorageScope.Secure);
229
- }
160
+ durability.flushSecureWrites();
230
161
  }
231
162
  function scheduleDiskWrite(key, value) {
232
- pendingDiskWrites.set(key, {
233
- key,
234
- value
235
- });
236
- if (diskFlushScheduled) {
237
- return;
238
- }
239
- diskFlushScheduled = true;
240
- (0, _shared.runMicrotask)(flushDiskWrites);
163
+ durability.scheduleDiskWrite(key, value);
241
164
  }
242
165
  function scheduleSecureWrite(key, value, accessControl) {
243
- const pendingWrite = {
244
- key,
245
- value
246
- };
247
- if (accessControl !== undefined) {
248
- pendingWrite.accessControl = accessControl;
249
- }
250
- pendingSecureWrites.set(key, pendingWrite);
251
- if (secureFlushScheduled) {
252
- return;
253
- }
254
- secureFlushScheduled = true;
255
- (0, _shared.runMicrotask)(flushSecureWrites);
166
+ durability.scheduleSecureWrite(key, value, accessControl);
256
167
  }
257
- function getRawValue(key, scope) {
258
- (0, _internal.assertValidScope)(scope);
168
+ function setDiskWritesAsyncMode(enabled) {
169
+ durability.setDiskWritesAsync(enabled);
170
+ }
171
+ function isDiskWritesAsync() {
172
+ return durability.isDiskWritesAsync();
173
+ }
174
+ function getStoredRawValue(key, scope) {
259
175
  if (scope === _Storage.StorageScope.Memory) {
260
176
  const value = memoryStore.get(key);
261
177
  return typeof value === "string" ? value : undefined;
@@ -268,19 +184,37 @@ function createStorageCore(buildAdapter) {
268
184
  }
269
185
  return adapter.backend.get(key, scope);
270
186
  }
187
+ function getRawValue(key, scope) {
188
+ (0, _internal.assertValidScope)(scope);
189
+ if (scope === _Storage.StorageScope.Memory) {
190
+ const value = memoryStore.get(key);
191
+ return typeof value === "string" ? (0, _internal.unescapeCollidingRawValue)(value) : undefined;
192
+ }
193
+ if (scope === _Storage.StorageScope.Disk && hasPendingDiskWrite(key)) {
194
+ const pending = readPendingDiskWrite(key);
195
+ return pending === undefined ? undefined : (0, _internal.unescapeCollidingRawValue)(pending);
196
+ }
197
+ if (scope === _Storage.StorageScope.Secure && hasPendingSecureWrite(key)) {
198
+ const pending = readPendingSecureWrite(key);
199
+ return pending === undefined ? undefined : (0, _internal.unescapeCollidingRawValue)(pending);
200
+ }
201
+ const raw = adapter.backend.get(key, scope);
202
+ return raw === undefined ? undefined : (0, _internal.unescapeCollidingRawValue)(raw);
203
+ }
271
204
  function setRawValue(key, value, scope) {
272
205
  (0, _internal.assertValidScope)(scope);
206
+ const storedValue = (0, _internal.escapeCollidingRawValue)(value);
273
207
  const oldValue = scope === _Storage.StorageScope.Memory ? getEventRawValue(scope, key) : undefined;
274
208
  if (scope === _Storage.StorageScope.Memory) {
275
- memoryStore.set(key, value);
209
+ memoryStore.set(key, storedValue);
276
210
  (0, _shared.notifyKeyListeners)(memoryListeners, key);
277
211
  emitKeyChange(scope, key, oldValue, value, "set", "memory");
278
212
  return;
279
213
  }
280
214
  if (scope === _Storage.StorageScope.Disk) {
281
- cacheRawValue(scope, key, value);
282
- if (diskWritesAsync) {
283
- scheduleDiskWrite(key, value);
215
+ cacheRawValue(scope, key, storedValue);
216
+ if (isDiskWritesAsync()) {
217
+ scheduleDiskWrite(key, storedValue);
284
218
  emitKeyChange(scope, key, oldValue, value, "set", adapter.changeSource);
285
219
  return;
286
220
  }
@@ -294,8 +228,8 @@ function createStorageCore(buildAdapter) {
294
228
  adapter.backend.setSecureAccessControl(secureDefaultAccessControl);
295
229
  }
296
230
  }
297
- adapter.backend.set(key, value, scope);
298
- cacheRawValue(scope, key, value);
231
+ adapter.backend.set(key, storedValue, scope);
232
+ cacheRawValue(scope, key, storedValue);
299
233
  emitKeyChange(scope, key, oldValue, value, "set", adapter.changeSource);
300
234
  }
301
235
  function removeRawValue(key, scope) {
@@ -309,7 +243,7 @@ function createStorageCore(buildAdapter) {
309
243
  }
310
244
  if (scope === _Storage.StorageScope.Disk) {
311
245
  cacheRawValue(scope, key, undefined);
312
- if (diskWritesAsync) {
246
+ if (isDiskWritesAsync()) {
313
247
  scheduleDiskWrite(key, undefined);
314
248
  emitKeyChange(scope, key, oldValue, undefined, "remove", adapter.changeSource);
315
249
  return;
@@ -333,9 +267,6 @@ function createStorageCore(buildAdapter) {
333
267
  const parsed = Number.parseInt(raw, 10);
334
268
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
335
269
  }
336
- function writeMigrationVersion(scope, version) {
337
- setRawValue(_internal.MIGRATION_VERSION_KEY, String(version), scope);
338
- }
339
270
  const internals = {
340
271
  getScopedListeners,
341
272
  cacheRawValue,
@@ -344,10 +275,10 @@ function createStorageCore(buildAdapter) {
344
275
  clearPendingDiskWrite,
345
276
  clearPendingSecureWrite,
346
277
  clearAllPendingDiskWrites: () => {
347
- pendingDiskWrites.clear();
278
+ durability.clearAllPendingDiskWrites();
348
279
  },
349
280
  clearAllPendingSecureWrites: () => {
350
- pendingSecureWrites.clear();
281
+ durability.clearAllPendingSecureWrites();
351
282
  },
352
283
  flushDiskWrites,
353
284
  flushSecureWrites,
@@ -376,7 +307,9 @@ function createStorageCore(buildAdapter) {
376
307
  }
377
308
  const previousValues = shouldReadPreviousEventValues(scope) ? removeKeys.map(key => getEventRawValue(scope, key)) : [];
378
309
  removeKeys.forEach(key => memoryStore.delete(key));
379
- removeKeys.forEach(key => (0, _shared.notifyKeyListeners)(memoryListeners, key));
310
+ removeKeys.forEach(key => {
311
+ (0, _shared.notifyKeyListeners)(memoryListeners, key);
312
+ });
380
313
  emitBatchChange(scope, "clear", "memory", removeKeys.map((key, index) => (0, _shared.createKeyChange)(scope, key, previousValues[index], undefined, "clear", "memory")));
381
314
  return;
382
315
  }
@@ -390,9 +323,11 @@ function createStorageCore(buildAdapter) {
390
323
  if (removeKeys.length === 0) {
391
324
  return;
392
325
  }
393
- const previousValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(removeKeys, scope) : [];
326
+ const previousValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(removeKeys, scope).map(value => value === undefined ? undefined : (0, _internal.unescapeCollidingRawValue)(value)) : [];
394
327
  adapter.backend.removeBatch(removeKeys, scope);
395
- removeKeys.forEach(key => cacheRawValue(scope, key, undefined));
328
+ removeKeys.forEach(key => {
329
+ cacheRawValue(scope, key, undefined);
330
+ });
396
331
  emitBatchChange(scope, "clear", adapter.changeSource, removeKeys.map((key, index) => (0, _shared.createKeyChange)(scope, key, previousValues[index], undefined, "clear", adapter.changeSource)));
397
332
  });
398
333
  }
@@ -462,11 +397,11 @@ function createStorageCore(buildAdapter) {
462
397
  }
463
398
  if (scope === _Storage.StorageScope.Disk) {
464
399
  flushDiskWrites();
465
- pendingDiskWrites.clear();
400
+ durability.clearAllPendingDiskWrites();
466
401
  }
467
402
  if (scope === _Storage.StorageScope.Secure) {
468
403
  flushSecureWrites();
469
- pendingSecureWrites.clear();
404
+ durability.clearAllPendingSecureWrites();
470
405
  }
471
406
  clearScopeRawCache(scope);
472
407
  adapter.backend.clear(scope);
@@ -560,7 +495,9 @@ function createStorageCore(buildAdapter) {
560
495
  affectedKeys.forEach(key => {
561
496
  memoryStore.delete(key);
562
497
  });
563
- affectedKeys.forEach(key => (0, _shared.notifyKeyListeners)(memoryListeners, key));
498
+ affectedKeys.forEach(key => {
499
+ (0, _shared.notifyKeyListeners)(memoryListeners, key);
500
+ });
564
501
  emitBatchChange(scope, "clearNamespace", "memory", previousValues.map(({
565
502
  key,
566
503
  value
@@ -661,7 +598,7 @@ function createStorageCore(buildAdapter) {
661
598
  keys.forEach((key, idx) => {
662
599
  const value = values[idx];
663
600
  if (value !== undefined) {
664
- result[key] = value;
601
+ result[key] = (0, _internal.unescapeCollidingRawValue)(value);
665
602
  }
666
603
  });
667
604
  return result;
@@ -674,7 +611,7 @@ function createStorageCore(buildAdapter) {
674
611
  if (scope === _Storage.StorageScope.Memory) {
675
612
  for (const key of memoryStore.keys()) {
676
613
  const value = memoryStore.get(key);
677
- if (typeof value === "string") result[key] = value;
614
+ if (typeof value === "string") result[key] = (0, _internal.unescapeCollidingRawValue)(value);
678
615
  }
679
616
  return result;
680
617
  }
@@ -689,7 +626,7 @@ function createStorageCore(buildAdapter) {
689
626
  const values = adapter.backend.getBatch(keys, scope);
690
627
  keys.forEach((key, idx) => {
691
628
  const val = values[idx];
692
- if (val !== undefined) result[key] = val;
629
+ if (val !== undefined) result[key] = (0, _internal.unescapeCollidingRawValue)(val);
693
630
  });
694
631
  return result;
695
632
  });
@@ -720,7 +657,7 @@ function createStorageCore(buildAdapter) {
720
657
  },
721
658
  setDiskWritesAsync: enabled => {
722
659
  measureOperation("storage:setDiskWritesAsync", _Storage.StorageScope.Disk, () => {
723
- diskWritesAsync = enabled;
660
+ setDiskWritesAsyncMode(enabled);
724
661
  if (!enabled) {
725
662
  flushDiskWrites();
726
663
  }
@@ -737,22 +674,16 @@ function createStorageCore(buildAdapter) {
737
674
  });
738
675
  },
739
676
  setMetricsObserver: observer => {
740
- metricsObserver = observer;
677
+ metrics.setObserver(observer);
741
678
  },
742
679
  getMetricsSnapshot: () => {
743
- const snapshot = {};
744
- metricsCounters.forEach((value, key) => {
745
- snapshot[key] = {
746
- count: value.count,
747
- totalDurationMs: value.totalDurationMs,
748
- avgDurationMs: value.count === 0 ? 0 : value.totalDurationMs / value.count,
749
- maxDurationMs: value.maxDurationMs
750
- };
751
- });
752
- return snapshot;
680
+ return metrics.getSnapshot();
681
+ },
682
+ getScopedMetricsSnapshot: () => {
683
+ return metrics.getScopedSnapshot();
753
684
  },
754
685
  resetMetrics: () => {
755
- metricsCounters.clear();
686
+ metrics.reset();
756
687
  },
757
688
  getSecureMetadata: key => {
758
689
  return measureOperation("storage:getSecureMetadata", _Storage.StorageScope.Secure, () => {
@@ -803,12 +734,15 @@ function createStorageCore(buildAdapter) {
803
734
  (0, _internal.assertValidScope)(scope);
804
735
  if (keys.length === 0) return;
805
736
  const values = keys.map(k => data[k]);
737
+ const storedValues = values.map(_internal.escapeCollidingRawValue);
806
738
  const changes = keys.map((key, index) => (0, _shared.createKeyChange)(scope, key, getEventRawValue(scope, key), values[index], "import", scope === _Storage.StorageScope.Memory ? "memory" : adapter.changeSource));
807
739
  if (scope === _Storage.StorageScope.Memory) {
808
740
  keys.forEach((key, index) => {
809
- memoryStore.set(key, values[index]);
741
+ memoryStore.set(key, storedValues[index]);
742
+ });
743
+ keys.forEach(key => {
744
+ (0, _shared.notifyKeyListeners)(memoryListeners, key);
810
745
  });
811
- keys.forEach(key => (0, _shared.notifyKeyListeners)(memoryListeners, key));
812
746
  emitBatchChange(scope, "import", "memory", changes);
813
747
  return;
814
748
  }
@@ -816,11 +750,13 @@ function createStorageCore(buildAdapter) {
816
750
  flushSecureWrites();
817
751
  adapter.backend.setSecureAccessControl(secureDefaultAccessControl);
818
752
  }
819
- if (scope === _Storage.StorageScope.Disk && adapter.flushDiskWritesOnImport) {
753
+ if (scope === _Storage.StorageScope.Disk) {
820
754
  flushDiskWrites();
821
755
  }
822
- adapter.backend.setBatch(keys, values, scope);
823
- keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
756
+ adapter.backend.setBatch(keys, storedValues, scope);
757
+ keys.forEach((key, index) => {
758
+ cacheRawValue(scope, key, storedValues[index]);
759
+ });
824
760
  emitBatchChange(scope, "import", adapter.changeSource, changes);
825
761
  }, keys.length);
826
762
  }
@@ -869,20 +805,28 @@ function createStorageCore(buildAdapter) {
869
805
  hasLastValue = false;
870
806
  lastExpiresAt = undefined;
871
807
  };
808
+ const resolveNonMemoryScope = () => {
809
+ if (nonMemoryScope === null) {
810
+ throw new Error("NitroStorage: this operation requires Disk or Secure scope.");
811
+ }
812
+ return nonMemoryScope;
813
+ };
872
814
  const ensureSubscription = () => {
873
815
  if (unsubscribe) {
874
816
  return;
875
817
  }
876
818
  const listener = () => {
877
819
  invalidateParsedCache();
878
- listeners.forEach(callback => callback());
820
+ listeners.forEach(callback => {
821
+ callback();
822
+ });
879
823
  };
880
824
  if (isMemory) {
881
825
  unsubscribe = addKeyListener(memoryListeners, storageKey, listener);
882
826
  return;
883
827
  }
884
- adapter.ensureScopeSubscription(nonMemoryScope);
885
- unsubscribe = addKeyListener(getScopedListeners(nonMemoryScope), storageKey, listener);
828
+ adapter.ensureScopeSubscription(resolveNonMemoryScope());
829
+ unsubscribe = addKeyListener(getScopedListeners(resolveNonMemoryScope()), storageKey, listener);
886
830
  };
887
831
  const readStoredRaw = () => {
888
832
  if (isMemory) {
@@ -893,28 +837,29 @@ function createStorageCore(buildAdapter) {
893
837
  memoryExpiration.delete(storageKey);
894
838
  memoryStore.delete(storageKey);
895
839
  (0, _shared.notifyKeyListeners)(memoryListeners, storageKey);
896
- emitKeyChange(config.scope, storageKey, typeof expiredRaw === "string" ? expiredRaw : undefined, undefined, "expire", "memory");
840
+ emitKeyChange(config.scope, storageKey, typeof expiredRaw === "string" ? (0, _internal.unescapeCollidingRawValue)(expiredRaw) : undefined, undefined, "expire", "memory");
897
841
  onExpired?.(storageKey);
898
842
  return undefined;
899
843
  }
900
844
  }
901
- return memoryStore.get(storageKey);
845
+ const memoryStored = memoryStore.get(storageKey);
846
+ return typeof memoryStored === "string" ? (0, _internal.unescapeCollidingRawValue)(memoryStored) : memoryStored;
902
847
  }
903
848
  migrateRenamesIfNeeded();
904
849
  if (nonMemoryScope === _Storage.StorageScope.Disk) {
905
- const pending = pendingDiskWrites.get(storageKey);
850
+ const pending = durability.readPendingDiskWrite(storageKey);
906
851
  if (pending !== undefined) {
907
- return pending.value;
852
+ return pending;
908
853
  }
909
854
  }
910
855
  if (nonMemoryScope === _Storage.StorageScope.Secure && !isBiometric) {
911
- const pending = pendingSecureWrites.get(storageKey);
856
+ const pending = durability.readPendingSecureWrite(storageKey);
912
857
  if (pending !== undefined) {
913
- return pending.value;
858
+ return pending;
914
859
  }
915
860
  }
916
861
  if (readCache) {
917
- const cache = getScopeRawCache(nonMemoryScope);
862
+ const cache = getScopeRawCache(resolveNonMemoryScope());
918
863
  const cached = cache.get(storageKey);
919
864
  if (cached !== undefined || cache.has(storageKey)) {
920
865
  return cached;
@@ -924,7 +869,7 @@ function createStorageCore(buildAdapter) {
924
869
  return readBackendRaw(() => adapter.backend.getSecureBiometric(storageKey));
925
870
  }
926
871
  const raw = readBackendRaw(() => adapter.backend.get(storageKey, config.scope));
927
- cacheRawValue(nonMemoryScope, storageKey, raw);
872
+ cacheRawValue(resolveNonMemoryScope(), storageKey, raw);
928
873
  return raw;
929
874
  };
930
875
  const readBackendRaw = read => {
@@ -933,7 +878,7 @@ function createStorageCore(buildAdapter) {
933
878
  } catch (error) {
934
879
  onReadError?.(error);
935
880
  if (fallbackToCacheOnReadError) {
936
- const cached = getScopeRawCache(nonMemoryScope).get(storageKey);
881
+ const cached = getScopeRawCache(resolveNonMemoryScope()).get(storageKey);
937
882
  if (cached !== undefined) {
938
883
  return cached;
939
884
  }
@@ -949,9 +894,9 @@ function createStorageCore(buildAdapter) {
949
894
  emitKeyChange(config.scope, storageKey, oldValue, rawValue, "set", adapter.changeSource);
950
895
  return;
951
896
  }
952
- cacheRawValue(nonMemoryScope, storageKey, rawValue);
897
+ cacheRawValue(resolveNonMemoryScope(), storageKey, rawValue);
953
898
  if (nonMemoryScope === _Storage.StorageScope.Disk) {
954
- if (coalesceDiskWrites || diskWritesAsync) {
899
+ if (coalesceDiskWrites || isDiskWritesAsync()) {
955
900
  scheduleDiskWrite(storageKey, rawValue);
956
901
  emitKeyChange(config.scope, storageKey, oldValue, rawValue, "set", adapter.changeSource);
957
902
  return;
@@ -1020,9 +965,9 @@ function createStorageCore(buildAdapter) {
1020
965
  emitKeyChange(config.scope, storageKey, oldValue, undefined, operation, adapter.changeSource);
1021
966
  return;
1022
967
  }
1023
- cacheRawValue(nonMemoryScope, storageKey, undefined);
968
+ cacheRawValue(resolveNonMemoryScope(), storageKey, undefined);
1024
969
  if (nonMemoryScope === _Storage.StorageScope.Disk) {
1025
- if (coalesceDiskWrites || diskWritesAsync) {
970
+ if (coalesceDiskWrites || isDiskWritesAsync()) {
1026
971
  scheduleDiskWrite(storageKey, undefined);
1027
972
  emitKeyChange(config.scope, storageKey, oldValue, undefined, operation, adapter.changeSource);
1028
973
  return;
@@ -1046,7 +991,8 @@ function createStorageCore(buildAdapter) {
1046
991
  if (memoryExpiration) {
1047
992
  memoryExpiration.set(storageKey, Date.now() + (expirationTtlMs ?? 0));
1048
993
  }
1049
- memoryStore.set(storageKey, value);
994
+ const storedValue = typeof value === "string" ? (0, _internal.escapeCollidingRawValue)(value) : value;
995
+ memoryStore.set(storageKey, storedValue);
1050
996
  (0, _shared.notifyKeyListeners)(memoryListeners, storageKey);
1051
997
  emitKeyChange(config.scope, storageKey, oldValue, typeof value === "string" ? value : undefined, "set", "memory");
1052
998
  return;
@@ -1097,7 +1043,9 @@ function createStorageCore(buildAdapter) {
1097
1043
  onExpired?.(storageKey);
1098
1044
  lastValue = ensureValidatedValue(defaultValue, false);
1099
1045
  hasLastValue = true;
1100
- listeners.forEach(cb => cb());
1046
+ listeners.forEach(cb => {
1047
+ cb();
1048
+ });
1101
1049
  return lastValue;
1102
1050
  }
1103
1051
  }
@@ -1133,7 +1081,9 @@ function createStorageCore(buildAdapter) {
1133
1081
  onExpired?.(storageKey);
1134
1082
  lastValue = ensureValidatedValue(defaultValue, false);
1135
1083
  hasLastValue = true;
1136
- listeners.forEach(cb => cb());
1084
+ listeners.forEach(cb => {
1085
+ cb();
1086
+ });
1137
1087
  return lastValue;
1138
1088
  }
1139
1089
  deserializableRaw = parsed.payload;
@@ -1151,7 +1101,10 @@ function createStorageCore(buildAdapter) {
1151
1101
  };
1152
1102
  const getCurrentVersion = () => {
1153
1103
  const raw = readStoredRaw();
1154
- return (0, _internal.toVersionToken)(raw);
1104
+ if (raw === undefined) {
1105
+ return (0, _internal.toVersionToken)(undefined);
1106
+ }
1107
+ return (0, _internal.toVersionToken)(typeof raw === "string" ? (0, _internal.unescapeCollidingRawValue)(raw) : raw);
1155
1108
  };
1156
1109
  const get = () => measureOperation("item:get", config.scope, () => getInternal());
1157
1110
  const getWithVersion = () => measureOperation("item:getWithVersion", config.scope, () => ({
@@ -1217,15 +1170,13 @@ function createStorageCore(buildAdapter) {
1217
1170
  if (isMemory) return memoryStore.has(storageKey);
1218
1171
  if (isBiometric) return adapter.backend.hasSecureBiometric(storageKey);
1219
1172
  if (nonMemoryScope === _Storage.StorageScope.Disk) {
1220
- const pending = pendingDiskWrites.get(storageKey);
1221
- if (pending !== undefined) {
1222
- return pending.value !== undefined;
1173
+ if (durability.hasPendingDiskWrite(storageKey)) {
1174
+ return durability.readPendingDiskWrite(storageKey) !== undefined;
1223
1175
  }
1224
1176
  }
1225
1177
  if (nonMemoryScope === _Storage.StorageScope.Secure) {
1226
- const pending = pendingSecureWrites.get(storageKey);
1227
- if (pending !== undefined) {
1228
- return pending.value !== undefined;
1178
+ if (durability.hasPendingSecureWrite(storageKey)) {
1179
+ return durability.readPendingSecureWrite(storageKey) !== undefined;
1229
1180
  }
1230
1181
  }
1231
1182
  return adapter.backend.has(storageKey, config.scope);
@@ -1238,7 +1189,7 @@ function createStorageCore(buildAdapter) {
1238
1189
  if (listeners.size === 0 && unsubscribe) {
1239
1190
  unsubscribe();
1240
1191
  if (!isMemory) {
1241
- adapter.maybeCleanupScopeSubscription(nonMemoryScope);
1192
+ adapter.maybeCleanupScopeSubscription(resolveNonMemoryScope());
1242
1193
  }
1243
1194
  unsubscribe = null;
1244
1195
  }
@@ -1276,11 +1227,20 @@ function createStorageCore(buildAdapter) {
1276
1227
  deserialize,
1277
1228
  _triggerListeners: () => {
1278
1229
  invalidateParsedCache();
1279
- listeners.forEach(listener => listener());
1230
+ listeners.forEach(listener => {
1231
+ listener();
1232
+ });
1280
1233
  },
1281
1234
  _invalidateParsedCacheOnly: () => {
1282
1235
  invalidateParsedCache();
1283
1236
  },
1237
+ _deleteMemoryEntry: () => {
1238
+ if (memoryExpiration) {
1239
+ memoryExpiration.delete(storageKey);
1240
+ }
1241
+ memoryStore.delete(storageKey);
1242
+ invalidateParsedCache();
1243
+ },
1284
1244
  _hasValidation: validate !== undefined,
1285
1245
  _hasExpiration: expiration !== undefined,
1286
1246
  _readCacheEnabled: readCache,
@@ -1323,16 +1283,16 @@ function createStorageCore(buildAdapter) {
1323
1283
  const keyIndexes = [];
1324
1284
  items.forEach((item, index) => {
1325
1285
  if (scope === _Storage.StorageScope.Disk) {
1326
- const pending = pendingDiskWrites.get(item.key);
1286
+ const pending = durability.readPendingDiskWrite(item.key);
1327
1287
  if (pending !== undefined) {
1328
- rawValues[index] = pending.value;
1288
+ rawValues[index] = pending;
1329
1289
  return;
1330
1290
  }
1331
1291
  }
1332
1292
  if (scope === _Storage.StorageScope.Secure) {
1333
- const pending = pendingSecureWrites.get(item.key);
1293
+ const pending = durability.readPendingSecureWrite(item.key);
1334
1294
  if (pending !== undefined) {
1335
- rawValues[index] = pending.value;
1295
+ rawValues[index] = pending;
1336
1296
  return;
1337
1297
  }
1338
1298
  }
@@ -1384,7 +1344,9 @@ function createStorageCore(buildAdapter) {
1384
1344
  items.forEach(({
1385
1345
  item,
1386
1346
  value
1387
- }) => item.set(value));
1347
+ }) => {
1348
+ item.set(value);
1349
+ });
1388
1350
  return;
1389
1351
  }
1390
1352
  const changes = items.map(({
@@ -1402,7 +1364,9 @@ function createStorageCore(buildAdapter) {
1402
1364
  });
1403
1365
  items.forEach(({
1404
1366
  item
1405
- }) => (0, _shared.notifyKeyListeners)(memoryListeners, item.key));
1367
+ }) => {
1368
+ (0, _shared.notifyKeyListeners)(memoryListeners, item.key);
1369
+ });
1406
1370
  emitBatchChange(scope, "setBatch", "memory", changes);
1407
1371
  return;
1408
1372
  }
@@ -1422,14 +1386,16 @@ function createStorageCore(buildAdapter) {
1422
1386
  items.forEach(({
1423
1387
  item,
1424
1388
  value
1425
- }) => item.set(value));
1389
+ }) => {
1390
+ item.set(value);
1391
+ });
1426
1392
  return;
1427
1393
  }
1428
1394
  flushSecureWrites();
1429
1395
  const keys = secureEntries.map(({
1430
1396
  item
1431
1397
  }) => item.key);
1432
- const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope) : [];
1398
+ const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope).map(value => value === undefined ? undefined : (0, _internal.unescapeCollidingRawValue)(value)) : [];
1433
1399
  const groupedByAccessControl = new Map();
1434
1400
  secureEntries.forEach(({
1435
1401
  item,
@@ -1451,7 +1417,9 @@ function createStorageCore(buildAdapter) {
1451
1417
  groupedByAccessControl.forEach((group, accessControl) => {
1452
1418
  adapter.backend.setSecureAccessControl(accessControl);
1453
1419
  adapter.backend.setBatch(group.keys, group.values, scope);
1454
- group.keys.forEach((key, index) => cacheRawValue(scope, key, group.values[index]));
1420
+ group.keys.forEach((key, index) => {
1421
+ cacheRawValue(scope, key, group.values[index]);
1422
+ });
1455
1423
  });
1456
1424
  emitBatchChange(scope, "setBatch", adapter.changeSource, secureEntries.map(({
1457
1425
  item,
@@ -1467,14 +1435,18 @@ function createStorageCore(buildAdapter) {
1467
1435
  items.forEach(({
1468
1436
  item,
1469
1437
  value
1470
- }) => item.set(value));
1438
+ }) => {
1439
+ item.set(value);
1440
+ });
1471
1441
  return;
1472
1442
  }
1473
1443
  const keys = items.map(entry => entry.item.key);
1474
1444
  const values = items.map(entry => entry.item.serialize(entry.value));
1475
- const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope) : [];
1445
+ const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope).map(value => value === undefined ? undefined : (0, _internal.unescapeCollidingRawValue)(value)) : [];
1476
1446
  adapter.backend.setBatch(keys, values, scope);
1477
- keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
1447
+ keys.forEach((key, index) => {
1448
+ cacheRawValue(scope, key, values[index]);
1449
+ });
1478
1450
  emitBatchChange(scope, "setBatch", adapter.changeSource, keys.map((key, index) => (0, _shared.createKeyChange)(scope, key, oldValues[index], values[index], "setBatch", adapter.changeSource)));
1479
1451
  }, items.length);
1480
1452
  }
@@ -1483,7 +1455,12 @@ function createStorageCore(buildAdapter) {
1483
1455
  (0, _internal.assertBatchScope)(items, scope);
1484
1456
  if (scope === _Storage.StorageScope.Memory) {
1485
1457
  const changes = items.map(item => (0, _shared.createKeyChange)(scope, item.key, getEventRawValue(scope, item.key), undefined, "removeBatch", "memory"));
1486
- items.forEach(item => item.delete());
1458
+ items.forEach(item => {
1459
+ asInternal(item)._deleteMemoryEntry();
1460
+ });
1461
+ items.forEach(item => {
1462
+ (0, _shared.notifyKeyListeners)(memoryListeners, item.key);
1463
+ });
1487
1464
  emitBatchChange(scope, "removeBatch", "memory", changes);
1488
1465
  return;
1489
1466
  }
@@ -1494,9 +1471,11 @@ function createStorageCore(buildAdapter) {
1494
1471
  if (scope === _Storage.StorageScope.Secure) {
1495
1472
  flushSecureWrites();
1496
1473
  }
1497
- const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope) : [];
1474
+ const oldValues = shouldReadPreviousEventValues(scope) ? adapter.backend.getBatch(keys, scope).map(value => value === undefined ? undefined : (0, _internal.unescapeCollidingRawValue)(value)) : [];
1498
1475
  adapter.backend.removeBatch(keys, scope);
1499
- keys.forEach(key => cacheRawValue(scope, key, undefined));
1476
+ keys.forEach(key => {
1477
+ cacheRawValue(scope, key, undefined);
1478
+ });
1500
1479
  emitBatchChange(scope, "removeBatch", adapter.changeSource, keys.map((key, index) => (0, _shared.createKeyChange)(scope, key, oldValues[index], undefined, "removeBatch", adapter.changeSource)));
1501
1480
  }, items.length);
1502
1481
  }
@@ -1515,23 +1494,17 @@ function createStorageCore(buildAdapter) {
1515
1494
  const currentVersion = readMigrationVersion(scope);
1516
1495
  const versions = Array.from(registeredMigrations.keys()).filter(version => version > currentVersion).sort((a, b) => a - b);
1517
1496
  let appliedVersion = currentVersion;
1518
- const context = {
1519
- scope,
1520
- getRaw: key => getRawValue(key, scope),
1521
- setRaw: (key, value) => setRawValue(key, value, scope),
1522
- removeRaw: key => removeRawValue(key, scope)
1523
- };
1524
1497
  versions.forEach(version => {
1525
1498
  const migration = registeredMigrations.get(version);
1526
1499
  if (!migration) {
1527
1500
  return;
1528
1501
  }
1529
- migration(context);
1502
+ runTransaction(scope, tx => {
1503
+ migration(tx);
1504
+ tx.setRaw(_internal.MIGRATION_VERSION_KEY, String(version));
1505
+ });
1530
1506
  appliedVersion = version;
1531
1507
  });
1532
- if (appliedVersion !== currentVersion) {
1533
- writeMigrationVersion(scope, appliedVersion);
1534
- }
1535
1508
  return appliedVersion;
1536
1509
  });
1537
1510
  }
@@ -1567,7 +1540,7 @@ function createStorageCore(buildAdapter) {
1567
1540
  }
1568
1541
  rollback.set(key, {
1569
1542
  kind: "raw",
1570
- value: getRawValue(key, scope),
1543
+ value: getStoredRawValue(key, scope),
1571
1544
  ...(scope === _Storage.StorageScope.Secure && internal?._secureAccessControl !== undefined ? {
1572
1545
  accessControl: internal._secureAccessControl
1573
1546
  } : {})
@@ -1604,6 +1577,8 @@ function createStorageCore(buildAdapter) {
1604
1577
  return transaction(tx);
1605
1578
  } catch (error) {
1606
1579
  const rollbackEntries = Array.from(rollback.entries()).reverse();
1580
+ const rollbackSource = scope === _Storage.StorageScope.Memory ? "memory" : adapter.changeSource;
1581
+ const preRollbackValues = rollbackEntries.length > 0 ? rollbackEntries.map(([key]) => getEventRawValue(scope, key)) : [];
1607
1582
  if (scope === _Storage.StorageScope.Memory) {
1608
1583
  rollbackEntries.forEach(([key, record]) => {
1609
1584
  if (record.value === NOT_SET) {
@@ -1655,13 +1630,20 @@ function createStorageCore(buildAdapter) {
1655
1630
  adapter.backend.setSecureAccessControl(accessControl);
1656
1631
  }
1657
1632
  adapter.backend.setBatch(group.keys, group.values, scope);
1658
- group.keys.forEach((key, index) => cacheRawValue(scope, key, group.values[index]));
1633
+ group.keys.forEach((key, index) => {
1634
+ cacheRawValue(scope, key, group.values[index]);
1635
+ });
1659
1636
  });
1660
1637
  if (keysToRemove.length > 0) {
1661
1638
  adapter.backend.removeBatch(keysToRemove, scope);
1662
- keysToRemove.forEach(key => cacheRawValue(scope, key, undefined));
1639
+ keysToRemove.forEach(key => {
1640
+ cacheRawValue(scope, key, undefined);
1641
+ });
1663
1642
  }
1664
1643
  }
1644
+ if (rollbackEntries.length > 0) {
1645
+ emitBatchChange(scope, "rollback", rollbackSource, rollbackEntries.map(([key], index) => (0, _shared.createKeyChange)(scope, key, preRollbackValues[index], getEventRawValue(scope, key), "rollback", rollbackSource)));
1646
+ }
1665
1647
  throw error;
1666
1648
  }
1667
1649
  });
@@ -1773,7 +1755,9 @@ function createStorageCore(buildAdapter) {
1773
1755
  toggle,
1774
1756
  values: () => Object.keys(item.get()),
1775
1757
  size: () => Object.keys(item.get()).length,
1776
- clear: () => item.set({}),
1758
+ clear: () => {
1759
+ item.set({});
1760
+ },
1777
1761
  reset: item.reset,
1778
1762
  subscribe: item.subscribe,
1779
1763
  scope: item.scope,