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

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,CAgBA,CAAA"}
@@ -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,EAAoB,aAAa,EAAqD,MAAM,eAAe,CAAC;AAExH,eAAO,MAAM,iBAAiB,QAAO,aAwDpC,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 => ({ key: key.slice(service.length + 1), service, metadata }));
45
+ },
46
+ clearService: async (options) => {
47
+ const rnsiOpts = convertOptions(options);
48
+ const service = options?.service ?? 'default';
49
+ const items = await store.getAllItems(options);
50
+ await KVStore.multiRemove(items.map(item => `${service}-${item.key}`));
51
+ await Promise.all(items.map(async (item) => {
52
+ await SecureStore.deleteItemAsync(item.key, rnsiOpts);
53
+ }));
54
+ },
55
+ };
56
+ return store;
57
+ };
58
+ /**
59
+ * Create default metadata for backwards compatibility with items that don't
60
+ * have stored metadata.
61
+ */
62
+ const createMetadata = (isSecure = true) => {
63
+ return {
64
+ backend: "keychain",
65
+ accessControl: isSecure ? "biometryCurrentSet" : "none",
66
+ securityLevel: isSecure ? "biometry" : "software",
67
+ timestamp: Date.now(),
68
+ };
69
+ };
70
+ function convertOptions(options) {
71
+ const accessGroup = options?.keychainGroup
72
+ ?? AUTH_DEFAULT_OPTIONS.keychainGroup
73
+ ?? '';
74
+ const keychainService = options?.service
75
+ ?? AUTH_DEFAULT_OPTIONS.service
76
+ ?? '';
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.6",
4
4
  "description": "Evolu for React Native and Expo",
5
5
  "keywords": [
6
6
  "evolu",
@@ -50,26 +50,30 @@
50
50
  "devDependencies": {
51
51
  "@op-engineering/op-sqlite": "^15.0.3",
52
52
  "expo": "^54.0.10",
53
+ "expo-secure-store": "~15.0.7",
53
54
  "expo-sqlite": "~16.0.8",
54
55
  "react-native": "^0.81.4",
55
56
  "react-native-nitro-modules": "^0.31.2",
56
- "react-native-sensitive-info": "^6.0.0-rc.4",
57
+ "react-native-sensitive-info": "^6.0.0-rc.8",
58
+ "react-native-svg": "^15.14.0",
57
59
  "shx": "^0.4.0",
58
60
  "typescript": "^5.9.2",
59
61
  "vitest": "^4.0.4",
60
- "@evolu/common": "6.0.1-preview.20",
61
- "@evolu/react": "9.0.1-preview.5",
62
+ "@evolu/common": "6.0.1-preview.23",
63
+ "@evolu/react": "9.0.1-preview.6",
62
64
  "@evolu/tsconfig": "0.0.2"
63
65
  },
64
66
  "peerDependencies": {
65
- "@evolu/common": "^6.0.1-preview.20",
66
- "@evolu/react": "^9.0.1-preview.5",
67
+ "@evolu/common": "^6.0.1-preview.23",
68
+ "@evolu/react": "^9.0.1-preview.6",
67
69
  "@op-engineering/op-sqlite": ">=12",
68
70
  "expo": ">=54",
69
- "expo-sqlite": ">=15",
71
+ "expo-secure-store": ">=15",
72
+ "expo-sqlite": ">=16",
70
73
  "react-native": ">=0.81",
71
74
  "react-native-nitro-modules": ">=0.31",
72
- "react-native-sensitive-info": ">=6"
75
+ "react-native-sensitive-info": ">=6",
76
+ "react-native-svg": ">=15"
73
77
  },
74
78
  "peerDependenciesMeta": {
75
79
  "@op-engineering/op-sqlite": {
@@ -81,11 +85,17 @@
81
85
  "expo-sqlite": {
82
86
  "optional": true
83
87
  },
88
+ "expo-secure-store": {
89
+ "optional": true
90
+ },
84
91
  "react-native-nitro-modules": {
85
92
  "optional": true
86
93
  },
87
94
  "react-native-sensitive-info": {
88
95
  "optional": true
96
+ },
97
+ "react-native-svg": {
98
+ "optional": true
89
99
  }
90
100
  },
91
101
  "publishConfig": {
@@ -0,0 +1,27 @@
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 style={{
15
+ width: size,
16
+ height: size,
17
+ borderRadius,
18
+ overflow: 'hidden',
19
+ }}>
20
+ <SvgXml
21
+ xml={svg}
22
+ width={size}
23
+ height={size}
24
+ />
25
+ </View>
26
+ ) : null;
27
+ }
@@ -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,118 @@
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 { LocalAuthOptions, SecureStorage, SensitiveInfoItem, StorageMetadata, AccessControl } from "@evolu/common";
6
+
7
+ export const createSecureStore = (): SecureStorage => {
8
+ const store: SecureStorage = {
9
+ setItem: async (key, value, options) => {
10
+ const rnsiOpts = convertOptions(options);
11
+ const service = options?.service ?? 'default';
12
+ const metadata = createMetadata(options?.accessControl === 'none');
13
+ await KVStore.setItem(`${service}-${key}`, '1');
14
+ await SecureStore.setItemAsync(key, JSON.stringify({value, metadata}), rnsiOpts);
15
+ return { metadata };
16
+ },
17
+
18
+ getItem: async (key, options) => {
19
+ const rnsiOpts = convertOptions(options);
20
+ const service = options?.service ?? 'default';
21
+ let data: {value: string; metadata: StorageMetadata};
22
+ try {
23
+ const result = await SecureStore.getItemAsync(key, rnsiOpts);
24
+ if (!result) return null;
25
+ data = JSON.parse(result) as {value: string; metadata: StorageMetadata};
26
+ } catch (_error) {
27
+ return null;
28
+ }
29
+ return { key, service, ...data };
30
+ },
31
+
32
+ deleteItem: async (key, options) => {
33
+ const rnsiOpts = convertOptions(options);
34
+ const service = options?.service ?? 'default';
35
+ await Promise.all([
36
+ KVStore.removeItemAsync(`${service}-${key}`),
37
+ SecureStore.deleteItemAsync(key, rnsiOpts),
38
+ ]);
39
+ return true;
40
+ },
41
+
42
+ getAllItems: async (options) => {
43
+ const keys = await KVStore.getAllKeysAsync();
44
+ const service = options?.service ?? 'default';
45
+ const metadata = createMetadata(options?.accessControl === 'none');
46
+ return keys
47
+ .filter(key => key.startsWith(`${service}-`))
48
+ .map(key => ({ key: key.slice(service.length + 1), service, metadata }));
49
+ },
50
+
51
+ clearService: async (options) => {
52
+ const rnsiOpts = convertOptions(options);
53
+ const service = options?.service ?? 'default';
54
+ const items = await store.getAllItems(options);
55
+ await KVStore.multiRemove(items.map(item => `${service}-${item.key}`));
56
+ await Promise.all(items.map(async (item) => {
57
+ await SecureStore.deleteItemAsync(item.key, rnsiOpts);
58
+ }));
59
+ },
60
+ };
61
+
62
+ return store;
63
+ };
64
+
65
+ /**
66
+ * Create default metadata for backwards compatibility with items that don't
67
+ * have stored metadata.
68
+ */
69
+ const createMetadata = (isSecure = true): SensitiveInfoItem["metadata"] => {
70
+ return {
71
+ backend: "keychain",
72
+ accessControl: isSecure ? "biometryCurrentSet" : "none",
73
+ securityLevel: isSecure ? "biometry" : "software",
74
+ timestamp: Date.now(),
75
+ };
76
+ };
77
+
78
+ function convertOptions(options?: LocalAuthOptions): SecureStore.SecureStoreOptions {
79
+ const accessGroup = options?.keychainGroup
80
+ ?? AUTH_DEFAULT_OPTIONS.keychainGroup
81
+ ?? '';
82
+ const keychainService = options?.service
83
+ ?? AUTH_DEFAULT_OPTIONS.service
84
+ ?? '';
85
+ const keychainAccessible = convertKeychainAccessible(options?.accessControl
86
+ ?? AUTH_DEFAULT_OPTIONS.accessControl
87
+ ?? 'biometryCurrentSet');
88
+ const authenticationPrompt = options?.authenticationPrompt?.title
89
+ ?? AUTH_DEFAULT_OPTIONS.authenticationPrompt?.title
90
+ ?? '';
91
+ return {
92
+ accessGroup,
93
+ keychainService,
94
+ keychainAccessible,
95
+ authenticationPrompt,
96
+ requireAuthentication: options?.accessControl !== 'none',
97
+ };
98
+ }
99
+
100
+ function convertKeychainAccessible(accessControl: AccessControl): SecureStore.KeychainAccessibilityConstant {
101
+ switch (accessControl) {
102
+ case "none":
103
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
104
+ return SecureStore.ALWAYS;
105
+ case "biometryCurrentSet":
106
+ return SecureStore.AFTER_FIRST_UNLOCK;
107
+ case "biometryAny":
108
+ return SecureStore.AFTER_FIRST_UNLOCK;
109
+ case "devicePasscode":
110
+ return SecureStore.AFTER_FIRST_UNLOCK;
111
+ case "secureEnclaveBiometry":
112
+ return SecureStore.AFTER_FIRST_UNLOCK;
113
+ // Exhaustive check
114
+ default: accessControl satisfies never;
115
+ // Default (for typescript, should never hit)
116
+ return SecureStore.AFTER_FIRST_UNLOCK;
117
+ }
118
+ }