jazz-react-native 0.8.44 → 0.8.46

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,186 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { RNDemoAuth } from "../auth/DemoAuthMethod";
3
+ import { KvStoreContext } from "../storage/kv-store-context";
4
+ // Initialize mock storage
5
+ const mockStorage = {};
6
+ // Mock KvStore implementation
7
+ const mockKvStore = {
8
+ get: vi.fn(async (key) => mockStorage[key] || null),
9
+ set: vi.fn(async (key, value) => {
10
+ mockStorage[key] = value;
11
+ }),
12
+ delete: vi.fn(async (key) => {
13
+ delete mockStorage[key];
14
+ }),
15
+ clearAll: vi.fn(async () => {
16
+ Object.keys(mockStorage).forEach((key) => delete mockStorage[key]);
17
+ }),
18
+ };
19
+ KvStoreContext.getInstance().initialize(mockKvStore);
20
+ beforeEach(() => {
21
+ mockKvStore.clearAll();
22
+ vi.clearAllMocks();
23
+ });
24
+ function setup() {
25
+ const mockDriver = {
26
+ onReady: vi.fn(),
27
+ onSignedIn: vi.fn(),
28
+ onError: vi.fn(),
29
+ };
30
+ return {
31
+ mockStorage,
32
+ mockKvStore,
33
+ mockDriver,
34
+ };
35
+ }
36
+ describe("RNDemoAuth", () => {
37
+ describe("initialization", () => {
38
+ it("should initialize with seed accounts", async () => {
39
+ const { mockKvStore, mockDriver } = setup();
40
+ const seedAccounts = {
41
+ testUser: {
42
+ accountID: "test-account-id",
43
+ accountSecret: "test-secret",
44
+ },
45
+ };
46
+ await RNDemoAuth.init(mockDriver, seedAccounts);
47
+ expect(mockKvStore.set).toHaveBeenCalledWith("demo-auth-existing-users", "testUser");
48
+ expect(mockKvStore.set).toHaveBeenCalledWith("demo-auth-existing-users-" + btoa("testUser"), expect.any(String));
49
+ });
50
+ });
51
+ describe("authentication", () => {
52
+ it("should handle new user signup", async () => {
53
+ const { mockDriver } = setup();
54
+ mockDriver.onReady = vi.fn(({ signUp }) => {
55
+ signUp("testUser");
56
+ });
57
+ const auth = await RNDemoAuth.init(mockDriver);
58
+ const result = await auth.start();
59
+ expect(mockDriver.onReady).toHaveBeenCalled();
60
+ expect(result.type).toBe("new");
61
+ expect(result.saveCredentials).toBeDefined();
62
+ });
63
+ it("should handle existing user login", async () => {
64
+ const { mockStorage, mockDriver } = setup();
65
+ // Set up existing user in storage
66
+ const existingUser = {
67
+ accountID: "test-account-id",
68
+ accountSecret: "test-secret",
69
+ };
70
+ mockStorage["demo-auth-logged-in-secret"] = JSON.stringify(existingUser);
71
+ const auth = await RNDemoAuth.init(mockDriver);
72
+ const result = await auth.start();
73
+ if (result.type !== "existing") {
74
+ throw new Error("Result is not a existing user");
75
+ }
76
+ expect(result.type).toBe("existing");
77
+ expect(result.credentials).toEqual({
78
+ accountID: existingUser.accountID,
79
+ secret: existingUser.accountSecret,
80
+ });
81
+ });
82
+ it("should handle logout", async () => {
83
+ const { mockKvStore, mockDriver } = setup();
84
+ mockDriver.onReady = vi.fn(({ signUp }) => {
85
+ signUp("testUser");
86
+ });
87
+ const auth = await RNDemoAuth.init(mockDriver);
88
+ const result = await auth.start();
89
+ await result.logOut();
90
+ expect(mockKvStore.delete).toHaveBeenCalledWith("demo-auth-logged-in-secret");
91
+ });
92
+ });
93
+ describe("user management", () => {
94
+ it("should signup a new user", async () => {
95
+ const { mockKvStore, mockDriver } = setup();
96
+ mockDriver.onReady = vi.fn(({ signUp }) => {
97
+ return signUp("testUser");
98
+ });
99
+ const auth = await RNDemoAuth.init(mockDriver);
100
+ const result = await auth.start();
101
+ if (result.type !== "new") {
102
+ throw new Error("Result is not a new user");
103
+ }
104
+ await result.saveCredentials({
105
+ accountID: "test-account-id",
106
+ secret: "test-secret",
107
+ });
108
+ expect(mockKvStore.set).toHaveBeenCalledWith("demo-auth-existing-users-" + btoa("testUser"), expect.any(String));
109
+ expect(mockKvStore.set).toHaveBeenCalledWith("demo-auth-existing-users", "testUser");
110
+ expect(mockKvStore.set).toHaveBeenCalledWith("demo-auth-logged-in-secret", expect.any(String));
111
+ });
112
+ it("should login an existing user", async () => {
113
+ const { mockStorage, mockKvStore, mockDriver } = setup();
114
+ const credentials = {
115
+ accountID: "test-account-id",
116
+ accountSecret: "test-secret",
117
+ };
118
+ mockStorage["demo-auth-existing-users-" + btoa("testUser")] =
119
+ JSON.stringify(credentials);
120
+ mockDriver.onReady = vi.fn(({ logInAs }) => {
121
+ return logInAs("testUser");
122
+ });
123
+ const auth = await RNDemoAuth.init(mockDriver);
124
+ const result = await auth.start();
125
+ if (result.type !== "existing") {
126
+ throw new Error("Result is not a existing user");
127
+ }
128
+ expect(result.credentials).toEqual({
129
+ accountID: credentials.accountID,
130
+ secret: credentials.accountSecret,
131
+ });
132
+ expect(mockKvStore.set).toHaveBeenCalledWith("demo-auth-logged-in-secret", JSON.stringify(credentials));
133
+ });
134
+ it("should handle duplicate usernames by adding suffix", async () => {
135
+ const { mockStorage, mockKvStore, mockDriver } = setup();
136
+ mockDriver.onReady = vi.fn(({ signUp }) => {
137
+ return signUp("testUser");
138
+ });
139
+ mockStorage["demo-auth-existing-users"] = "testUser";
140
+ const auth = await RNDemoAuth.init(mockDriver);
141
+ const result = await auth.start();
142
+ if (result.type !== "new") {
143
+ throw new Error("Result is not a new user");
144
+ }
145
+ await result.saveCredentials({
146
+ accountID: "test-account-id",
147
+ secret: "test-secret",
148
+ });
149
+ expect(mockKvStore.set).toHaveBeenCalledWith("demo-auth-existing-users-" + btoa("testUser-2"), expect.any(String));
150
+ expect(mockKvStore.set).toHaveBeenCalledWith("demo-auth-existing-users", "testUser,testUser-2");
151
+ });
152
+ it("should retrieve existing users", async () => {
153
+ const { mockStorage, mockDriver } = setup();
154
+ mockDriver.onReady = vi.fn(({ signUp }) => {
155
+ return signUp("testUser");
156
+ });
157
+ mockStorage["demo-auth-existing-users"] = "user1,user2,user3";
158
+ const auth = await RNDemoAuth.init(mockDriver);
159
+ const result = await auth.start();
160
+ if (result.type !== "new") {
161
+ throw new Error("Result is not a new user");
162
+ }
163
+ await result.saveCredentials({
164
+ accountID: "test-account-id",
165
+ secret: "test-secret",
166
+ });
167
+ const onReadyCall = vi.mocked(mockDriver.onReady).mock.calls[0][0];
168
+ const existingUsers = await onReadyCall.getExistingUsers();
169
+ expect(existingUsers).toEqual(["user1", "user2", "user3", "testUser"]);
170
+ });
171
+ it("should migrate legacy user keys to the new format", async () => {
172
+ const { mockStorage, mockKvStore, mockDriver } = setup();
173
+ const value = JSON.stringify({
174
+ accountID: "test-account-id",
175
+ accountSecret: "test-secret",
176
+ });
177
+ mockStorage["demo-auth-existing-users"] = "testUser";
178
+ mockStorage["demo-auth-existing-users-testUser"] = value;
179
+ await RNDemoAuth.init(mockDriver);
180
+ expect(mockKvStore.set).toHaveBeenCalledWith("demo-auth-existing-users-" + btoa("testUser"), value);
181
+ expect(mockKvStore.set).toHaveBeenCalledWith("demo-auth-storage-version", "2");
182
+ expect(mockKvStore.delete).toHaveBeenCalledWith("demo-auth-existing-users-testUser");
183
+ });
184
+ });
185
+ });
186
+ //# sourceMappingURL=DemoAuthMethod.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DemoAuthMethod.test.js","sourceRoot":"","sources":["../../src/tests/DemoAuthMethod.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jazz-react-native",
3
- "version": "0.8.44",
3
+ "version": "0.8.46",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -11,24 +11,29 @@
11
11
  "react-native": "./dist/index.js",
12
12
  "types": "./dist/index.d.ts",
13
13
  "default": "./dist/index.js"
14
+ },
15
+ "./crypto": {
16
+ "react-native": "./dist/crypto/index.js",
17
+ "types": "./dist/crypto/index.d.ts",
18
+ "default": "./dist/crypto/index.js"
14
19
  }
15
20
  },
16
21
  "license": "MIT",
17
22
  "dependencies": {
23
+ "@scure/base": "1.2.1",
18
24
  "@scure/bip39": "^1.3.0",
19
- "cojson": "0.8.44",
20
- "cojson-transport-ws": "0.8.44",
21
- "jazz-tools": "0.8.44"
25
+ "react-native-quick-crypto": "1.0.0-beta.9",
26
+ "cojson": "0.8.45",
27
+ "cojson-transport-ws": "0.8.45",
28
+ "jazz-tools": "0.8.45"
22
29
  },
23
30
  "peerDependencies": {
24
31
  "@react-native-community/netinfo": "*",
25
- "expo-linking": "*",
26
32
  "expo-secure-store": "*",
27
33
  "react-native": "*"
28
34
  },
29
35
  "devDependencies": {
30
36
  "@react-native-community/netinfo": "^11.4.1",
31
- "expo-linking": "~7.0.3",
32
37
  "expo-secure-store": "~14.0.0",
33
38
  "react-native": "~0.76.3",
34
39
  "typescript": "~5.6.2"
@@ -23,6 +23,69 @@ export namespace RNDemoAuth {
23
23
 
24
24
  const localStorageKey = "demo-auth-logged-in-secret";
25
25
 
26
+ function getUserStorageKey(username: string) {
27
+ return `demo-auth-existing-users-${btoa(username)}`;
28
+ }
29
+
30
+ function getLegacyUserStorageKey(username: string) {
31
+ return `demo-auth-existing-users-${username}`;
32
+ }
33
+
34
+ async function getStorageVersion(kvStore: KvStore) {
35
+ try {
36
+ const version = await kvStore.get("demo-auth-storage-version");
37
+ return version ? parseInt(version) : 1;
38
+ } catch (error) {
39
+ return 1;
40
+ }
41
+ }
42
+
43
+ async function setStorageVersion(kvStore: KvStore, version: number) {
44
+ await kvStore.set("demo-auth-storage-version", version.toString());
45
+ }
46
+
47
+ async function getExistingUsers(kvStore: KvStore) {
48
+ const existingUsers = await kvStore.get("demo-auth-existing-users");
49
+ return existingUsers ? existingUsers.split(",") : [];
50
+ }
51
+
52
+ async function addUserToExistingUsers(username: string, kvStore: KvStore) {
53
+ const existingUsers = await getExistingUsers(kvStore);
54
+
55
+ if (existingUsers.includes(username)) {
56
+ return;
57
+ }
58
+
59
+ await kvStore.set(
60
+ "demo-auth-existing-users",
61
+ existingUsers.concat(username).join(","),
62
+ );
63
+ }
64
+
65
+ /**
66
+ * Migrates existing users keys to use a base64 encoded username.
67
+ *
68
+ * This is done to avoid issues with special characters in the username.
69
+ */
70
+ async function migrateExistingUsersKeys(kvStore: KvStore) {
71
+ if ((await getStorageVersion(kvStore)) >= 2) {
72
+ return;
73
+ }
74
+
75
+ await setStorageVersion(kvStore, 2);
76
+
77
+ const existingUsers = await getExistingUsers(kvStore);
78
+
79
+ for (const existingUsername of existingUsers) {
80
+ const legacyKey = getLegacyUserStorageKey(existingUsername);
81
+ const storageData = await kvStore.get(legacyKey);
82
+ if (storageData) {
83
+ await kvStore.set(getUserStorageKey(existingUsername), storageData);
84
+ await kvStore.delete(legacyKey);
85
+ }
86
+ }
87
+ }
88
+
26
89
  export class RNDemoAuth implements AuthMethod {
27
90
  private constructor(
28
91
  private driver: RNDemoAuth.Driver,
@@ -39,27 +102,16 @@ export class RNDemoAuth implements AuthMethod {
39
102
  },
40
103
  ) {
41
104
  const kvStore = KvStoreContext.getInstance().getStorage();
105
+
106
+ await migrateExistingUsersKeys(kvStore);
107
+
42
108
  for (const [name, credentials] of Object.entries(seedAccounts || {})) {
43
109
  const storageData = JSON.stringify(credentials satisfies StorageData);
44
- if (
45
- !(
46
- (await kvStore.get("demo-auth-existing-users"))?.split(",") as
47
- | string[]
48
- | undefined
49
- )?.includes(name)
50
- ) {
51
- const existingUsers = await kvStore.get("demo-auth-existing-users");
52
- if (existingUsers) {
53
- await kvStore.set(
54
- "demo-auth-existing-users",
55
- existingUsers + "," + name,
56
- );
57
- } else {
58
- await kvStore.set("demo-auth-existing-users", name);
59
- }
60
- }
61
- await kvStore.set("demo-auth-existing-users-" + name, storageData);
110
+
111
+ await addUserToExistingUsers(name, kvStore);
112
+ await kvStore.set(getUserStorageKey(name), storageData);
62
113
  }
114
+
63
115
  return new RNDemoAuth(driver, kvStore);
64
116
  }
65
117
 
@@ -103,13 +155,9 @@ export class RNDemoAuth implements AuthMethod {
103
155
  accountSecret: credentials.secret,
104
156
  } satisfies StorageData);
105
157
 
106
- // Retrieve the list of existing users
107
- const existingUsers = await this.kvStore.get(
108
- "demo-auth-existing-users",
158
+ const existingUsernames = await getExistingUsers(
159
+ this.kvStore,
109
160
  );
110
- const existingUsernames = existingUsers
111
- ? existingUsers.split(",")
112
- : [];
113
161
 
114
162
  // Determine if the username already exists and generate a unique username
115
163
  let uniqueUsername = username;
@@ -122,18 +170,11 @@ export class RNDemoAuth implements AuthMethod {
122
170
  // Save credentials using the unique username
123
171
  await this.kvStore.set(localStorageKey, storageData);
124
172
  await this.kvStore.set(
125
- "demo-auth-existing-users-" + uniqueUsername,
173
+ getUserStorageKey(uniqueUsername),
126
174
  storageData,
127
175
  );
128
176
 
129
- // Update the list of existing users
130
- const updatedUsers = existingUsers
131
- ? `${existingUsers},${uniqueUsername}`
132
- : uniqueUsername;
133
- await this.kvStore.set(
134
- "demo-auth-existing-users",
135
- updatedUsers,
136
- );
177
+ await addUserToExistingUsers(uniqueUsername, this.kvStore);
137
178
  },
138
179
  onSuccess: () => {
139
180
  this.driver.onSignedIn({ logOut });
@@ -149,17 +190,12 @@ export class RNDemoAuth implements AuthMethod {
149
190
  });
150
191
  },
151
192
  getExistingUsers: async () => {
152
- return (
153
- (await this.kvStore.get("demo-auth-existing-users"))?.split(
154
- ",",
155
- ) ?? []
156
- );
193
+ return await getExistingUsers(this.kvStore);
157
194
  },
158
195
  logInAs: async (existingUser) => {
159
196
  const storageData = JSON.parse(
160
- (await this.kvStore.get(
161
- "demo-auth-existing-users-" + existingUser,
162
- )) ?? "{}",
197
+ (await this.kvStore.get(getUserStorageKey(existingUser))) ??
198
+ "{}",
163
199
  ) as StorageData;
164
200
 
165
201
  await this.kvStore.set(
@@ -0,0 +1,58 @@
1
+ import { base58 } from "@scure/base";
2
+ import { JsonValue, PureJSCrypto } from "cojson/native";
3
+ import { CojsonInternalTypes, cojsonInternals } from "cojson/native";
4
+ import { Ed } from "react-native-quick-crypto";
5
+ const { stableStringify } = cojsonInternals;
6
+
7
+ const textEncoder = new TextEncoder();
8
+
9
+ export class RNQuickCrypto extends PureJSCrypto {
10
+ ed: Ed;
11
+
12
+ constructor() {
13
+ super();
14
+ this.ed = new Ed("ed25519", {});
15
+ }
16
+
17
+ static async create(): Promise<RNQuickCrypto> {
18
+ return new RNQuickCrypto();
19
+ }
20
+
21
+ newEd25519SigningKey(): Uint8Array {
22
+ this.ed.generateKeyPairSync();
23
+ return new Uint8Array(this.ed.getPrivateKey());
24
+ }
25
+
26
+ getSignerID(
27
+ secret: CojsonInternalTypes.SignerSecret,
28
+ ): CojsonInternalTypes.SignerID {
29
+ return `signer_z${base58.encode(
30
+ base58.decode(secret.substring("signerSecret_z".length)),
31
+ )}`;
32
+ }
33
+
34
+ sign(
35
+ secret: CojsonInternalTypes.SignerSecret,
36
+ message: JsonValue,
37
+ ): CojsonInternalTypes.Signature {
38
+ const signature = new Uint8Array(
39
+ this.ed.signSync(
40
+ textEncoder.encode(stableStringify(message)),
41
+ base58.decode(secret.substring("signerSecret_z".length)),
42
+ ),
43
+ );
44
+ return `signature_z${base58.encode(signature)}`;
45
+ }
46
+
47
+ verify(
48
+ signature: CojsonInternalTypes.Signature,
49
+ message: JsonValue,
50
+ id: CojsonInternalTypes.SignerID,
51
+ ): boolean {
52
+ return this.ed.verifySync(
53
+ base58.decode(signature.substring("signature_z".length)),
54
+ textEncoder.encode(stableStringify(message)),
55
+ base58.decode(id.substring("signer_z".length)),
56
+ );
57
+ }
58
+ }
@@ -0,0 +1 @@
1
+ export { RNQuickCrypto } from "./RNQuickCrypto.js";
package/src/index.ts CHANGED
@@ -15,15 +15,13 @@ import {
15
15
  createJazzContext,
16
16
  } from "jazz-tools";
17
17
 
18
- import NetInfo from "@react-native-community/netinfo";
19
18
  import { RawAccountID } from "cojson";
20
- import { createWebSocketPeer } from "cojson-transport-ws";
21
- import * as Linking from "expo-linking";
22
- import { PureJSCrypto } from "jazz-tools/native";
23
19
 
24
20
  export { RNDemoAuth } from "./auth/DemoAuthMethod.js";
25
21
 
22
+ import { PureJSCrypto } from "cojson/native";
26
23
  import { createWebSocketPeerWithReconnection } from "./createWebSocketPeerWithReconnection.js";
24
+ import type { RNQuickCrypto } from "./crypto/RNQuickCrypto.js";
27
25
  import { KvStoreContext } from "./storage/kv-store-context.js";
28
26
 
29
27
  /** @category Context Creation */
@@ -51,7 +49,7 @@ export type BaseReactNativeContextOptions = {
51
49
  peer: `wss://${string}` | `ws://${string}`;
52
50
  reconnectionTimeout?: number;
53
51
  storage?: "indexedDB" | "singleTabOPFS";
54
- crypto?: CryptoProvider;
52
+ CryptoProvider?: typeof PureJSCrypto | typeof RNQuickCrypto;
55
53
  };
56
54
 
57
55
  /** @category Context Creation */
@@ -75,17 +73,19 @@ export async function createJazzRNContext<Acc extends Account>(
75
73
  },
76
74
  );
77
75
 
76
+ const CryptoProvider = options.CryptoProvider || PureJSCrypto;
77
+
78
78
  const context =
79
79
  "auth" in options
80
80
  ? await createJazzContext({
81
81
  AccountSchema: options.AccountSchema,
82
82
  auth: options.auth,
83
- crypto: await PureJSCrypto.create(),
83
+ crypto: await CryptoProvider.create(),
84
84
  peersToLoadFrom: [websocketPeer.peer],
85
85
  sessionProvider: provideLockSession,
86
86
  })
87
87
  : await createJazzContext({
88
- crypto: await PureJSCrypto.create(),
88
+ crypto: await CryptoProvider.create(),
89
89
  peersToLoadFrom: [websocketPeer.peer],
90
90
  });
91
91
 
@@ -165,6 +165,7 @@ export function createInviteLink<C extends CoValue>(
165
165
  }
166
166
 
167
167
  /** @category Invite Links */
168
+ // TODO: copied from jazz-browser, should be shared
168
169
  export function parseInviteLink<C extends CoValue>(
169
170
  inviteURL: string,
170
171
  ):
@@ -174,33 +175,67 @@ export function parseInviteLink<C extends CoValue>(
174
175
  inviteSecret: InviteSecret;
175
176
  }
176
177
  | undefined {
177
- const url = Linking.parse(inviteURL);
178
- const parts = url.path?.split("/");
179
-
180
- if (!parts || parts[0] !== "invite") {
181
- return undefined;
182
- }
178
+ const url = new URL(inviteURL);
179
+ const parts = url.hash.split("/");
183
180
 
184
181
  let valueHint: string | undefined;
185
182
  let valueID: ID<C> | undefined;
186
183
  let inviteSecret: InviteSecret | undefined;
187
184
 
188
- if (parts.length === 4) {
189
- valueHint = parts[1];
190
- valueID = parts[2] as ID<C>;
191
- inviteSecret = parts[3] as InviteSecret;
192
- } else if (parts.length === 3) {
193
- valueID = parts[1] as ID<C>;
194
- inviteSecret = parts[2] as InviteSecret;
195
- }
185
+ if (parts[0] === "#" && parts[1] === "invite") {
186
+ if (parts.length === 5) {
187
+ valueHint = parts[2];
188
+ valueID = parts[3] as ID<C>;
189
+ inviteSecret = parts[4] as InviteSecret;
190
+ } else if (parts.length === 4) {
191
+ valueID = parts[2] as ID<C>;
192
+ inviteSecret = parts[3] as InviteSecret;
193
+ }
196
194
 
197
- if (!valueID || !inviteSecret) {
198
- return undefined;
195
+ if (!valueID || !inviteSecret) {
196
+ return undefined;
197
+ }
198
+ return { valueID, inviteSecret, valueHint };
199
199
  }
200
-
201
- return { valueID, inviteSecret, valueHint };
202
200
  }
203
201
 
202
+ // getting out of the `expo` business 🤞
203
+ // export function parseInviteLink<C extends CoValue>(
204
+ // inviteURL: string,
205
+ // ):
206
+ // | {
207
+ // valueID: ID<C>;
208
+ // valueHint?: string;
209
+ // inviteSecret: InviteSecret;
210
+ // }
211
+ // | undefined {
212
+ // const url = Linking.parse(inviteURL);
213
+ // const parts = url.path?.split("/");
214
+
215
+ // if (!parts || parts[0] !== "invite") {
216
+ // return undefined;
217
+ // }
218
+
219
+ // let valueHint: string | undefined;
220
+ // let valueID: ID<C> | undefined;
221
+ // let inviteSecret: InviteSecret | undefined;
222
+
223
+ // if (parts.length === 4) {
224
+ // valueHint = parts[1];
225
+ // valueID = parts[2] as ID<C>;
226
+ // inviteSecret = parts[3] as InviteSecret;
227
+ // } else if (parts.length === 3) {
228
+ // valueID = parts[1] as ID<C>;
229
+ // inviteSecret = parts[2] as InviteSecret;
230
+ // }
231
+
232
+ // if (!valueID || !inviteSecret) {
233
+ // return undefined;
234
+ // }
235
+
236
+ // return { valueID, inviteSecret, valueHint };
237
+ // }
238
+
204
239
  /////////
205
240
 
206
241
  export * from "./provider.js";
package/src/provider.tsx CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  } from "jazz-tools";
15
15
  import { Linking } from "react-native";
16
16
  import {
17
+ BaseReactNativeContextOptions,
17
18
  KvStore,
18
19
  KvStoreContext,
19
20
  ReactNativeContext,
@@ -27,6 +28,11 @@ import { ExpoSecureStoreAdapter } from "./storage/expo-secure-store-adapter.js";
27
28
  export function createJazzRNApp<Acc extends Account>({
28
29
  kvStore = new ExpoSecureStoreAdapter(),
29
30
  AccountSchema = Account as unknown as AccountClass<Acc>,
31
+ CryptoProvider,
32
+ }: {
33
+ kvStore?: KvStore;
34
+ AccountSchema?: AccountClass<Acc>;
35
+ CryptoProvider?: BaseReactNativeContextOptions["CryptoProvider"];
30
36
  } = {}): JazzReactApp<Acc> {
31
37
  const JazzContext = React.createContext<
32
38
  ReactNativeContext<Acc> | ReactNativeGuestContext | undefined
@@ -61,12 +67,14 @@ export function createJazzRNApp<Acc extends Account>({
61
67
  ? {
62
68
  peer,
63
69
  storage,
70
+ CryptoProvider,
64
71
  }
65
72
  : {
66
73
  AccountSchema,
67
74
  auth: auth,
68
75
  peer,
69
76
  storage,
77
+ CryptoProvider,
70
78
  },
71
79
  ).then((context) => {
72
80
  setCtx({