jazz-react-native 0.8.44 → 0.8.46

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,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({