react-native-nitro-storage 0.13.0 → 0.15.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.
- package/CHANGELOG.md +31 -0
- package/README.md +260 -7
- package/cpp/bindings/HybridStorage.cpp +21 -0
- package/cpp/bindings/HybridStorage.hpp +2 -0
- package/cpp/core/NativeStorageAdapter.hpp +6 -0
- package/docs/api-reference.md +68 -43
- package/docs/batch-transactions-migrations.md +7 -0
- package/docs/qa/agent-device-replay.md +106 -0
- package/docs/secure-storage.md +12 -6
- package/ios/IOSStorageAdapterCpp.hpp +12 -0
- package/ios/IOSStorageAdapterCpp.mm +180 -2
- package/lib/commonjs/Storage.types.js +4 -2
- package/lib/commonjs/Storage.types.js.map +1 -1
- package/lib/commonjs/core/durability.js +30 -2
- package/lib/commonjs/core/durability.js.map +1 -1
- package/lib/commonjs/index.js +16 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +10 -1
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/shared.js +6 -0
- package/lib/commonjs/shared.js.map +1 -1
- package/lib/commonjs/storage-core.js +116 -3
- package/lib/commonjs/storage-core.js.map +1 -1
- package/lib/commonjs/testing.js +32 -2
- package/lib/commonjs/testing.js.map +1 -1
- package/lib/module/Storage.types.js +4 -2
- package/lib/module/Storage.types.js.map +1 -1
- package/lib/module/core/durability.js +30 -2
- package/lib/module/core/durability.js.map +1 -1
- package/lib/module/index.js +16 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +10 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/shared.js +5 -0
- package/lib/module/shared.js.map +1 -1
- package/lib/module/storage-core.js +117 -4
- package/lib/module/storage-core.js.map +1 -1
- package/lib/module/testing.js +31 -1
- package/lib/module/testing.js.map +1 -1
- package/lib/typescript/Storage.nitro.d.ts +2 -0
- package/lib/typescript/Storage.nitro.d.ts.map +1 -1
- package/lib/typescript/Storage.types.d.ts +4 -2
- package/lib/typescript/Storage.types.d.ts.map +1 -1
- package/lib/typescript/core/durability.d.ts +7 -1
- package/lib/typescript/core/durability.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +8 -3
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +8 -3
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/shared.d.ts +9 -1
- package/lib/typescript/shared.d.ts.map +1 -1
- package/lib/typescript/storage-core.d.ts +34 -4
- package/lib/typescript/storage-core.d.ts.map +1 -1
- package/lib/typescript/storage-platform.d.ts +2 -0
- package/lib/typescript/storage-platform.d.ts.map +1 -1
- package/lib/typescript/testing.d.ts +16 -5
- package/lib/typescript/testing.d.ts.map +1 -1
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +2 -0
- package/package.json +2 -2
- package/src/Storage.nitro.ts +2 -0
- package/src/Storage.types.ts +4 -2
- package/src/core/durability.ts +46 -2
- package/src/index.ts +24 -0
- package/src/index.web.ts +18 -0
- package/src/shared.ts +27 -1
- package/src/storage-core.ts +182 -8
- package/src/storage-platform.ts +2 -0
- package/src/testing.ts +42 -0
|
@@ -5,8 +5,19 @@ import { createMetricsRegistry } from "./core/metrics";
|
|
|
5
5
|
import { MIGRATION_VERSION_KEY, isStoredEnvelope, assertBatchScope, assertValidScope, toVersionToken, assertValidStorageKey, prefixKey, isNamespaced, escapeCollidingRawValue, unescapeCollidingRawValue } from "./internal";
|
|
6
6
|
import { assertAccessControlLevel, assertBiometricLevel, canUseRawBatchPath, canUseSecureRawBatchPath, createStorageCompositeError, createKeyChange, defaultDeserialize, defaultSerialize, isKeychainLockedError, isUpdater, normalizeStorageError, notifyAllListeners, notifyKeyListeners, redactSecureKeyChange, typedKeys } from "./shared";
|
|
7
7
|
import { StorageEventRegistry } from "./storage-events";
|
|
8
|
-
import { isStorageError } from "./storage-runtime";
|
|
8
|
+
import { getStorageErrorCode, isStorageError } from "./storage-runtime";
|
|
9
9
|
import { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
|
|
10
|
+
function assertSynchronousCallbackResult(value, callbackKind) {
|
|
11
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const then = value.then;
|
|
15
|
+
if (typeof then !== "function") {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
void Promise.resolve(value).catch(() => undefined);
|
|
19
|
+
throw new TypeError(`${callbackKind} callbacks must be synchronous and cannot return a Promise or thenable.`);
|
|
20
|
+
}
|
|
10
21
|
const EMPTY_KEYS = Object.freeze([]);
|
|
11
22
|
function asInternal(item) {
|
|
12
23
|
return item;
|
|
@@ -802,6 +813,10 @@ export function createStorageCore(buildAdapter) {
|
|
|
802
813
|
flushSecureWrites();
|
|
803
814
|
});
|
|
804
815
|
},
|
|
816
|
+
/** Observes scheduled flush failures; explicit flush calls still throw. */
|
|
817
|
+
setScheduledFlushErrorObserver: observer => {
|
|
818
|
+
durability.setScheduledFlushErrorObserver(observer);
|
|
819
|
+
},
|
|
805
820
|
setMetricsObserver: observer => {
|
|
806
821
|
metrics.setObserver(observer);
|
|
807
822
|
},
|
|
@@ -2126,6 +2141,84 @@ export function createStorageCore(buildAdapter) {
|
|
|
2126
2141
|
}
|
|
2127
2142
|
registeredMigrations.set(version, migration);
|
|
2128
2143
|
}
|
|
2144
|
+
function migrateSecureAccessControl(level, options) {
|
|
2145
|
+
return measureOperation("storage:migrateSecureAccessControl", StorageScope.Secure, () => {
|
|
2146
|
+
assertAccessControlLevel(level);
|
|
2147
|
+
const requestedKeys = options?.keys;
|
|
2148
|
+
requestedKeys?.forEach(key => {
|
|
2149
|
+
assertValidStorageKey(key);
|
|
2150
|
+
});
|
|
2151
|
+
let keys;
|
|
2152
|
+
try {
|
|
2153
|
+
flushSecureWrites();
|
|
2154
|
+
keys = Array.from(new Set(requestedKeys ?? adapter.backend.getAllKeys(StorageScope.Secure)));
|
|
2155
|
+
} catch (error) {
|
|
2156
|
+
if (getStorageErrorCode(error) !== "keychain_locked") {
|
|
2157
|
+
throw error;
|
|
2158
|
+
}
|
|
2159
|
+
return {
|
|
2160
|
+
migrated: [],
|
|
2161
|
+
locked: [],
|
|
2162
|
+
missing: [],
|
|
2163
|
+
skipped: [],
|
|
2164
|
+
failed: [],
|
|
2165
|
+
enumerationLocked: true
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
secureDefaultAccessControl = level;
|
|
2169
|
+
adapter.backend.setSecureAccessControl(level);
|
|
2170
|
+
const result = {
|
|
2171
|
+
migrated: [],
|
|
2172
|
+
locked: [],
|
|
2173
|
+
missing: [],
|
|
2174
|
+
skipped: [],
|
|
2175
|
+
failed: [],
|
|
2176
|
+
enumerationLocked: false
|
|
2177
|
+
};
|
|
2178
|
+
if (!adapter.supportsSecureAccessControlMigration()) {
|
|
2179
|
+
result.skipped = keys;
|
|
2180
|
+
return result;
|
|
2181
|
+
}
|
|
2182
|
+
keys.forEach(key => {
|
|
2183
|
+
try {
|
|
2184
|
+
let hasBiometric;
|
|
2185
|
+
try {
|
|
2186
|
+
hasBiometric = adapter.backend.hasSecureBiometric(key);
|
|
2187
|
+
} catch (error) {
|
|
2188
|
+
if (getStorageErrorCode(error) !== "keychain_locked") {
|
|
2189
|
+
throw error;
|
|
2190
|
+
}
|
|
2191
|
+
result.skipped.push(key);
|
|
2192
|
+
return;
|
|
2193
|
+
}
|
|
2194
|
+
if (hasBiometric) {
|
|
2195
|
+
result.skipped.push(key);
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
const raw = adapter.backend.get(key, StorageScope.Secure);
|
|
2199
|
+
if (raw === undefined) {
|
|
2200
|
+
result.missing.push(key);
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2203
|
+
adapter.backend.set(key, raw, StorageScope.Secure);
|
|
2204
|
+
result.migrated.push(key);
|
|
2205
|
+
} catch (error) {
|
|
2206
|
+
const code = getStorageErrorCode(error);
|
|
2207
|
+
if (code === "keychain_locked") {
|
|
2208
|
+
result.locked.push(key);
|
|
2209
|
+
} else {
|
|
2210
|
+
result.failed.push(code === undefined ? {
|
|
2211
|
+
key
|
|
2212
|
+
} : {
|
|
2213
|
+
key,
|
|
2214
|
+
code
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
return result;
|
|
2220
|
+
}, options?.keys?.length ?? 1);
|
|
2221
|
+
}
|
|
2129
2222
|
function migrateToLatest(scope = StorageScope.Disk) {
|
|
2130
2223
|
return measureOperation("migration:run", scope, () => {
|
|
2131
2224
|
assertValidScope(scope);
|
|
@@ -2138,7 +2231,8 @@ export function createStorageCore(buildAdapter) {
|
|
|
2138
2231
|
return;
|
|
2139
2232
|
}
|
|
2140
2233
|
runTransaction(scope, tx => {
|
|
2141
|
-
migration(tx);
|
|
2234
|
+
const migrationResult = migration(tx);
|
|
2235
|
+
assertSynchronousCallbackResult(migrationResult, "Migration");
|
|
2142
2236
|
tx.setRaw(MIGRATION_VERSION_KEY, String(version));
|
|
2143
2237
|
});
|
|
2144
2238
|
appliedVersion = version;
|
|
@@ -2158,6 +2252,12 @@ export function createStorageCore(buildAdapter) {
|
|
|
2158
2252
|
const NOT_SET = Symbol();
|
|
2159
2253
|
const rollback = new Map();
|
|
2160
2254
|
const itemRenameStates = new Map();
|
|
2255
|
+
let transactionOpen = true;
|
|
2256
|
+
const assertTransactionOpen = () => {
|
|
2257
|
+
if (!transactionOpen) {
|
|
2258
|
+
throw new TypeError("Transaction context is closed.");
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2161
2261
|
const rememberRollback = (key, item, includeOtherSecureRepresentation = false) => {
|
|
2162
2262
|
const internal = item ? item : undefined;
|
|
2163
2263
|
const representation = scope === StorageScope.Secure && internal?._isBiometric === true ? "biometric" : "plain";
|
|
@@ -2228,34 +2328,46 @@ export function createStorageCore(buildAdapter) {
|
|
|
2228
2328
|
};
|
|
2229
2329
|
const tx = {
|
|
2230
2330
|
scope,
|
|
2231
|
-
getRaw: key =>
|
|
2331
|
+
getRaw: key => {
|
|
2332
|
+
assertTransactionOpen();
|
|
2333
|
+
return getRawValue(key, scope);
|
|
2334
|
+
},
|
|
2232
2335
|
setRaw: (key, value) => {
|
|
2336
|
+
assertTransactionOpen();
|
|
2233
2337
|
rememberRollback(key);
|
|
2234
2338
|
setRawValue(key, value, scope);
|
|
2235
2339
|
},
|
|
2236
2340
|
removeRaw: key => {
|
|
2341
|
+
assertTransactionOpen();
|
|
2237
2342
|
rememberRollback(key, undefined, scope === StorageScope.Secure);
|
|
2238
2343
|
removeRawValue(key, scope);
|
|
2239
2344
|
},
|
|
2240
2345
|
getItem: item => {
|
|
2346
|
+
assertTransactionOpen();
|
|
2241
2347
|
assertBatchScope([item], scope);
|
|
2242
2348
|
rememberItemRollback(item);
|
|
2243
2349
|
return item.get();
|
|
2244
2350
|
},
|
|
2245
2351
|
setItem: (item, value) => {
|
|
2352
|
+
assertTransactionOpen();
|
|
2246
2353
|
assertBatchScope([item], scope);
|
|
2247
2354
|
rememberItemRollback(item);
|
|
2248
2355
|
item.set(value);
|
|
2249
2356
|
},
|
|
2250
2357
|
removeItem: item => {
|
|
2358
|
+
assertTransactionOpen();
|
|
2251
2359
|
assertBatchScope([item], scope);
|
|
2252
2360
|
rememberItemRollback(item);
|
|
2253
2361
|
item.delete();
|
|
2254
2362
|
}
|
|
2255
2363
|
};
|
|
2256
2364
|
try {
|
|
2257
|
-
|
|
2365
|
+
const result = transaction(tx);
|
|
2366
|
+
assertSynchronousCallbackResult(result, "Transaction");
|
|
2367
|
+
transactionOpen = false;
|
|
2368
|
+
return result;
|
|
2258
2369
|
} catch (error) {
|
|
2370
|
+
transactionOpen = false;
|
|
2259
2371
|
itemRenameStates.forEach((migrated, item) => {
|
|
2260
2372
|
item._setRenameMigrationState(migrated);
|
|
2261
2373
|
});
|
|
@@ -2586,6 +2698,7 @@ export function createStorageCore(buildAdapter) {
|
|
|
2586
2698
|
removeBatch,
|
|
2587
2699
|
registerMigration,
|
|
2588
2700
|
migrateToLatest,
|
|
2701
|
+
migrateSecureAccessControl,
|
|
2589
2702
|
runTransaction,
|
|
2590
2703
|
createSecureAuthStorage,
|
|
2591
2704
|
internals
|