@evolu/react-native 12.0.1-preview.5 → 12.0.1-preview.7

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.
@@ -0,0 +1,8 @@
1
+ import { FC } from "react";
2
+ import { OwnerId } from "@evolu/common";
3
+ export declare const EvoluAvatar: FC<{
4
+ id: OwnerId;
5
+ size?: number;
6
+ borderRadius?: number;
7
+ }>;
8
+ //# sourceMappingURL=EvoluAvatar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EvoluAvatar.d.ts","sourceRoot":"","sources":["../../src/components/EvoluAvatar.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,EAAE,EAAW,MAAM,OAAO,CAAC;AAEpC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAcA,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { View } from "react-native";
3
+ import { SvgXml } from "react-native-svg";
4
+ import { useMemo } from "react";
5
+ import { bloSvg } from "blo";
6
+ export const EvoluAvatar = ({ id, size = 32, borderRadius = 3 }) => {
7
+ const svg = useMemo(() => bloSvg(`0x${id}`), [id]);
8
+ return id ? (_jsx(View, { style: {
9
+ width: size,
10
+ height: size,
11
+ borderRadius,
12
+ overflow: "hidden",
13
+ }, children: _jsx(SvgXml, { xml: svg, width: size, height: size }) })) : null;
14
+ };
@@ -1,3 +1,5 @@
1
1
  import { EvoluDeps } from "@evolu/common/evolu";
2
+ export * from "./components/EvoluAvatar.js";
3
+ export declare const localAuth: import("@evolu/common").LocalAuth;
2
4
  export declare const evoluReactNativeDeps: EvoluDeps;
3
5
  //# sourceMappingURL=expo-sqlite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"expo-sqlite.d.ts","sourceRoot":"","sources":["../src/expo-sqlite.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,SAAS,EAEV,MAAM,qBAAqB,CAAC;AA+B7B,eAAO,MAAM,oBAAoB,EAAE,SAOlC,CAAC"}
1
+ {"version":3,"file":"expo-sqlite.d.ts","sourceRoot":"","sources":["../src/expo-sqlite.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,SAAS,EAEV,MAAM,qBAAqB,CAAC;AA2B7B,cAAc,6BAA6B,CAAC;AAE5C,eAAO,MAAM,SAAS,mCAGpB,CAAC;AAEH,eAAO,MAAM,oBAAoB,EAAE,SAMlC,CAAC"}
@@ -1,17 +1,13 @@
1
1
  import { createConsole, createLocalAuth, createRandom, createRandomBytes, createTime, createWebSocket, } from "@evolu/common";
2
2
  import { createDbWorkerForPlatform, } from "@evolu/common/evolu";
3
3
  import * as Expo from "expo";
4
- import { SensitiveInfo } from "react-native-sensitive-info";
5
4
  import { createExpoSqliteDriver } from "./providers/ExpoSqliteDriver.js";
5
+ import { createSecureStore } from "./utils/LocalAuth.js";
6
6
  import { polyfillHermes } from "./utils/Hermes.js";
7
7
  polyfillHermes();
8
8
  const console = createConsole();
9
9
  const time = createTime();
10
10
  const randomBytes = createRandomBytes();
11
- const localAuth = createLocalAuth({
12
- randomBytes: randomBytes,
13
- secureStorage: SensitiveInfo,
14
- });
15
11
  const createDbWorker = () => createDbWorkerForPlatform({
16
12
  console,
17
13
  createSqliteDriver: createExpoSqliteDriver,
@@ -23,11 +19,15 @@ const createDbWorker = () => createDbWorkerForPlatform({
23
19
  const reloadApp = () => {
24
20
  void Expo.reloadAppAsync();
25
21
  };
22
+ export * from "./components/EvoluAvatar.js";
23
+ export const localAuth = createLocalAuth({
24
+ randomBytes: randomBytes,
25
+ secureStorage: createSecureStore(),
26
+ });
26
27
  export const evoluReactNativeDeps = {
27
28
  console,
28
29
  createDbWorker,
29
30
  randomBytes,
30
- localAuth,
31
31
  reloadApp,
32
32
  time,
33
33
  };
@@ -1,3 +1,5 @@
1
1
  import { EvoluDeps } from "@evolu/common/evolu";
2
+ export * from "./components/EvoluAvatar.js";
3
+ export declare const localAuth: import("@evolu/common").LocalAuth;
2
4
  export declare const evoluReactNativeDeps: EvoluDeps;
3
5
  //# sourceMappingURL=op-sqlite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"op-sqlite.d.ts","sourceRoot":"","sources":["../src/op-sqlite.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,SAAS,EAEV,MAAM,qBAAqB,CAAC;AAmC7B,eAAO,MAAM,oBAAoB,EAAE,SAOlC,CAAC"}
1
+ {"version":3,"file":"op-sqlite.d.ts","sourceRoot":"","sources":["../src/op-sqlite.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,SAAS,EAEV,MAAM,qBAAqB,CAAC;AA+B7B,cAAc,6BAA6B,CAAC;AAE5C,eAAO,MAAM,SAAS,mCAGpB,CAAC;AAEH,eAAO,MAAM,oBAAoB,EAAE,SAMlC,CAAC"}
package/dist/op-sqlite.js CHANGED
@@ -8,10 +8,6 @@ polyfillHermes();
8
8
  const console = createConsole();
9
9
  const time = createTime();
10
10
  const randomBytes = createRandomBytes();
11
- const localAuth = createLocalAuth({
12
- randomBytes: randomBytes,
13
- secureStorage: SensitiveInfo,
14
- });
15
11
  const createDbWorker = () => createDbWorkerForPlatform({
16
12
  console,
17
13
  createSqliteDriver: createOpSqliteDriver,
@@ -28,11 +24,15 @@ const reloadApp = () => {
28
24
  // TODO: reload not implemented for bare rn
29
25
  }
30
26
  };
27
+ export * from "./components/EvoluAvatar.js";
28
+ export const localAuth = createLocalAuth({
29
+ randomBytes: randomBytes,
30
+ secureStorage: SensitiveInfo,
31
+ });
31
32
  export const evoluReactNativeDeps = {
32
33
  console,
33
34
  createDbWorker,
34
35
  randomBytes,
35
- localAuth,
36
36
  reloadApp,
37
37
  time,
38
38
  };
@@ -0,0 +1,3 @@
1
+ import type { SecureStorage } from "@evolu/common";
2
+ export declare const createSecureStore: () => SecureStorage;
3
+ //# sourceMappingURL=LocalAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalAuth.d.ts","sourceRoot":"","sources":["../../src/utils/LocalAuth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,aAAa,EAId,MAAM,eAAe,CAAC;AAEvB,eAAO,MAAM,iBAAiB,QAAO,aAqEpC,CAAC"}
@@ -0,0 +1,110 @@
1
+ import KVStore from "expo-sqlite/kv-store";
2
+ import * as SecureStore from "expo-secure-store";
3
+ import { AUTH_DEFAULT_OPTIONS } from "@evolu/common";
4
+ export const createSecureStore = () => {
5
+ const store = {
6
+ setItem: async (key, value, options) => {
7
+ const rnsiOpts = convertOptions(options);
8
+ const service = options?.service ?? "default";
9
+ const metadata = createMetadata(options?.accessControl === "none");
10
+ await KVStore.setItem(`${service}-${key}`, "1");
11
+ await SecureStore.setItemAsync(key, JSON.stringify({ value, metadata }), rnsiOpts);
12
+ return { metadata };
13
+ },
14
+ getItem: async (key, options) => {
15
+ const rnsiOpts = convertOptions(options);
16
+ const service = options?.service ?? "default";
17
+ let data;
18
+ try {
19
+ const result = await SecureStore.getItemAsync(key, rnsiOpts);
20
+ if (!result)
21
+ return null;
22
+ data = JSON.parse(result);
23
+ }
24
+ catch (_error) {
25
+ return null;
26
+ }
27
+ return { key, service, ...data };
28
+ },
29
+ deleteItem: async (key, options) => {
30
+ const rnsiOpts = convertOptions(options);
31
+ const service = options?.service ?? "default";
32
+ await Promise.all([
33
+ KVStore.removeItemAsync(`${service}-${key}`),
34
+ SecureStore.deleteItemAsync(key, rnsiOpts),
35
+ ]);
36
+ return true;
37
+ },
38
+ getAllItems: async (options) => {
39
+ const keys = await KVStore.getAllKeysAsync();
40
+ const service = options?.service ?? "default";
41
+ const metadata = createMetadata(options?.accessControl === "none");
42
+ return keys
43
+ .filter((key) => key.startsWith(`${service}-`))
44
+ .map((key) => ({
45
+ key: key.slice(service.length + 1),
46
+ service,
47
+ metadata,
48
+ }));
49
+ },
50
+ clearService: async (options) => {
51
+ const rnsiOpts = convertOptions(options);
52
+ const service = options?.service ?? "default";
53
+ const items = await store.getAllItems(options);
54
+ await KVStore.multiRemove(items.map((item) => `${service}-${item.key}`));
55
+ await Promise.all(items.map(async (item) => {
56
+ await SecureStore.deleteItemAsync(item.key, rnsiOpts);
57
+ }));
58
+ },
59
+ };
60
+ return store;
61
+ };
62
+ /**
63
+ * Create default metadata for backwards compatibility with items that don't
64
+ * have stored metadata.
65
+ */
66
+ const createMetadata = (isSecure = true) => {
67
+ return {
68
+ backend: "keychain",
69
+ accessControl: isSecure ? "biometryCurrentSet" : "none",
70
+ securityLevel: isSecure ? "biometry" : "software",
71
+ timestamp: Date.now(),
72
+ };
73
+ };
74
+ function convertOptions(options) {
75
+ const accessGroup = options?.keychainGroup ?? AUTH_DEFAULT_OPTIONS.keychainGroup ?? "";
76
+ const keychainService = options?.service ?? AUTH_DEFAULT_OPTIONS.service ?? "";
77
+ const keychainAccessible = convertKeychainAccessible(options?.accessControl ??
78
+ AUTH_DEFAULT_OPTIONS.accessControl ??
79
+ "biometryCurrentSet");
80
+ const authenticationPrompt = options?.authenticationPrompt?.title ??
81
+ AUTH_DEFAULT_OPTIONS.authenticationPrompt?.title ??
82
+ "";
83
+ return {
84
+ accessGroup,
85
+ keychainService,
86
+ keychainAccessible,
87
+ authenticationPrompt,
88
+ requireAuthentication: options?.accessControl !== "none",
89
+ };
90
+ }
91
+ function convertKeychainAccessible(accessControl) {
92
+ switch (accessControl) {
93
+ case "none":
94
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
95
+ return SecureStore.ALWAYS;
96
+ case "biometryCurrentSet":
97
+ return SecureStore.AFTER_FIRST_UNLOCK;
98
+ case "biometryAny":
99
+ return SecureStore.AFTER_FIRST_UNLOCK;
100
+ case "devicePasscode":
101
+ return SecureStore.AFTER_FIRST_UNLOCK;
102
+ case "secureEnclaveBiometry":
103
+ return SecureStore.AFTER_FIRST_UNLOCK;
104
+ // Exhaustive check
105
+ default:
106
+ accessControl;
107
+ // Default (for typescript, should never hit)
108
+ return SecureStore.AFTER_FIRST_UNLOCK;
109
+ }
110
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evolu/react-native",
3
- "version": "12.0.1-preview.5",
3
+ "version": "12.0.1-preview.7",
4
4
  "description": "Evolu for React Native and Expo",
5
5
  "keywords": [
6
6
  "evolu",
@@ -47,29 +47,36 @@
47
47
  "src/**",
48
48
  "README.md"
49
49
  ],
50
+ "dependencies": {
51
+ "blo": "^2.0.0"
52
+ },
50
53
  "devDependencies": {
51
54
  "@op-engineering/op-sqlite": "^15.0.3",
52
55
  "expo": "^54.0.10",
56
+ "expo-secure-store": "~15.0.7",
53
57
  "expo-sqlite": "~16.0.8",
54
58
  "react-native": "^0.81.4",
55
59
  "react-native-nitro-modules": "^0.31.2",
56
- "react-native-sensitive-info": "^6.0.0-rc.4",
60
+ "react-native-sensitive-info": "^6.0.0-rc.8",
61
+ "react-native-svg": "^15.14.0",
57
62
  "shx": "^0.4.0",
58
63
  "typescript": "^5.9.2",
59
64
  "vitest": "^4.0.4",
60
- "@evolu/common": "6.0.1-preview.20",
61
- "@evolu/react": "9.0.1-preview.5",
65
+ "@evolu/common": "6.0.1-preview.25",
66
+ "@evolu/react": "9.0.1-preview.6",
62
67
  "@evolu/tsconfig": "0.0.2"
63
68
  },
64
69
  "peerDependencies": {
65
- "@evolu/common": "^6.0.1-preview.20",
66
- "@evolu/react": "^9.0.1-preview.5",
70
+ "@evolu/common": "^6.0.1-preview.25",
71
+ "@evolu/react": "^9.0.1-preview.6",
67
72
  "@op-engineering/op-sqlite": ">=12",
68
73
  "expo": ">=54",
69
- "expo-sqlite": ">=15",
74
+ "expo-secure-store": ">=15",
75
+ "expo-sqlite": ">=16",
70
76
  "react-native": ">=0.81",
71
77
  "react-native-nitro-modules": ">=0.31",
72
- "react-native-sensitive-info": ">=6"
78
+ "react-native-sensitive-info": ">=6",
79
+ "react-native-svg": ">=15"
73
80
  },
74
81
  "peerDependenciesMeta": {
75
82
  "@op-engineering/op-sqlite": {
@@ -81,11 +88,17 @@
81
88
  "expo-sqlite": {
82
89
  "optional": true
83
90
  },
91
+ "expo-secure-store": {
92
+ "optional": true
93
+ },
84
94
  "react-native-nitro-modules": {
85
95
  "optional": true
86
96
  },
87
97
  "react-native-sensitive-info": {
88
98
  "optional": true
99
+ },
100
+ "react-native-svg": {
101
+ "optional": true
89
102
  }
90
103
  },
91
104
  "publishConfig": {
@@ -0,0 +1,25 @@
1
+ import { View } from "react-native";
2
+ import { SvgXml } from "react-native-svg";
3
+ import { FC, useMemo } from "react";
4
+ import { bloSvg } from "blo";
5
+ import { OwnerId } from "@evolu/common";
6
+
7
+ export const EvoluAvatar: FC<{
8
+ id: OwnerId;
9
+ size?: number;
10
+ borderRadius?: number;
11
+ }> = ({ id, size = 32, borderRadius = 3 }) => {
12
+ const svg = useMemo(() => bloSvg(`0x${id}`), [id]);
13
+ return id ? (
14
+ <View
15
+ style={{
16
+ width: size,
17
+ height: size,
18
+ borderRadius,
19
+ overflow: "hidden",
20
+ }}
21
+ >
22
+ <SvgXml xml={svg} width={size} height={size} />
23
+ </View>
24
+ ) : null;
25
+ };
@@ -15,8 +15,8 @@ import {
15
15
  } from "@evolu/common/evolu";
16
16
 
17
17
  import * as Expo from "expo";
18
- import { SensitiveInfo } from "react-native-sensitive-info";
19
18
  import { createExpoSqliteDriver } from "./providers/ExpoSqliteDriver.js";
19
+ import { createSecureStore } from "./utils/LocalAuth.js";
20
20
  import { polyfillHermes } from "./utils/Hermes.js";
21
21
 
22
22
  polyfillHermes();
@@ -24,10 +24,6 @@ polyfillHermes();
24
24
  const console = createConsole();
25
25
  const time = createTime();
26
26
  const randomBytes = createRandomBytes();
27
- const localAuth = createLocalAuth({
28
- randomBytes: randomBytes,
29
- secureStorage: SensitiveInfo,
30
- });
31
27
 
32
28
  const createDbWorker: CreateDbWorker = () =>
33
29
  createDbWorkerForPlatform({
@@ -43,11 +39,17 @@ const reloadApp: ReloadApp = () => {
43
39
  void Expo.reloadAppAsync();
44
40
  };
45
41
 
42
+ export * from "./components/EvoluAvatar.js";
43
+
44
+ export const localAuth = createLocalAuth({
45
+ randomBytes: randomBytes,
46
+ secureStorage: createSecureStore(),
47
+ });
48
+
46
49
  export const evoluReactNativeDeps: EvoluDeps = {
47
50
  console,
48
51
  createDbWorker,
49
52
  randomBytes,
50
- localAuth,
51
53
  reloadApp,
52
54
  time,
53
55
  };
package/src/op-sqlite.ts CHANGED
@@ -24,10 +24,6 @@ polyfillHermes();
24
24
  const console = createConsole();
25
25
  const time = createTime();
26
26
  const randomBytes = createRandomBytes();
27
- const localAuth = createLocalAuth({
28
- randomBytes: randomBytes,
29
- secureStorage: SensitiveInfo,
30
- });
31
27
 
32
28
  const createDbWorker: CreateDbWorker = () =>
33
29
  createDbWorkerForPlatform({
@@ -47,11 +43,17 @@ const reloadApp: ReloadApp = () => {
47
43
  }
48
44
  };
49
45
 
46
+ export * from "./components/EvoluAvatar.js";
47
+
48
+ export const localAuth = createLocalAuth({
49
+ randomBytes: randomBytes,
50
+ secureStorage: SensitiveInfo,
51
+ });
52
+
50
53
  export const evoluReactNativeDeps: EvoluDeps = {
51
54
  console,
52
55
  createDbWorker,
53
56
  randomBytes,
54
- localAuth,
55
57
  reloadApp,
56
58
  time,
57
59
  };
@@ -0,0 +1,143 @@
1
+ import KVStore from "expo-sqlite/kv-store";
2
+ import * as SecureStore from "expo-secure-store";
3
+ import { AUTH_DEFAULT_OPTIONS } from "@evolu/common";
4
+
5
+ import type {
6
+ LocalAuthOptions,
7
+ SecureStorage,
8
+ SensitiveInfoItem,
9
+ StorageMetadata,
10
+ AccessControl,
11
+ } from "@evolu/common";
12
+
13
+ export const createSecureStore = (): SecureStorage => {
14
+ const store: SecureStorage = {
15
+ setItem: async (key, value, options) => {
16
+ const rnsiOpts = convertOptions(options);
17
+ const service = options?.service ?? "default";
18
+ const metadata = createMetadata(options?.accessControl === "none");
19
+ await KVStore.setItem(`${service}-${key}`, "1");
20
+ await SecureStore.setItemAsync(
21
+ key,
22
+ JSON.stringify({ value, metadata }),
23
+ rnsiOpts,
24
+ );
25
+ return { metadata };
26
+ },
27
+
28
+ getItem: async (key, options) => {
29
+ const rnsiOpts = convertOptions(options);
30
+ const service = options?.service ?? "default";
31
+ let data: { value: string; metadata: StorageMetadata };
32
+ try {
33
+ const result = await SecureStore.getItemAsync(key, rnsiOpts);
34
+ if (!result) return null;
35
+ data = JSON.parse(result) as {
36
+ value: string;
37
+ metadata: StorageMetadata;
38
+ };
39
+ } catch (_error) {
40
+ return null;
41
+ }
42
+ return { key, service, ...data };
43
+ },
44
+
45
+ deleteItem: async (key, options) => {
46
+ const rnsiOpts = convertOptions(options);
47
+ const service = options?.service ?? "default";
48
+ await Promise.all([
49
+ KVStore.removeItemAsync(`${service}-${key}`),
50
+ SecureStore.deleteItemAsync(key, rnsiOpts),
51
+ ]);
52
+ return true;
53
+ },
54
+
55
+ getAllItems: async (options) => {
56
+ const keys = await KVStore.getAllKeysAsync();
57
+ const service = options?.service ?? "default";
58
+ const metadata = createMetadata(options?.accessControl === "none");
59
+ return keys
60
+ .filter((key) => key.startsWith(`${service}-`))
61
+ .map((key) => ({
62
+ key: key.slice(service.length + 1),
63
+ service,
64
+ metadata,
65
+ }));
66
+ },
67
+
68
+ clearService: async (options) => {
69
+ const rnsiOpts = convertOptions(options);
70
+ const service = options?.service ?? "default";
71
+ const items = await store.getAllItems(options);
72
+ await KVStore.multiRemove(items.map((item) => `${service}-${item.key}`));
73
+ await Promise.all(
74
+ items.map(async (item) => {
75
+ await SecureStore.deleteItemAsync(item.key, rnsiOpts);
76
+ }),
77
+ );
78
+ },
79
+ };
80
+
81
+ return store;
82
+ };
83
+
84
+ /**
85
+ * Create default metadata for backwards compatibility with items that don't
86
+ * have stored metadata.
87
+ */
88
+ const createMetadata = (isSecure = true): SensitiveInfoItem["metadata"] => {
89
+ return {
90
+ backend: "keychain",
91
+ accessControl: isSecure ? "biometryCurrentSet" : "none",
92
+ securityLevel: isSecure ? "biometry" : "software",
93
+ timestamp: Date.now(),
94
+ };
95
+ };
96
+
97
+ function convertOptions(
98
+ options?: LocalAuthOptions,
99
+ ): SecureStore.SecureStoreOptions {
100
+ const accessGroup =
101
+ options?.keychainGroup ?? AUTH_DEFAULT_OPTIONS.keychainGroup ?? "";
102
+ const keychainService =
103
+ options?.service ?? AUTH_DEFAULT_OPTIONS.service ?? "";
104
+ const keychainAccessible = convertKeychainAccessible(
105
+ options?.accessControl ??
106
+ AUTH_DEFAULT_OPTIONS.accessControl ??
107
+ "biometryCurrentSet",
108
+ );
109
+ const authenticationPrompt =
110
+ options?.authenticationPrompt?.title ??
111
+ AUTH_DEFAULT_OPTIONS.authenticationPrompt?.title ??
112
+ "";
113
+ return {
114
+ accessGroup,
115
+ keychainService,
116
+ keychainAccessible,
117
+ authenticationPrompt,
118
+ requireAuthentication: options?.accessControl !== "none",
119
+ };
120
+ }
121
+
122
+ function convertKeychainAccessible(
123
+ accessControl: AccessControl,
124
+ ): SecureStore.KeychainAccessibilityConstant {
125
+ switch (accessControl) {
126
+ case "none":
127
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
128
+ return SecureStore.ALWAYS;
129
+ case "biometryCurrentSet":
130
+ return SecureStore.AFTER_FIRST_UNLOCK;
131
+ case "biometryAny":
132
+ return SecureStore.AFTER_FIRST_UNLOCK;
133
+ case "devicePasscode":
134
+ return SecureStore.AFTER_FIRST_UNLOCK;
135
+ case "secureEnclaveBiometry":
136
+ return SecureStore.AFTER_FIRST_UNLOCK;
137
+ // Exhaustive check
138
+ default:
139
+ accessControl satisfies never;
140
+ // Default (for typescript, should never hit)
141
+ return SecureStore.AFTER_FIRST_UNLOCK;
142
+ }
143
+ }