react-native-nitro-storage 0.1.4 → 0.3.1
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.
- package/README.md +432 -345
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +191 -3
- package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +21 -41
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +181 -29
- package/android/src/main/java/com/nitrostorage/NitroStoragePackage.kt +2 -2
- package/app.plugin.js +9 -7
- package/cpp/bindings/HybridStorage.cpp +239 -10
- package/cpp/bindings/HybridStorage.hpp +10 -0
- package/cpp/core/NativeStorageAdapter.hpp +22 -0
- package/ios/IOSStorageAdapterCpp.hpp +25 -0
- package/ios/IOSStorageAdapterCpp.mm +315 -33
- package/lib/commonjs/Storage.types.js +23 -1
- package/lib/commonjs/Storage.types.js.map +1 -1
- package/lib/commonjs/index.js +680 -68
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +801 -133
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/internal.js +112 -0
- package/lib/commonjs/internal.js.map +1 -0
- package/lib/module/Storage.types.js +22 -0
- package/lib/module/Storage.types.js.map +1 -1
- package/lib/module/index.js +660 -71
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +766 -125
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/internal.js +100 -0
- package/lib/module/internal.js.map +1 -0
- package/lib/typescript/Storage.nitro.d.ts +10 -0
- package/lib/typescript/Storage.nitro.d.ts.map +1 -1
- package/lib/typescript/Storage.types.d.ts +20 -0
- package/lib/typescript/Storage.types.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +68 -9
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +79 -13
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/internal.d.ts +21 -0
- package/lib/typescript/internal.d.ts.map +1 -0
- package/lib/typescript/migration.d.ts +2 -3
- package/lib/typescript/migration.d.ts.map +1 -1
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +10 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +10 -0
- package/package.json +22 -8
- package/src/Storage.nitro.ts +11 -2
- package/src/Storage.types.ts +22 -0
- package/src/index.ts +943 -84
- package/src/index.web.ts +1082 -137
- package/src/internal.ts +144 -0
- package/src/migration.ts +3 -3
|
@@ -3,193 +3,689 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports
|
|
6
|
+
Object.defineProperty(exports, "AccessControl", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _Storage.AccessControl;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "BiometricLevel", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _Storage.BiometricLevel;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "StorageScope", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _Storage.StorageScope;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
exports.createSecureAuthStorage = createSecureAuthStorage;
|
|
7
25
|
exports.createStorageItem = createStorageItem;
|
|
8
26
|
exports.getBatch = getBatch;
|
|
27
|
+
Object.defineProperty(exports, "migrateFromMMKV", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: function () {
|
|
30
|
+
return _migration.migrateFromMMKV;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
exports.migrateToLatest = migrateToLatest;
|
|
34
|
+
exports.registerMigration = registerMigration;
|
|
9
35
|
exports.removeBatch = removeBatch;
|
|
36
|
+
exports.runTransaction = runTransaction;
|
|
10
37
|
exports.setBatch = setBatch;
|
|
11
38
|
exports.storage = void 0;
|
|
12
39
|
exports.useSetStorage = useSetStorage;
|
|
13
40
|
exports.useStorage = useStorage;
|
|
41
|
+
exports.useStorageSelector = useStorageSelector;
|
|
14
42
|
var _react = require("react");
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
var _Storage = require("./Storage.types");
|
|
44
|
+
var _internal = require("./internal");
|
|
45
|
+
var _migration = require("./migration");
|
|
46
|
+
function asInternal(item) {
|
|
47
|
+
return item;
|
|
48
|
+
}
|
|
49
|
+
const registeredMigrations = new Map();
|
|
50
|
+
const runMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : task => {
|
|
51
|
+
Promise.resolve().then(task);
|
|
52
|
+
};
|
|
53
|
+
const memoryStore = new Map();
|
|
54
|
+
const memoryListeners = new Map();
|
|
55
|
+
const webScopeListeners = new Map([[_Storage.StorageScope.Disk, new Map()], [_Storage.StorageScope.Secure, new Map()]]);
|
|
56
|
+
const scopedRawCache = new Map([[_Storage.StorageScope.Disk, new Map()], [_Storage.StorageScope.Secure, new Map()]]);
|
|
57
|
+
const pendingSecureWrites = new Map();
|
|
58
|
+
let secureFlushScheduled = false;
|
|
59
|
+
const SECURE_WEB_PREFIX = "__secure_";
|
|
60
|
+
const BIOMETRIC_WEB_PREFIX = "__bio_";
|
|
61
|
+
let hasWarnedAboutWebBiometricFallback = false;
|
|
62
|
+
function getBrowserStorage(scope) {
|
|
63
|
+
if (scope === _Storage.StorageScope.Disk) {
|
|
64
|
+
return globalThis.localStorage;
|
|
65
|
+
}
|
|
66
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
67
|
+
return globalThis.localStorage;
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
function toSecureStorageKey(key) {
|
|
72
|
+
return `${SECURE_WEB_PREFIX}${key}`;
|
|
73
|
+
}
|
|
74
|
+
function fromSecureStorageKey(key) {
|
|
75
|
+
return key.slice(SECURE_WEB_PREFIX.length);
|
|
76
|
+
}
|
|
77
|
+
function toBiometricStorageKey(key) {
|
|
78
|
+
return `${BIOMETRIC_WEB_PREFIX}${key}`;
|
|
79
|
+
}
|
|
80
|
+
function fromBiometricStorageKey(key) {
|
|
81
|
+
return key.slice(BIOMETRIC_WEB_PREFIX.length);
|
|
82
|
+
}
|
|
83
|
+
function getScopedListeners(scope) {
|
|
84
|
+
return webScopeListeners.get(scope);
|
|
85
|
+
}
|
|
86
|
+
function getScopeRawCache(scope) {
|
|
87
|
+
return scopedRawCache.get(scope);
|
|
88
|
+
}
|
|
89
|
+
function cacheRawValue(scope, key, value) {
|
|
90
|
+
getScopeRawCache(scope).set(key, value);
|
|
91
|
+
}
|
|
92
|
+
function readCachedRawValue(scope, key) {
|
|
93
|
+
return getScopeRawCache(scope).get(key);
|
|
94
|
+
}
|
|
95
|
+
function hasCachedRawValue(scope, key) {
|
|
96
|
+
return getScopeRawCache(scope).has(key);
|
|
97
|
+
}
|
|
98
|
+
function clearScopeRawCache(scope) {
|
|
99
|
+
getScopeRawCache(scope).clear();
|
|
100
|
+
}
|
|
101
|
+
function notifyKeyListeners(registry, key) {
|
|
102
|
+
registry.get(key)?.forEach(listener => listener());
|
|
103
|
+
}
|
|
104
|
+
function notifyAllListeners(registry) {
|
|
105
|
+
registry.forEach(listeners => {
|
|
106
|
+
listeners.forEach(listener => listener());
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function addKeyListener(registry, key, listener) {
|
|
110
|
+
let listeners = registry.get(key);
|
|
111
|
+
if (!listeners) {
|
|
112
|
+
listeners = new Set();
|
|
113
|
+
registry.set(key, listeners);
|
|
114
|
+
}
|
|
115
|
+
listeners.add(listener);
|
|
116
|
+
return () => {
|
|
117
|
+
const scopedListeners = registry.get(key);
|
|
118
|
+
if (!scopedListeners) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
scopedListeners.delete(listener);
|
|
122
|
+
if (scopedListeners.size === 0) {
|
|
123
|
+
registry.delete(key);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function readPendingSecureWrite(key) {
|
|
128
|
+
return pendingSecureWrites.get(key)?.value;
|
|
129
|
+
}
|
|
130
|
+
function hasPendingSecureWrite(key) {
|
|
131
|
+
return pendingSecureWrites.has(key);
|
|
132
|
+
}
|
|
133
|
+
function clearPendingSecureWrite(key) {
|
|
134
|
+
pendingSecureWrites.delete(key);
|
|
135
|
+
}
|
|
136
|
+
function flushSecureWrites() {
|
|
137
|
+
secureFlushScheduled = false;
|
|
138
|
+
if (pendingSecureWrites.size === 0) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const writes = Array.from(pendingSecureWrites.values());
|
|
142
|
+
pendingSecureWrites.clear();
|
|
143
|
+
const keysToSet = [];
|
|
144
|
+
const valuesToSet = [];
|
|
145
|
+
const keysToRemove = [];
|
|
146
|
+
writes.forEach(({
|
|
147
|
+
key,
|
|
148
|
+
value
|
|
149
|
+
}) => {
|
|
150
|
+
if (value === undefined) {
|
|
151
|
+
keysToRemove.push(key);
|
|
152
|
+
} else {
|
|
153
|
+
keysToSet.push(key);
|
|
154
|
+
valuesToSet.push(value);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
if (keysToSet.length > 0) {
|
|
158
|
+
WebStorage.setBatch(keysToSet, valuesToSet, _Storage.StorageScope.Secure);
|
|
159
|
+
}
|
|
160
|
+
if (keysToRemove.length > 0) {
|
|
161
|
+
WebStorage.removeBatch(keysToRemove, _Storage.StorageScope.Secure);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function scheduleSecureWrite(key, value) {
|
|
165
|
+
pendingSecureWrites.set(key, {
|
|
166
|
+
key,
|
|
167
|
+
value
|
|
168
|
+
});
|
|
169
|
+
if (secureFlushScheduled) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
secureFlushScheduled = true;
|
|
173
|
+
runMicrotask(flushSecureWrites);
|
|
28
174
|
}
|
|
29
175
|
const WebStorage = {
|
|
30
176
|
name: "Storage",
|
|
31
177
|
equals: other => other === WebStorage,
|
|
32
178
|
dispose: () => {},
|
|
33
179
|
set: (key, value, scope) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
180
|
+
const storage = getBrowserStorage(scope);
|
|
181
|
+
if (!storage) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const storageKey = scope === _Storage.StorageScope.Secure ? toSecureStorageKey(key) : key;
|
|
185
|
+
storage.setItem(storageKey, value);
|
|
186
|
+
if (scope === _Storage.StorageScope.Disk || scope === _Storage.StorageScope.Secure) {
|
|
187
|
+
notifyKeyListeners(getScopedListeners(scope), key);
|
|
40
188
|
}
|
|
41
189
|
},
|
|
42
190
|
get: (key, scope) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return sessionStorage?.getItem(key) ?? undefined;
|
|
47
|
-
}
|
|
48
|
-
return undefined;
|
|
191
|
+
const storage = getBrowserStorage(scope);
|
|
192
|
+
const storageKey = scope === _Storage.StorageScope.Secure ? toSecureStorageKey(key) : key;
|
|
193
|
+
return storage?.getItem(storageKey) ?? undefined;
|
|
49
194
|
},
|
|
50
195
|
remove: (key, scope) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
196
|
+
const storage = getBrowserStorage(scope);
|
|
197
|
+
if (!storage) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
201
|
+
storage.removeItem(toSecureStorageKey(key));
|
|
202
|
+
storage.removeItem(toBiometricStorageKey(key));
|
|
203
|
+
} else {
|
|
204
|
+
storage.removeItem(key);
|
|
205
|
+
}
|
|
206
|
+
if (scope === _Storage.StorageScope.Disk || scope === _Storage.StorageScope.Secure) {
|
|
207
|
+
notifyKeyListeners(getScopedListeners(scope), key);
|
|
57
208
|
}
|
|
58
209
|
},
|
|
59
210
|
clear: scope => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
211
|
+
const storage = getBrowserStorage(scope);
|
|
212
|
+
if (!storage) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
216
|
+
const keysToRemove = [];
|
|
217
|
+
for (let i = 0; i < storage.length; i++) {
|
|
218
|
+
const key = storage.key(i);
|
|
219
|
+
if (key?.startsWith(SECURE_WEB_PREFIX) || key?.startsWith(BIOMETRIC_WEB_PREFIX)) {
|
|
220
|
+
keysToRemove.push(key);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
keysToRemove.forEach(key => storage.removeItem(key));
|
|
224
|
+
} else if (scope === _Storage.StorageScope.Disk) {
|
|
225
|
+
const keysToRemove = [];
|
|
226
|
+
for (let i = 0; i < storage.length; i++) {
|
|
227
|
+
const key = storage.key(i);
|
|
228
|
+
if (key && !key.startsWith(SECURE_WEB_PREFIX) && !key.startsWith(BIOMETRIC_WEB_PREFIX)) {
|
|
229
|
+
keysToRemove.push(key);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
keysToRemove.forEach(key => storage.removeItem(key));
|
|
233
|
+
} else {
|
|
234
|
+
storage.clear();
|
|
235
|
+
}
|
|
236
|
+
if (scope === _Storage.StorageScope.Disk || scope === _Storage.StorageScope.Secure) {
|
|
237
|
+
notifyAllListeners(getScopedListeners(scope));
|
|
70
238
|
}
|
|
71
239
|
},
|
|
72
240
|
setBatch: (keys, values, scope) => {
|
|
73
|
-
|
|
241
|
+
const storage = getBrowserStorage(scope);
|
|
242
|
+
if (!storage) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
keys.forEach((key, index) => {
|
|
246
|
+
const storageKey = scope === _Storage.StorageScope.Secure ? toSecureStorageKey(key) : key;
|
|
247
|
+
storage.setItem(storageKey, values[index]);
|
|
248
|
+
});
|
|
249
|
+
if (scope === _Storage.StorageScope.Disk || scope === _Storage.StorageScope.Secure) {
|
|
250
|
+
const listeners = getScopedListeners(scope);
|
|
251
|
+
keys.forEach(key => notifyKeyListeners(listeners, key));
|
|
252
|
+
}
|
|
74
253
|
},
|
|
75
254
|
getBatch: (keys, scope) => {
|
|
76
|
-
|
|
255
|
+
const storage = getBrowserStorage(scope);
|
|
256
|
+
return keys.map(key => {
|
|
257
|
+
const storageKey = scope === _Storage.StorageScope.Secure ? toSecureStorageKey(key) : key;
|
|
258
|
+
return storage?.getItem(storageKey) ?? undefined;
|
|
259
|
+
});
|
|
77
260
|
},
|
|
78
261
|
removeBatch: (keys, scope) => {
|
|
79
|
-
keys.forEach(key =>
|
|
262
|
+
keys.forEach(key => {
|
|
263
|
+
WebStorage.remove(key, scope);
|
|
264
|
+
});
|
|
80
265
|
},
|
|
81
266
|
addOnChange: (_scope, _callback) => {
|
|
82
267
|
return () => {};
|
|
268
|
+
},
|
|
269
|
+
has: (key, scope) => {
|
|
270
|
+
const storage = getBrowserStorage(scope);
|
|
271
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
272
|
+
return storage?.getItem(toSecureStorageKey(key)) !== null || storage?.getItem(toBiometricStorageKey(key)) !== null;
|
|
273
|
+
}
|
|
274
|
+
return storage?.getItem(key) !== null;
|
|
275
|
+
},
|
|
276
|
+
getAllKeys: scope => {
|
|
277
|
+
const storage = getBrowserStorage(scope);
|
|
278
|
+
if (!storage) return [];
|
|
279
|
+
const keys = new Set();
|
|
280
|
+
for (let i = 0; i < storage.length; i++) {
|
|
281
|
+
const k = storage.key(i);
|
|
282
|
+
if (!k) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
286
|
+
if (k.startsWith(SECURE_WEB_PREFIX)) {
|
|
287
|
+
keys.add(fromSecureStorageKey(k));
|
|
288
|
+
} else if (k.startsWith(BIOMETRIC_WEB_PREFIX)) {
|
|
289
|
+
keys.add(fromBiometricStorageKey(k));
|
|
290
|
+
}
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (k.startsWith(SECURE_WEB_PREFIX) || k.startsWith(BIOMETRIC_WEB_PREFIX)) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
keys.add(k);
|
|
297
|
+
}
|
|
298
|
+
return Array.from(keys);
|
|
299
|
+
},
|
|
300
|
+
size: scope => {
|
|
301
|
+
return WebStorage.getAllKeys(scope).length;
|
|
302
|
+
},
|
|
303
|
+
setSecureAccessControl: () => {},
|
|
304
|
+
setKeychainAccessGroup: () => {},
|
|
305
|
+
setSecureBiometric: (key, value) => {
|
|
306
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && !hasWarnedAboutWebBiometricFallback) {
|
|
307
|
+
hasWarnedAboutWebBiometricFallback = true;
|
|
308
|
+
console.warn("[NitroStorage] Biometric storage is not supported on web. Using localStorage.");
|
|
309
|
+
}
|
|
310
|
+
globalThis.localStorage?.setItem(toBiometricStorageKey(key), value);
|
|
311
|
+
notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), key);
|
|
312
|
+
},
|
|
313
|
+
getSecureBiometric: key => {
|
|
314
|
+
return globalThis.localStorage?.getItem(toBiometricStorageKey(key)) ?? undefined;
|
|
315
|
+
},
|
|
316
|
+
deleteSecureBiometric: key => {
|
|
317
|
+
globalThis.localStorage?.removeItem(toBiometricStorageKey(key));
|
|
318
|
+
notifyKeyListeners(getScopedListeners(_Storage.StorageScope.Secure), key);
|
|
319
|
+
},
|
|
320
|
+
hasSecureBiometric: key => {
|
|
321
|
+
return globalThis.localStorage?.getItem(toBiometricStorageKey(key)) !== null;
|
|
322
|
+
},
|
|
323
|
+
clearSecureBiometric: () => {
|
|
324
|
+
const storage = globalThis.localStorage;
|
|
325
|
+
if (!storage) return;
|
|
326
|
+
const keysToNotify = [];
|
|
327
|
+
const toRemove = [];
|
|
328
|
+
for (let i = 0; i < storage.length; i++) {
|
|
329
|
+
const k = storage.key(i);
|
|
330
|
+
if (k?.startsWith(BIOMETRIC_WEB_PREFIX)) {
|
|
331
|
+
toRemove.push(k);
|
|
332
|
+
keysToNotify.push(fromBiometricStorageKey(k));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
toRemove.forEach(k => storage.removeItem(k));
|
|
336
|
+
const listeners = getScopedListeners(_Storage.StorageScope.Secure);
|
|
337
|
+
keysToNotify.forEach(key => notifyKeyListeners(listeners, key));
|
|
83
338
|
}
|
|
84
339
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
340
|
+
function getRawValue(key, scope) {
|
|
341
|
+
(0, _internal.assertValidScope)(scope);
|
|
342
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
343
|
+
const value = memoryStore.get(key);
|
|
344
|
+
return typeof value === "string" ? value : undefined;
|
|
345
|
+
}
|
|
346
|
+
if (scope === _Storage.StorageScope.Secure && hasPendingSecureWrite(key)) {
|
|
347
|
+
return readPendingSecureWrite(key);
|
|
348
|
+
}
|
|
349
|
+
return WebStorage.get(key, scope);
|
|
350
|
+
}
|
|
351
|
+
function setRawValue(key, value, scope) {
|
|
352
|
+
(0, _internal.assertValidScope)(scope);
|
|
353
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
354
|
+
memoryStore.set(key, value);
|
|
355
|
+
notifyKeyListeners(memoryListeners, key);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
359
|
+
flushSecureWrites();
|
|
360
|
+
clearPendingSecureWrite(key);
|
|
361
|
+
}
|
|
362
|
+
WebStorage.set(key, value, scope);
|
|
363
|
+
cacheRawValue(scope, key, value);
|
|
364
|
+
}
|
|
365
|
+
function removeRawValue(key, scope) {
|
|
366
|
+
(0, _internal.assertValidScope)(scope);
|
|
367
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
368
|
+
memoryStore.delete(key);
|
|
369
|
+
notifyKeyListeners(memoryListeners, key);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
373
|
+
flushSecureWrites();
|
|
374
|
+
clearPendingSecureWrite(key);
|
|
375
|
+
}
|
|
376
|
+
WebStorage.remove(key, scope);
|
|
377
|
+
cacheRawValue(scope, key, undefined);
|
|
378
|
+
}
|
|
379
|
+
function readMigrationVersion(scope) {
|
|
380
|
+
const raw = getRawValue(_internal.MIGRATION_VERSION_KEY, scope);
|
|
381
|
+
if (raw === undefined) {
|
|
382
|
+
return 0;
|
|
383
|
+
}
|
|
384
|
+
const parsed = Number.parseInt(raw, 10);
|
|
385
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
386
|
+
}
|
|
387
|
+
function writeMigrationVersion(scope, version) {
|
|
388
|
+
setRawValue(_internal.MIGRATION_VERSION_KEY, String(version), scope);
|
|
89
389
|
}
|
|
90
390
|
const storage = exports.storage = {
|
|
91
391
|
clear: scope => {
|
|
92
|
-
if (scope === StorageScope.Memory) {
|
|
392
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
93
393
|
memoryStore.clear();
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
394
|
+
notifyAllListeners(memoryListeners);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
398
|
+
flushSecureWrites();
|
|
399
|
+
pendingSecureWrites.clear();
|
|
400
|
+
}
|
|
401
|
+
clearScopeRawCache(scope);
|
|
402
|
+
WebStorage.clear(scope);
|
|
403
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
404
|
+
WebStorage.clearSecureBiometric();
|
|
97
405
|
}
|
|
98
406
|
},
|
|
99
407
|
clearAll: () => {
|
|
100
|
-
storage.clear(StorageScope.Memory);
|
|
101
|
-
storage.clear(StorageScope.Disk);
|
|
102
|
-
storage.clear(StorageScope.Secure);
|
|
103
|
-
}
|
|
408
|
+
storage.clear(_Storage.StorageScope.Memory);
|
|
409
|
+
storage.clear(_Storage.StorageScope.Disk);
|
|
410
|
+
storage.clear(_Storage.StorageScope.Secure);
|
|
411
|
+
},
|
|
412
|
+
clearNamespace: (namespace, scope) => {
|
|
413
|
+
(0, _internal.assertValidScope)(scope);
|
|
414
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
415
|
+
for (const key of memoryStore.keys()) {
|
|
416
|
+
if ((0, _internal.isNamespaced)(key, namespace)) {
|
|
417
|
+
memoryStore.delete(key);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
notifyAllListeners(memoryListeners);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
424
|
+
flushSecureWrites();
|
|
425
|
+
}
|
|
426
|
+
const keys = WebStorage.getAllKeys(scope);
|
|
427
|
+
const namespacedKeys = keys.filter(k => (0, _internal.isNamespaced)(k, namespace));
|
|
428
|
+
if (namespacedKeys.length > 0) {
|
|
429
|
+
WebStorage.removeBatch(namespacedKeys, scope);
|
|
430
|
+
namespacedKeys.forEach(k => cacheRawValue(scope, k, undefined));
|
|
431
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
432
|
+
namespacedKeys.forEach(k => clearPendingSecureWrite(k));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
clearBiometric: () => {
|
|
437
|
+
WebStorage.clearSecureBiometric();
|
|
438
|
+
},
|
|
439
|
+
has: (key, scope) => {
|
|
440
|
+
(0, _internal.assertValidScope)(scope);
|
|
441
|
+
if (scope === _Storage.StorageScope.Memory) return memoryStore.has(key);
|
|
442
|
+
return WebStorage.has(key, scope);
|
|
443
|
+
},
|
|
444
|
+
getAllKeys: scope => {
|
|
445
|
+
(0, _internal.assertValidScope)(scope);
|
|
446
|
+
if (scope === _Storage.StorageScope.Memory) return Array.from(memoryStore.keys());
|
|
447
|
+
return WebStorage.getAllKeys(scope);
|
|
448
|
+
},
|
|
449
|
+
getAll: scope => {
|
|
450
|
+
(0, _internal.assertValidScope)(scope);
|
|
451
|
+
const result = {};
|
|
452
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
453
|
+
memoryStore.forEach((value, key) => {
|
|
454
|
+
if (typeof value === "string") result[key] = value;
|
|
455
|
+
});
|
|
456
|
+
return result;
|
|
457
|
+
}
|
|
458
|
+
const keys = WebStorage.getAllKeys(scope);
|
|
459
|
+
keys.forEach(key => {
|
|
460
|
+
const val = WebStorage.get(key, scope);
|
|
461
|
+
if (val !== undefined) result[key] = val;
|
|
462
|
+
});
|
|
463
|
+
return result;
|
|
464
|
+
},
|
|
465
|
+
size: scope => {
|
|
466
|
+
(0, _internal.assertValidScope)(scope);
|
|
467
|
+
if (scope === _Storage.StorageScope.Memory) return memoryStore.size;
|
|
468
|
+
return WebStorage.size(scope);
|
|
469
|
+
},
|
|
470
|
+
setAccessControl: _level => {},
|
|
471
|
+
setKeychainAccessGroup: _group => {}
|
|
104
472
|
};
|
|
473
|
+
function canUseRawBatchPath(item) {
|
|
474
|
+
return item._hasExpiration === false && item._hasValidation === false && item._isBiometric !== true && item._secureAccessControl === undefined;
|
|
475
|
+
}
|
|
105
476
|
function defaultSerialize(value) {
|
|
106
|
-
return
|
|
477
|
+
return (0, _internal.serializeWithPrimitiveFastPath)(value);
|
|
107
478
|
}
|
|
108
479
|
function defaultDeserialize(value) {
|
|
109
|
-
return
|
|
480
|
+
return (0, _internal.deserializeWithPrimitiveFastPath)(value);
|
|
110
481
|
}
|
|
111
482
|
function createStorageItem(config) {
|
|
483
|
+
const storageKey = (0, _internal.prefixKey)(config.namespace, config.key);
|
|
112
484
|
const serialize = config.serialize ?? defaultSerialize;
|
|
113
485
|
const deserialize = config.deserialize ?? defaultDeserialize;
|
|
114
|
-
const isMemory = config.scope === StorageScope.Memory;
|
|
486
|
+
const isMemory = config.scope === _Storage.StorageScope.Memory;
|
|
487
|
+
const isBiometric = config.biometric === true && config.scope === _Storage.StorageScope.Secure;
|
|
488
|
+
const secureAccessControl = config.accessControl;
|
|
489
|
+
const validate = config.validate;
|
|
490
|
+
const onValidationError = config.onValidationError;
|
|
491
|
+
const expiration = config.expiration;
|
|
492
|
+
const onExpired = config.onExpired;
|
|
493
|
+
const expirationTtlMs = expiration?.ttlMs;
|
|
494
|
+
const memoryExpiration = expiration && isMemory ? new Map() : null;
|
|
495
|
+
const readCache = !isMemory && config.readCache === true;
|
|
496
|
+
const coalesceSecureWrites = config.scope === _Storage.StorageScope.Secure && config.coalesceSecureWrites === true && !isBiometric && secureAccessControl === undefined;
|
|
497
|
+
const nonMemoryScope = config.scope === _Storage.StorageScope.Disk ? _Storage.StorageScope.Disk : config.scope === _Storage.StorageScope.Secure ? _Storage.StorageScope.Secure : null;
|
|
498
|
+
if (expiration && expiration.ttlMs <= 0) {
|
|
499
|
+
throw new Error("expiration.ttlMs must be greater than 0.");
|
|
500
|
+
}
|
|
115
501
|
const listeners = new Set();
|
|
116
502
|
let unsubscribe = null;
|
|
117
|
-
let lastRaw;
|
|
503
|
+
let lastRaw = undefined;
|
|
118
504
|
let lastValue;
|
|
505
|
+
let hasLastValue = false;
|
|
506
|
+
const invalidateParsedCache = () => {
|
|
507
|
+
lastRaw = undefined;
|
|
508
|
+
lastValue = undefined;
|
|
509
|
+
hasLastValue = false;
|
|
510
|
+
};
|
|
119
511
|
const ensureSubscription = () => {
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const listener = () => {
|
|
144
|
-
lastRaw = undefined;
|
|
145
|
-
lastValue = undefined;
|
|
146
|
-
listeners.forEach(l => l());
|
|
147
|
-
};
|
|
148
|
-
if (!secureListeners.has(config.key)) {
|
|
149
|
-
secureListeners.set(config.key, new Set());
|
|
512
|
+
if (unsubscribe) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const listener = () => {
|
|
516
|
+
invalidateParsedCache();
|
|
517
|
+
listeners.forEach(callback => callback());
|
|
518
|
+
};
|
|
519
|
+
if (isMemory) {
|
|
520
|
+
unsubscribe = addKeyListener(memoryListeners, storageKey, listener);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
unsubscribe = addKeyListener(getScopedListeners(nonMemoryScope), storageKey, listener);
|
|
524
|
+
};
|
|
525
|
+
const readStoredRaw = () => {
|
|
526
|
+
if (isMemory) {
|
|
527
|
+
if (memoryExpiration) {
|
|
528
|
+
const expiresAt = memoryExpiration.get(storageKey);
|
|
529
|
+
if (expiresAt !== undefined && expiresAt <= Date.now()) {
|
|
530
|
+
memoryExpiration.delete(storageKey);
|
|
531
|
+
memoryStore.delete(storageKey);
|
|
532
|
+
notifyKeyListeners(memoryListeners, storageKey);
|
|
533
|
+
onExpired?.(storageKey);
|
|
534
|
+
return undefined;
|
|
150
535
|
}
|
|
151
|
-
|
|
152
|
-
|
|
536
|
+
}
|
|
537
|
+
return memoryStore.get(storageKey);
|
|
538
|
+
}
|
|
539
|
+
if (nonMemoryScope === _Storage.StorageScope.Secure && !isBiometric && hasPendingSecureWrite(storageKey)) {
|
|
540
|
+
return readPendingSecureWrite(storageKey);
|
|
541
|
+
}
|
|
542
|
+
if (readCache) {
|
|
543
|
+
if (hasCachedRawValue(nonMemoryScope, storageKey)) {
|
|
544
|
+
return readCachedRawValue(nonMemoryScope, storageKey);
|
|
153
545
|
}
|
|
154
546
|
}
|
|
547
|
+
if (isBiometric) {
|
|
548
|
+
return WebStorage.getSecureBiometric(storageKey);
|
|
549
|
+
}
|
|
550
|
+
const raw = WebStorage.get(storageKey, config.scope);
|
|
551
|
+
cacheRawValue(nonMemoryScope, storageKey, raw);
|
|
552
|
+
return raw;
|
|
155
553
|
};
|
|
156
|
-
const
|
|
157
|
-
|
|
554
|
+
const writeStoredRaw = rawValue => {
|
|
555
|
+
if (isBiometric) {
|
|
556
|
+
WebStorage.setSecureBiometric(storageKey, rawValue);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
cacheRawValue(nonMemoryScope, storageKey, rawValue);
|
|
560
|
+
if (coalesceSecureWrites) {
|
|
561
|
+
scheduleSecureWrite(storageKey, rawValue);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
if (nonMemoryScope === _Storage.StorageScope.Secure) {
|
|
565
|
+
clearPendingSecureWrite(storageKey);
|
|
566
|
+
}
|
|
567
|
+
WebStorage.set(storageKey, rawValue, config.scope);
|
|
568
|
+
};
|
|
569
|
+
const removeStoredRaw = () => {
|
|
570
|
+
if (isBiometric) {
|
|
571
|
+
WebStorage.deleteSecureBiometric(storageKey);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
cacheRawValue(nonMemoryScope, storageKey, undefined);
|
|
575
|
+
if (coalesceSecureWrites) {
|
|
576
|
+
scheduleSecureWrite(storageKey, undefined);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (nonMemoryScope === _Storage.StorageScope.Secure) {
|
|
580
|
+
clearPendingSecureWrite(storageKey);
|
|
581
|
+
}
|
|
582
|
+
WebStorage.remove(storageKey, config.scope);
|
|
583
|
+
};
|
|
584
|
+
const writeValueWithoutValidation = value => {
|
|
158
585
|
if (isMemory) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
586
|
+
if (memoryExpiration) {
|
|
587
|
+
memoryExpiration.set(storageKey, Date.now() + (expirationTtlMs ?? 0));
|
|
588
|
+
}
|
|
589
|
+
memoryStore.set(storageKey, value);
|
|
590
|
+
notifyKeyListeners(memoryListeners, storageKey);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const serialized = serialize(value);
|
|
594
|
+
if (expiration) {
|
|
595
|
+
const envelope = {
|
|
596
|
+
__nitroStorageEnvelope: true,
|
|
597
|
+
expiresAt: Date.now() + expiration.ttlMs,
|
|
598
|
+
payload: serialized
|
|
599
|
+
};
|
|
600
|
+
writeStoredRaw(JSON.stringify(envelope));
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
writeStoredRaw(serialized);
|
|
604
|
+
};
|
|
605
|
+
const resolveInvalidValue = invalidValue => {
|
|
606
|
+
if (onValidationError) {
|
|
607
|
+
return onValidationError(invalidValue);
|
|
608
|
+
}
|
|
609
|
+
return config.defaultValue;
|
|
610
|
+
};
|
|
611
|
+
const ensureValidatedValue = (candidate, hadStoredValue) => {
|
|
612
|
+
if (!validate || validate(candidate)) {
|
|
613
|
+
return candidate;
|
|
614
|
+
}
|
|
615
|
+
const resolved = resolveInvalidValue(candidate);
|
|
616
|
+
if (validate && !validate(resolved)) {
|
|
617
|
+
return config.defaultValue;
|
|
618
|
+
}
|
|
619
|
+
if (hadStoredValue) {
|
|
620
|
+
writeValueWithoutValidation(resolved);
|
|
162
621
|
}
|
|
163
|
-
|
|
622
|
+
return resolved;
|
|
623
|
+
};
|
|
624
|
+
const get = () => {
|
|
625
|
+
const raw = readStoredRaw();
|
|
626
|
+
const canUseCachedValue = !expiration && !memoryExpiration;
|
|
627
|
+
if (canUseCachedValue && raw === lastRaw && hasLastValue) {
|
|
164
628
|
return lastValue;
|
|
165
629
|
}
|
|
166
630
|
lastRaw = raw;
|
|
167
631
|
if (raw === undefined) {
|
|
168
|
-
lastValue = config.defaultValue;
|
|
169
|
-
|
|
170
|
-
lastValue
|
|
632
|
+
lastValue = ensureValidatedValue(config.defaultValue, false);
|
|
633
|
+
hasLastValue = true;
|
|
634
|
+
return lastValue;
|
|
635
|
+
}
|
|
636
|
+
if (isMemory) {
|
|
637
|
+
lastValue = ensureValidatedValue(raw, true);
|
|
638
|
+
hasLastValue = true;
|
|
639
|
+
return lastValue;
|
|
171
640
|
}
|
|
641
|
+
let deserializableRaw = raw;
|
|
642
|
+
if (expiration) {
|
|
643
|
+
try {
|
|
644
|
+
const parsed = JSON.parse(raw);
|
|
645
|
+
if ((0, _internal.isStoredEnvelope)(parsed)) {
|
|
646
|
+
if (parsed.expiresAt <= Date.now()) {
|
|
647
|
+
removeStoredRaw();
|
|
648
|
+
invalidateParsedCache();
|
|
649
|
+
onExpired?.(storageKey);
|
|
650
|
+
lastValue = ensureValidatedValue(config.defaultValue, false);
|
|
651
|
+
hasLastValue = true;
|
|
652
|
+
return lastValue;
|
|
653
|
+
}
|
|
654
|
+
deserializableRaw = parsed.payload;
|
|
655
|
+
}
|
|
656
|
+
} catch {
|
|
657
|
+
// Keep backward compatibility with legacy raw values.
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
lastValue = ensureValidatedValue(deserialize(deserializableRaw), true);
|
|
661
|
+
hasLastValue = true;
|
|
172
662
|
return lastValue;
|
|
173
663
|
};
|
|
174
664
|
const set = valueOrFn => {
|
|
175
665
|
const currentValue = get();
|
|
176
666
|
const newValue = typeof valueOrFn === "function" ? valueOrFn(currentValue) : valueOrFn;
|
|
177
|
-
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
notifyMemoryListeners(config.key, newValue);
|
|
181
|
-
} else {
|
|
182
|
-
WebStorage.set(config.key, serialize(newValue), config.scope);
|
|
667
|
+
invalidateParsedCache();
|
|
668
|
+
if (validate && !validate(newValue)) {
|
|
669
|
+
throw new Error(`Validation failed for key "${storageKey}" in scope "${_Storage.StorageScope[config.scope]}".`);
|
|
183
670
|
}
|
|
671
|
+
writeValueWithoutValidation(newValue);
|
|
184
672
|
};
|
|
185
673
|
const deleteItem = () => {
|
|
186
|
-
|
|
674
|
+
invalidateParsedCache();
|
|
187
675
|
if (isMemory) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
676
|
+
if (memoryExpiration) {
|
|
677
|
+
memoryExpiration.delete(storageKey);
|
|
678
|
+
}
|
|
679
|
+
memoryStore.delete(storageKey);
|
|
680
|
+
notifyKeyListeners(memoryListeners, storageKey);
|
|
681
|
+
return;
|
|
192
682
|
}
|
|
683
|
+
removeStoredRaw();
|
|
684
|
+
};
|
|
685
|
+
const hasItem = () => {
|
|
686
|
+
if (isMemory) return memoryStore.has(storageKey);
|
|
687
|
+
if (isBiometric) return WebStorage.hasSecureBiometric(storageKey);
|
|
688
|
+
return WebStorage.has(storageKey, config.scope);
|
|
193
689
|
};
|
|
194
690
|
const subscribe = callback => {
|
|
195
691
|
ensureSubscription();
|
|
@@ -202,37 +698,94 @@ function createStorageItem(config) {
|
|
|
202
698
|
}
|
|
203
699
|
};
|
|
204
700
|
};
|
|
205
|
-
|
|
701
|
+
const storageItem = {
|
|
206
702
|
get,
|
|
207
703
|
set,
|
|
208
704
|
delete: deleteItem,
|
|
705
|
+
has: hasItem,
|
|
209
706
|
subscribe,
|
|
210
707
|
serialize,
|
|
211
708
|
deserialize,
|
|
212
709
|
_triggerListeners: () => {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
listeners.forEach(l => l());
|
|
710
|
+
invalidateParsedCache();
|
|
711
|
+
listeners.forEach(listener => listener());
|
|
216
712
|
},
|
|
713
|
+
_hasValidation: validate !== undefined,
|
|
714
|
+
_hasExpiration: expiration !== undefined,
|
|
715
|
+
_readCacheEnabled: readCache,
|
|
716
|
+
_isBiometric: isBiometric,
|
|
717
|
+
_secureAccessControl: secureAccessControl,
|
|
217
718
|
scope: config.scope,
|
|
218
|
-
key:
|
|
719
|
+
key: storageKey
|
|
219
720
|
};
|
|
721
|
+
return storageItem;
|
|
220
722
|
}
|
|
221
723
|
function useStorage(item) {
|
|
222
724
|
const value = (0, _react.useSyncExternalStore)(item.subscribe, item.get, item.get);
|
|
223
725
|
return [value, item.set];
|
|
224
726
|
}
|
|
727
|
+
function useStorageSelector(item, selector, isEqual = Object.is) {
|
|
728
|
+
const selectedRef = (0, _react.useRef)({
|
|
729
|
+
hasValue: false
|
|
730
|
+
});
|
|
731
|
+
const getSelectedSnapshot = () => {
|
|
732
|
+
const nextSelected = selector(item.get());
|
|
733
|
+
const current = selectedRef.current;
|
|
734
|
+
if (current.hasValue && isEqual(current.value, nextSelected)) {
|
|
735
|
+
return current.value;
|
|
736
|
+
}
|
|
737
|
+
selectedRef.current = {
|
|
738
|
+
hasValue: true,
|
|
739
|
+
value: nextSelected
|
|
740
|
+
};
|
|
741
|
+
return nextSelected;
|
|
742
|
+
};
|
|
743
|
+
const selectedValue = (0, _react.useSyncExternalStore)(item.subscribe, getSelectedSnapshot, getSelectedSnapshot);
|
|
744
|
+
return [selectedValue, item.set];
|
|
745
|
+
}
|
|
225
746
|
function useSetStorage(item) {
|
|
226
747
|
return item.set;
|
|
227
748
|
}
|
|
228
749
|
function getBatch(items, scope) {
|
|
229
|
-
|
|
750
|
+
(0, _internal.assertBatchScope)(items, scope);
|
|
751
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
230
752
|
return items.map(item => item.get());
|
|
231
753
|
}
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
754
|
+
const useRawBatchPath = items.every(item => canUseRawBatchPath(item));
|
|
755
|
+
if (!useRawBatchPath) {
|
|
756
|
+
return items.map(item => item.get());
|
|
757
|
+
}
|
|
758
|
+
const useBatchCache = items.every(item => item._readCacheEnabled === true);
|
|
759
|
+
const rawValues = new Array(items.length);
|
|
760
|
+
const keysToFetch = [];
|
|
761
|
+
const keyIndexes = [];
|
|
762
|
+
items.forEach((item, index) => {
|
|
763
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
764
|
+
if (hasPendingSecureWrite(item.key)) {
|
|
765
|
+
rawValues[index] = readPendingSecureWrite(item.key);
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (useBatchCache) {
|
|
770
|
+
if (hasCachedRawValue(scope, item.key)) {
|
|
771
|
+
rawValues[index] = readCachedRawValue(scope, item.key);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
keysToFetch.push(item.key);
|
|
776
|
+
keyIndexes.push(index);
|
|
777
|
+
});
|
|
778
|
+
if (keysToFetch.length > 0) {
|
|
779
|
+
const fetchedValues = WebStorage.getBatch(keysToFetch, scope);
|
|
780
|
+
fetchedValues.forEach((value, index) => {
|
|
781
|
+
const key = keysToFetch[index];
|
|
782
|
+
const targetIndex = keyIndexes[index];
|
|
783
|
+
rawValues[targetIndex] = value;
|
|
784
|
+
cacheRawValue(scope, key, value);
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
return items.map((item, index) => {
|
|
788
|
+
const raw = rawValues[index];
|
|
236
789
|
if (raw === undefined) {
|
|
237
790
|
return item.get();
|
|
238
791
|
}
|
|
@@ -240,29 +793,144 @@ function getBatch(items, scope) {
|
|
|
240
793
|
});
|
|
241
794
|
}
|
|
242
795
|
function setBatch(items, scope) {
|
|
243
|
-
|
|
796
|
+
(0, _internal.assertBatchScope)(items.map(batchEntry => batchEntry.item), scope);
|
|
797
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
244
798
|
items.forEach(({
|
|
245
799
|
item,
|
|
246
800
|
value
|
|
247
801
|
}) => item.set(value));
|
|
248
802
|
return;
|
|
249
803
|
}
|
|
250
|
-
const
|
|
251
|
-
const values = items.map(i => i.item.serialize(i.value));
|
|
252
|
-
WebStorage.setBatch(keys, values, scope);
|
|
253
|
-
items.forEach(({
|
|
804
|
+
const useRawBatchPath = items.every(({
|
|
254
805
|
item
|
|
255
|
-
}) =>
|
|
256
|
-
|
|
257
|
-
|
|
806
|
+
}) => canUseRawBatchPath(asInternal(item)));
|
|
807
|
+
if (!useRawBatchPath) {
|
|
808
|
+
items.forEach(({
|
|
809
|
+
item,
|
|
810
|
+
value
|
|
811
|
+
}) => item.set(value));
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
const keys = items.map(entry => entry.item.key);
|
|
815
|
+
const values = items.map(entry => entry.item.serialize(entry.value));
|
|
816
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
817
|
+
flushSecureWrites();
|
|
818
|
+
}
|
|
819
|
+
WebStorage.setBatch(keys, values, scope);
|
|
820
|
+
keys.forEach((key, index) => cacheRawValue(scope, key, values[index]));
|
|
258
821
|
}
|
|
259
822
|
function removeBatch(items, scope) {
|
|
260
|
-
|
|
823
|
+
(0, _internal.assertBatchScope)(items, scope);
|
|
824
|
+
if (scope === _Storage.StorageScope.Memory) {
|
|
261
825
|
items.forEach(item => item.delete());
|
|
262
826
|
return;
|
|
263
827
|
}
|
|
264
828
|
const keys = items.map(item => item.key);
|
|
829
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
830
|
+
flushSecureWrites();
|
|
831
|
+
}
|
|
265
832
|
WebStorage.removeBatch(keys, scope);
|
|
266
|
-
|
|
833
|
+
keys.forEach(key => cacheRawValue(scope, key, undefined));
|
|
834
|
+
}
|
|
835
|
+
function registerMigration(version, migration) {
|
|
836
|
+
if (!Number.isInteger(version) || version <= 0) {
|
|
837
|
+
throw new Error("Migration version must be a positive integer.");
|
|
838
|
+
}
|
|
839
|
+
if (registeredMigrations.has(version)) {
|
|
840
|
+
throw new Error(`Migration version ${version} is already registered.`);
|
|
841
|
+
}
|
|
842
|
+
registeredMigrations.set(version, migration);
|
|
843
|
+
}
|
|
844
|
+
function migrateToLatest(scope = _Storage.StorageScope.Disk) {
|
|
845
|
+
(0, _internal.assertValidScope)(scope);
|
|
846
|
+
const currentVersion = readMigrationVersion(scope);
|
|
847
|
+
const versions = Array.from(registeredMigrations.keys()).filter(version => version > currentVersion).sort((a, b) => a - b);
|
|
848
|
+
let appliedVersion = currentVersion;
|
|
849
|
+
const context = {
|
|
850
|
+
scope,
|
|
851
|
+
getRaw: key => getRawValue(key, scope),
|
|
852
|
+
setRaw: (key, value) => setRawValue(key, value, scope),
|
|
853
|
+
removeRaw: key => removeRawValue(key, scope)
|
|
854
|
+
};
|
|
855
|
+
versions.forEach(version => {
|
|
856
|
+
const migration = registeredMigrations.get(version);
|
|
857
|
+
if (!migration) {
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
migration(context);
|
|
861
|
+
writeMigrationVersion(scope, version);
|
|
862
|
+
appliedVersion = version;
|
|
863
|
+
});
|
|
864
|
+
return appliedVersion;
|
|
865
|
+
}
|
|
866
|
+
function runTransaction(scope, transaction) {
|
|
867
|
+
(0, _internal.assertValidScope)(scope);
|
|
868
|
+
if (scope === _Storage.StorageScope.Secure) {
|
|
869
|
+
flushSecureWrites();
|
|
870
|
+
}
|
|
871
|
+
const rollback = new Map();
|
|
872
|
+
const rememberRollback = key => {
|
|
873
|
+
if (rollback.has(key)) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
rollback.set(key, getRawValue(key, scope));
|
|
877
|
+
};
|
|
878
|
+
const tx = {
|
|
879
|
+
scope,
|
|
880
|
+
getRaw: key => getRawValue(key, scope),
|
|
881
|
+
setRaw: (key, value) => {
|
|
882
|
+
rememberRollback(key);
|
|
883
|
+
setRawValue(key, value, scope);
|
|
884
|
+
},
|
|
885
|
+
removeRaw: key => {
|
|
886
|
+
rememberRollback(key);
|
|
887
|
+
removeRawValue(key, scope);
|
|
888
|
+
},
|
|
889
|
+
getItem: item => {
|
|
890
|
+
(0, _internal.assertBatchScope)([item], scope);
|
|
891
|
+
return item.get();
|
|
892
|
+
},
|
|
893
|
+
setItem: (item, value) => {
|
|
894
|
+
(0, _internal.assertBatchScope)([item], scope);
|
|
895
|
+
rememberRollback(item.key);
|
|
896
|
+
item.set(value);
|
|
897
|
+
},
|
|
898
|
+
removeItem: item => {
|
|
899
|
+
(0, _internal.assertBatchScope)([item], scope);
|
|
900
|
+
rememberRollback(item.key);
|
|
901
|
+
item.delete();
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
try {
|
|
905
|
+
return transaction(tx);
|
|
906
|
+
} catch (error) {
|
|
907
|
+
Array.from(rollback.entries()).reverse().forEach(([key, previousValue]) => {
|
|
908
|
+
if (previousValue === undefined) {
|
|
909
|
+
removeRawValue(key, scope);
|
|
910
|
+
} else {
|
|
911
|
+
setRawValue(key, previousValue, scope);
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
throw error;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
function createSecureAuthStorage(config, options) {
|
|
918
|
+
const ns = options?.namespace ?? "auth";
|
|
919
|
+
const result = {};
|
|
920
|
+
for (const key of Object.keys(config)) {
|
|
921
|
+
const itemConfig = config[key];
|
|
922
|
+
result[key] = createStorageItem({
|
|
923
|
+
key,
|
|
924
|
+
scope: _Storage.StorageScope.Secure,
|
|
925
|
+
defaultValue: "",
|
|
926
|
+
namespace: ns,
|
|
927
|
+
biometric: itemConfig.biometric,
|
|
928
|
+
accessControl: itemConfig.accessControl,
|
|
929
|
+
expiration: itemConfig.ttlMs ? {
|
|
930
|
+
ttlMs: itemConfig.ttlMs
|
|
931
|
+
} : undefined
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
return result;
|
|
267
935
|
}
|
|
268
936
|
//# sourceMappingURL=index.web.js.map
|