react-native-nitro-storage 0.4.5 → 0.5.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/README.md +235 -960
- package/SECURITY.md +26 -0
- package/docs/api-reference.md +217 -0
- package/docs/batch-transactions-migrations.md +186 -0
- package/docs/benchmarks.md +37 -0
- package/docs/mmkv-migration.md +80 -0
- package/docs/react-hooks.md +113 -0
- package/docs/recipes.md +281 -0
- package/docs/secure-storage.md +171 -0
- package/docs/web-backends.md +141 -0
- package/lib/commonjs/index.js +51 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +54 -0
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/storage-runtime.js.map +1 -1
- package/lib/module/index.js +51 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +54 -0
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/storage-runtime.js.map +1 -1
- package/lib/typescript/index.d.ts +5 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +5 -2
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/storage-runtime.d.ts +32 -0
- package/lib/typescript/storage-runtime.d.ts.map +1 -1
- package/package.json +21 -8
- package/src/index.ts +67 -1
- package/src/index.web.ts +76 -0
- package/src/storage-runtime.ts +35 -0
package/src/index.ts
CHANGED
|
@@ -21,6 +21,8 @@ import type {
|
|
|
21
21
|
import {
|
|
22
22
|
getStorageErrorCode,
|
|
23
23
|
isLockedStorageErrorCode,
|
|
24
|
+
type SecureStorageMetadata,
|
|
25
|
+
type SecurityCapabilities,
|
|
24
26
|
type StorageCapabilities,
|
|
25
27
|
type StorageErrorCode,
|
|
26
28
|
} from "./storage-runtime";
|
|
@@ -30,6 +32,8 @@ export type { Storage } from "./Storage.nitro";
|
|
|
30
32
|
export { migrateFromMMKV } from "./migration";
|
|
31
33
|
export {
|
|
32
34
|
getStorageErrorCode,
|
|
35
|
+
type SecureStorageMetadata,
|
|
36
|
+
type SecurityCapabilities,
|
|
33
37
|
type StorageCapabilities,
|
|
34
38
|
type StorageErrorCode,
|
|
35
39
|
} from "./storage-runtime";
|
|
@@ -162,6 +166,7 @@ const metricsCounters = new Map<
|
|
|
162
166
|
string,
|
|
163
167
|
{ count: number; totalDurationMs: number; maxDurationMs: number }
|
|
164
168
|
>();
|
|
169
|
+
const nativeSecureBackend = "platform-secure-storage";
|
|
165
170
|
|
|
166
171
|
function recordMetric(
|
|
167
172
|
operation: string,
|
|
@@ -803,7 +808,7 @@ export const storage = {
|
|
|
803
808
|
platform: "native",
|
|
804
809
|
backend: {
|
|
805
810
|
disk: "platform-preferences",
|
|
806
|
-
secure:
|
|
811
|
+
secure: nativeSecureBackend,
|
|
807
812
|
},
|
|
808
813
|
writeBuffering: {
|
|
809
814
|
disk: true,
|
|
@@ -811,6 +816,67 @@ export const storage = {
|
|
|
811
816
|
},
|
|
812
817
|
errorClassification: true,
|
|
813
818
|
}),
|
|
819
|
+
getSecurityCapabilities: (): SecurityCapabilities => ({
|
|
820
|
+
platform: "native",
|
|
821
|
+
secureStorage: {
|
|
822
|
+
backend: nativeSecureBackend,
|
|
823
|
+
encrypted: "available",
|
|
824
|
+
accessControl: "unknown",
|
|
825
|
+
keychainAccessGroup: "unknown",
|
|
826
|
+
hardwareBacked: "unknown",
|
|
827
|
+
},
|
|
828
|
+
biometric: {
|
|
829
|
+
storage: "unknown",
|
|
830
|
+
prompt: "unknown",
|
|
831
|
+
biometryOnly: "unknown",
|
|
832
|
+
biometryOrPasscode: "unknown",
|
|
833
|
+
},
|
|
834
|
+
metadata: {
|
|
835
|
+
perKey: true,
|
|
836
|
+
listsWithoutValues: true,
|
|
837
|
+
persistsTimestamps: false,
|
|
838
|
+
},
|
|
839
|
+
}),
|
|
840
|
+
getSecureMetadata: (key: string): SecureStorageMetadata => {
|
|
841
|
+
return measureOperation(
|
|
842
|
+
"storage:getSecureMetadata",
|
|
843
|
+
StorageScope.Secure,
|
|
844
|
+
() => {
|
|
845
|
+
flushSecureWrites();
|
|
846
|
+
const storageModule = getStorageModule();
|
|
847
|
+
const biometricProtected = storageModule.hasSecureBiometric(key);
|
|
848
|
+
const exists =
|
|
849
|
+
biometricProtected || storageModule.has(key, StorageScope.Secure);
|
|
850
|
+
let kind: SecureStorageMetadata["kind"] = "missing";
|
|
851
|
+
if (exists) {
|
|
852
|
+
kind = biometricProtected ? "biometric" : "secure";
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return {
|
|
856
|
+
key,
|
|
857
|
+
exists,
|
|
858
|
+
kind,
|
|
859
|
+
backend: nativeSecureBackend,
|
|
860
|
+
encrypted: "available",
|
|
861
|
+
hardwareBacked: "unknown",
|
|
862
|
+
biometricProtected,
|
|
863
|
+
valueExposed: false,
|
|
864
|
+
};
|
|
865
|
+
},
|
|
866
|
+
);
|
|
867
|
+
},
|
|
868
|
+
getAllSecureMetadata: (): SecureStorageMetadata[] => {
|
|
869
|
+
return measureOperation(
|
|
870
|
+
"storage:getAllSecureMetadata",
|
|
871
|
+
StorageScope.Secure,
|
|
872
|
+
() => {
|
|
873
|
+
flushSecureWrites();
|
|
874
|
+
return getStorageModule()
|
|
875
|
+
.getAllKeys(StorageScope.Secure)
|
|
876
|
+
.map((key) => storage.getSecureMetadata(key));
|
|
877
|
+
},
|
|
878
|
+
);
|
|
879
|
+
},
|
|
814
880
|
getString: (key: string, scope: StorageScope): string | undefined => {
|
|
815
881
|
return measureOperation("storage:getString", scope, () => {
|
|
816
882
|
return getRawValue(key, scope);
|
package/src/index.web.ts
CHANGED
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
import {
|
|
22
22
|
getStorageErrorCode,
|
|
23
23
|
isLockedStorageErrorCode,
|
|
24
|
+
type SecureStorageMetadata,
|
|
25
|
+
type SecurityCapabilities,
|
|
24
26
|
type StorageCapabilities,
|
|
25
27
|
type StorageErrorCode,
|
|
26
28
|
} from "./storage-runtime";
|
|
@@ -29,6 +31,8 @@ export { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
|
|
|
29
31
|
export { migrateFromMMKV } from "./migration";
|
|
30
32
|
export {
|
|
31
33
|
getStorageErrorCode,
|
|
34
|
+
type SecureStorageMetadata,
|
|
35
|
+
type SecurityCapabilities,
|
|
32
36
|
type StorageCapabilities,
|
|
33
37
|
type StorageErrorCode,
|
|
34
38
|
} from "./storage-runtime";
|
|
@@ -261,6 +265,12 @@ function getBackendName(
|
|
|
261
265
|
return backend?.name ?? `web:${scopeName}`;
|
|
262
266
|
}
|
|
263
267
|
|
|
268
|
+
function getWebSecureEncryptionStatus(
|
|
269
|
+
backend: WebSecureStorageBackend | undefined,
|
|
270
|
+
): "unavailable" | "unknown" {
|
|
271
|
+
return backend?.name === "localStorage:secure" ? "unavailable" : "unknown";
|
|
272
|
+
}
|
|
273
|
+
|
|
264
274
|
function createWebStorageError(
|
|
265
275
|
scope: NonMemoryScope,
|
|
266
276
|
operation: string,
|
|
@@ -1307,6 +1317,72 @@ export const storage = {
|
|
|
1307
1317
|
},
|
|
1308
1318
|
errorClassification: true,
|
|
1309
1319
|
}),
|
|
1320
|
+
getSecurityCapabilities: (): SecurityCapabilities => {
|
|
1321
|
+
const secureBackend = getBackendName(
|
|
1322
|
+
StorageScope.Secure,
|
|
1323
|
+
webSecureStorageBackend,
|
|
1324
|
+
);
|
|
1325
|
+
return {
|
|
1326
|
+
platform: "web",
|
|
1327
|
+
secureStorage: {
|
|
1328
|
+
backend: secureBackend,
|
|
1329
|
+
encrypted: getWebSecureEncryptionStatus(webSecureStorageBackend),
|
|
1330
|
+
accessControl: "unavailable",
|
|
1331
|
+
keychainAccessGroup: "unavailable",
|
|
1332
|
+
hardwareBacked: "unavailable",
|
|
1333
|
+
},
|
|
1334
|
+
biometric: {
|
|
1335
|
+
storage: "unavailable",
|
|
1336
|
+
prompt: "unavailable",
|
|
1337
|
+
biometryOnly: "unavailable",
|
|
1338
|
+
biometryOrPasscode: "unavailable",
|
|
1339
|
+
},
|
|
1340
|
+
metadata: {
|
|
1341
|
+
perKey: true,
|
|
1342
|
+
listsWithoutValues: true,
|
|
1343
|
+
persistsTimestamps: false,
|
|
1344
|
+
},
|
|
1345
|
+
};
|
|
1346
|
+
},
|
|
1347
|
+
getSecureMetadata: (key: string): SecureStorageMetadata => {
|
|
1348
|
+
return measureOperation(
|
|
1349
|
+
"storage:getSecureMetadata",
|
|
1350
|
+
StorageScope.Secure,
|
|
1351
|
+
() => {
|
|
1352
|
+
flushSecureWrites();
|
|
1353
|
+
const biometricProtected = WebStorage.hasSecureBiometric(key);
|
|
1354
|
+
const exists =
|
|
1355
|
+
biometricProtected || WebStorage.has(key, StorageScope.Secure);
|
|
1356
|
+
let kind: SecureStorageMetadata["kind"] = "missing";
|
|
1357
|
+
if (exists) {
|
|
1358
|
+
kind = biometricProtected ? "biometric" : "secure";
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
return {
|
|
1362
|
+
key,
|
|
1363
|
+
exists,
|
|
1364
|
+
kind,
|
|
1365
|
+
backend: getBackendName(StorageScope.Secure, webSecureStorageBackend),
|
|
1366
|
+
encrypted: getWebSecureEncryptionStatus(webSecureStorageBackend),
|
|
1367
|
+
hardwareBacked: "unavailable",
|
|
1368
|
+
biometricProtected,
|
|
1369
|
+
valueExposed: false,
|
|
1370
|
+
};
|
|
1371
|
+
},
|
|
1372
|
+
);
|
|
1373
|
+
},
|
|
1374
|
+
getAllSecureMetadata: (): SecureStorageMetadata[] => {
|
|
1375
|
+
return measureOperation(
|
|
1376
|
+
"storage:getAllSecureMetadata",
|
|
1377
|
+
StorageScope.Secure,
|
|
1378
|
+
() => {
|
|
1379
|
+
flushSecureWrites();
|
|
1380
|
+
return WebStorage.getAllKeys(StorageScope.Secure).map((key) =>
|
|
1381
|
+
storage.getSecureMetadata(key),
|
|
1382
|
+
);
|
|
1383
|
+
},
|
|
1384
|
+
);
|
|
1385
|
+
},
|
|
1310
1386
|
getString: (key: string, scope: StorageScope): string | undefined => {
|
|
1311
1387
|
return measureOperation("storage:getString", scope, () => {
|
|
1312
1388
|
return getRawValue(key, scope);
|
package/src/storage-runtime.ts
CHANGED
|
@@ -19,6 +19,41 @@ export type StorageCapabilities = {
|
|
|
19
19
|
errorClassification: boolean;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
export type SecurityCapabilityStatus = "available" | "unavailable" | "unknown";
|
|
23
|
+
|
|
24
|
+
export type SecurityCapabilities = {
|
|
25
|
+
platform: "native" | "web";
|
|
26
|
+
secureStorage: {
|
|
27
|
+
backend: string;
|
|
28
|
+
encrypted: SecurityCapabilityStatus;
|
|
29
|
+
accessControl: SecurityCapabilityStatus;
|
|
30
|
+
keychainAccessGroup: SecurityCapabilityStatus;
|
|
31
|
+
hardwareBacked: SecurityCapabilityStatus;
|
|
32
|
+
};
|
|
33
|
+
biometric: {
|
|
34
|
+
storage: SecurityCapabilityStatus;
|
|
35
|
+
prompt: SecurityCapabilityStatus;
|
|
36
|
+
biometryOnly: SecurityCapabilityStatus;
|
|
37
|
+
biometryOrPasscode: SecurityCapabilityStatus;
|
|
38
|
+
};
|
|
39
|
+
metadata: {
|
|
40
|
+
perKey: boolean;
|
|
41
|
+
listsWithoutValues: boolean;
|
|
42
|
+
persistsTimestamps: boolean;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type SecureStorageMetadata = {
|
|
47
|
+
key: string;
|
|
48
|
+
exists: boolean;
|
|
49
|
+
kind: "secure" | "biometric" | "missing";
|
|
50
|
+
backend: string;
|
|
51
|
+
encrypted: SecurityCapabilityStatus;
|
|
52
|
+
hardwareBacked: SecurityCapabilityStatus;
|
|
53
|
+
biometricProtected: boolean;
|
|
54
|
+
valueExposed: false;
|
|
55
|
+
};
|
|
56
|
+
|
|
22
57
|
const STORAGE_ERROR_TAG_PATTERN = /\[nitro-error:([a-z_]+)\]/;
|
|
23
58
|
|
|
24
59
|
export function getStorageErrorCode(
|