jazz-tools 0.9.23 → 0.10.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.
Files changed (45) hide show
  1. package/.turbo/turbo-build.log +11 -11
  2. package/CHANGELOG.md +19 -0
  3. package/dist/{chunk-OJIEP4WE.js → chunk-UBD75Z27.js} +566 -118
  4. package/dist/chunk-UBD75Z27.js.map +1 -0
  5. package/dist/index.native.js +17 -5
  6. package/dist/index.native.js.map +1 -1
  7. package/dist/index.web.js +17 -5
  8. package/dist/index.web.js.map +1 -1
  9. package/dist/testing.js +124 -33
  10. package/dist/testing.js.map +1 -1
  11. package/package.json +5 -3
  12. package/src/auth/AuthSecretStorage.ts +109 -0
  13. package/src/auth/DemoAuth.ts +188 -0
  14. package/src/auth/InMemoryKVStore.ts +25 -0
  15. package/src/auth/KvStoreContext.ts +39 -0
  16. package/src/auth/PassphraseAuth.ts +113 -0
  17. package/src/coValues/account.ts +8 -3
  18. package/src/coValues/coFeed.ts +1 -1
  19. package/src/coValues/coList.ts +1 -1
  20. package/src/coValues/coMap.ts +1 -1
  21. package/src/coValues/group.ts +9 -8
  22. package/src/coValues/interfaces.ts +14 -5
  23. package/src/exports.ts +17 -3
  24. package/src/implementation/ContextManager.ts +178 -0
  25. package/src/implementation/activeAccountContext.ts +6 -1
  26. package/src/implementation/createContext.ts +173 -149
  27. package/src/testing.ts +171 -33
  28. package/src/tests/AuthSecretStorage.test.ts +275 -0
  29. package/src/tests/ContextManager.test.ts +256 -0
  30. package/src/tests/DemoAuth.test.ts +269 -0
  31. package/src/tests/PassphraseAuth.test.ts +152 -0
  32. package/src/tests/coFeed.test.ts +44 -39
  33. package/src/tests/coList.test.ts +21 -20
  34. package/src/tests/coMap.test.ts +21 -20
  35. package/src/tests/coPlainText.test.ts +21 -20
  36. package/src/tests/coRichText.test.ts +21 -20
  37. package/src/tests/createContext.test.ts +339 -0
  38. package/src/tests/deepLoading.test.ts +41 -42
  39. package/src/tests/fixtures.ts +2050 -0
  40. package/src/tests/groupsAndAccounts.test.ts +2 -2
  41. package/src/tests/subscribe.test.ts +42 -9
  42. package/src/tests/testing.test.ts +56 -0
  43. package/src/tests/utils.ts +11 -11
  44. package/src/types.ts +54 -0
  45. package/dist/chunk-OJIEP4WE.js.map +0 -1
@@ -122,7 +122,7 @@ describe("Group inheritance", () => {
122
122
  const mapAsReader = await TestMap.load(mapInChild.id, reader, {});
123
123
  expect(mapAsReader?.title).toBe("In Child");
124
124
 
125
- parentGroup.removeMember(reader);
125
+ await parentGroup.removeMember(reader);
126
126
 
127
127
  mapInChild.title = "In Child (updated)";
128
128
 
@@ -161,7 +161,7 @@ describe("Group inheritance", () => {
161
161
  const mapAsReader = await TestMap.load(mapInGrandChild.id, reader, {});
162
162
  expect(mapAsReader?.title).toBe("In Grand Child");
163
163
 
164
- grandParentGroup.removeMember(reader);
164
+ await grandParentGroup.removeMember(reader);
165
165
 
166
166
  mapInGrandChild.title = "In Grand Child (updated)";
167
167
 
@@ -1,4 +1,4 @@
1
- import { describe, expect, it, onTestFinished, vi } from "vitest";
1
+ import { beforeEach, describe, expect, it, onTestFinished, vi } from "vitest";
2
2
  import {
3
3
  Account,
4
4
  CoFeed,
@@ -7,12 +7,15 @@ import {
7
7
  FileStream,
8
8
  Group,
9
9
  co,
10
+ cojsonInternals,
10
11
  } from "../index.web.js";
11
12
  import {
12
13
  type DepthsIn,
14
+ ID,
13
15
  createCoValueObservable,
14
16
  subscribeToCoValue,
15
17
  } from "../internal.js";
18
+ import { setupJazzTestSync } from "../testing.js";
16
19
  import { setupAccount, waitFor } from "./utils.js";
17
20
 
18
21
  class ChatRoom extends CoMap {
@@ -43,6 +46,15 @@ function createMessage(me: Account | Group, text: string) {
43
46
  );
44
47
  }
45
48
 
49
+ beforeEach(async () => {
50
+ await setupJazzTestSync();
51
+ });
52
+
53
+ beforeEach(() => {
54
+ cojsonInternals.CO_VALUE_LOADING_CONFIG.MAX_RETRIES = 1;
55
+ cojsonInternals.CO_VALUE_LOADING_CONFIG.TIMEOUT = 1;
56
+ });
57
+
46
58
  describe("subscribeToCoValue", () => {
47
59
  it("subscribes to a CoMap", async () => {
48
60
  const { me, meOnSecondPeer } = await setupAccount();
@@ -163,7 +175,7 @@ describe("subscribeToCoValue", () => {
163
175
  onTestFinished(unsubscribe);
164
176
 
165
177
  await waitFor(() => {
166
- const lastValue = updateFn.mock.lastCall[0];
178
+ const lastValue = updateFn.mock.lastCall?.[0];
167
179
 
168
180
  expect(lastValue?.messages?.[0]?.text).toBe(message.text);
169
181
  });
@@ -175,7 +187,7 @@ describe("subscribeToCoValue", () => {
175
187
  expect(updateFn).toHaveBeenCalled();
176
188
  });
177
189
 
178
- const lastValue = updateFn.mock.lastCall[0];
190
+ const lastValue = updateFn.mock.lastCall?.[0];
179
191
  expect(lastValue?.messages?.[0]?.text).toBe(
180
192
  "Nevermind, she was gone to the supermarket",
181
193
  );
@@ -212,12 +224,12 @@ describe("subscribeToCoValue", () => {
212
224
  onTestFinished(unsubscribe);
213
225
 
214
226
  await waitFor(() => {
215
- const lastValue = updateFn.mock.lastCall[0];
227
+ const lastValue = updateFn.mock.lastCall?.[0];
216
228
 
217
229
  expect(lastValue?.messages?.[0]?.text).toBe(message.text);
218
230
  });
219
231
 
220
- const initialValue = updateFn.mock.lastCall[0];
232
+ const initialValue = updateFn.mock.lastCall?.[0];
221
233
  const initialMessagesList = initialValue?.messages;
222
234
  const initialMessage1 = initialValue?.messages[0];
223
235
  const initialMessage2 = initialValue?.messages[1];
@@ -231,7 +243,7 @@ describe("subscribeToCoValue", () => {
231
243
  expect(updateFn).toHaveBeenCalled();
232
244
  });
233
245
 
234
- const lastValue = updateFn.mock.lastCall[0];
246
+ const lastValue = updateFn.mock.lastCall?.[0];
235
247
  expect(lastValue).not.toBe(initialValue);
236
248
  expect(lastValue.messages).not.toBe(initialMessagesList);
237
249
  expect(lastValue.messages[0]).not.toBe(initialMessage1);
@@ -278,13 +290,13 @@ describe("subscribeToCoValue", () => {
278
290
  onTestFinished(unsubscribe);
279
291
 
280
292
  await waitFor(() => {
281
- const lastValue = updateFn.mock.lastCall[0];
293
+ const lastValue = updateFn.mock.lastCall?.[0];
282
294
 
283
295
  expect(lastValue?.messages?.[0]?.text).toBe(message.text);
284
296
  expect(lastValue?.messages?.[1]?.text).toBe(message2.text);
285
297
  });
286
298
 
287
- const initialValue = updateFn.mock.lastCall[0];
299
+ const initialValue = updateFn.mock.lastCall?.[0];
288
300
  chatRoom.name = "Me and Luigi";
289
301
 
290
302
  updateFn.mockClear();
@@ -293,7 +305,7 @@ describe("subscribeToCoValue", () => {
293
305
  expect(updateFn).toHaveBeenCalled();
294
306
  });
295
307
 
296
- const lastValue = updateFn.mock.lastCall[0];
308
+ const lastValue = updateFn.mock.lastCall?.[0];
297
309
  expect(lastValue).not.toBe(initialValue);
298
310
  expect(lastValue.name).toBe("Me and Luigi");
299
311
 
@@ -368,4 +380,25 @@ describe("createCoValueObservable", () => {
368
380
  unsubscribe();
369
381
  expect(observable.getCurrentValue()).toBeUndefined();
370
382
  });
383
+
384
+ it("should return null if the coValue is not found", async () => {
385
+ const { meOnSecondPeer } = await setupAccount();
386
+ const observable = createCoValueObservable<TestMap, DepthsIn<TestMap>>();
387
+
388
+ const unsubscribe = observable.subscribe(
389
+ TestMap,
390
+ "co_z123" as ID<TestMap>,
391
+ meOnSecondPeer,
392
+ {},
393
+ () => {},
394
+ );
395
+
396
+ expect(observable.getCurrentValue()).toBeUndefined();
397
+
398
+ await waitFor(() => {
399
+ expect(observable.getCurrentValue()).toBeNull();
400
+ });
401
+
402
+ unsubscribe();
403
+ });
371
404
  });
@@ -48,4 +48,60 @@ describe("Jazz Test Sync", () => {
48
48
 
49
49
  expect(account1.root?.value).toBe("ok");
50
50
  });
51
+
52
+ test("correctly manages the global me during the migrations", async () => {
53
+ class MyRoot extends CoMap {
54
+ value = co.string;
55
+ }
56
+
57
+ class CustomAccount extends Account {
58
+ root = co.ref(MyRoot);
59
+
60
+ migrate() {
61
+ if (this.root === undefined) {
62
+ this.root = MyRoot.create({
63
+ value: "ok",
64
+ });
65
+ }
66
+ }
67
+ }
68
+
69
+ const account1 = await createJazzTestAccount({
70
+ AccountSchema: CustomAccount,
71
+ isCurrentActiveAccount: true,
72
+ });
73
+
74
+ const account2 = await createJazzTestAccount({
75
+ AccountSchema: CustomAccount,
76
+ isCurrentActiveAccount: false,
77
+ });
78
+
79
+ expect(account1.root?.value).toBe("ok");
80
+ expect(account2.root?.value).toBe("ok");
81
+
82
+ expect(Account.getMe()).toBe(account1);
83
+ });
84
+
85
+ test("throws when running multiple migrations in parallel", async () => {
86
+ class CustomAccount extends Account {
87
+ async migrate() {
88
+ await new Promise((resolve) => setTimeout(resolve, 10));
89
+ }
90
+ }
91
+
92
+ const promise = Promise.all([
93
+ createJazzTestAccount({
94
+ AccountSchema: CustomAccount,
95
+ isCurrentActiveAccount: true,
96
+ }),
97
+ createJazzTestAccount({
98
+ AccountSchema: CustomAccount,
99
+ isCurrentActiveAccount: true,
100
+ }),
101
+ ]);
102
+
103
+ await expect(promise).rejects.toThrow(
104
+ "It is not possible to create multiple accounts in parallel inside the test environment.",
105
+ );
106
+ });
51
107
  });
@@ -5,8 +5,7 @@ import { cojsonInternals } from "cojson";
5
5
  import {
6
6
  Account,
7
7
  WasmCrypto,
8
- createJazzContext,
9
- fixedCredentialsAuth,
8
+ createJazzContextFromExistingCredentials,
10
9
  randomSessionProvider,
11
10
  } from "../index.web";
12
11
 
@@ -31,15 +30,16 @@ export async function setupAccount() {
31
30
  throw "me is not a controlled account";
32
31
  }
33
32
  me._raw.core.node.syncManager.addPeer(secondPeer);
34
- const { account: meOnSecondPeer } = await createJazzContext({
35
- auth: fixedCredentialsAuth({
36
- accountID: me.id,
37
- secret: me._raw.agentSecret,
38
- }),
39
- sessionProvider: randomSessionProvider,
40
- peersToLoadFrom: [initialAsPeer],
41
- crypto: Crypto,
42
- });
33
+ const { account: meOnSecondPeer } =
34
+ await createJazzContextFromExistingCredentials({
35
+ credentials: {
36
+ accountID: me.id,
37
+ secret: me._raw.agentSecret,
38
+ },
39
+ sessionProvider: randomSessionProvider,
40
+ peersToLoadFrom: [initialAsPeer],
41
+ crypto: Crypto,
42
+ });
43
43
 
44
44
  return { me, meOnSecondPeer };
45
45
  }
package/src/types.ts ADDED
@@ -0,0 +1,54 @@
1
+ import type { AgentSecret, LocalNode } from "cojson";
2
+ import type { Account } from "./exports.js";
3
+ import type { AnonymousJazzAgent, ID } from "./internal.js";
4
+
5
+ export type AuthCredentials = {
6
+ accountID: ID<Account>;
7
+ secretSeed?: Uint8Array;
8
+ accountSecret: AgentSecret;
9
+ provider?: "anonymous" | "clerk" | "demo" | "passkey" | "passphrase" | string;
10
+ };
11
+
12
+ export type AuthenticateAccountFunction = (
13
+ credentials: AuthCredentials,
14
+ ) => Promise<void>;
15
+ export type RegisterAccountFunction = (
16
+ accountSecret: AgentSecret,
17
+ creationProps: { name: string },
18
+ ) => Promise<ID<Account>>;
19
+
20
+ /** @category Context Creation */
21
+ export type JazzAuthContext<Acc extends Account> = {
22
+ me: Acc;
23
+ node: LocalNode;
24
+ authenticate: AuthenticateAccountFunction;
25
+ logOut: () => Promise<void>;
26
+ done: () => void;
27
+ };
28
+
29
+ export type JazzGuestContext = {
30
+ guest: AnonymousJazzAgent;
31
+ node: LocalNode;
32
+ authenticate: AuthenticateAccountFunction;
33
+ logOut: () => void;
34
+ done: () => void;
35
+ };
36
+
37
+ export type JazzContextType<Acc extends Account> =
38
+ | JazzAuthContext<Acc>
39
+ | JazzGuestContext;
40
+
41
+ export type NewAccountProps = {
42
+ secret?: AgentSecret;
43
+ creationProps?: { name: string };
44
+ };
45
+
46
+ export type SyncConfig =
47
+ | {
48
+ peer: `wss://${string}` | `ws://${string}`;
49
+ when?: "always" | "signedUp";
50
+ }
51
+ | {
52
+ peer?: `wss://${string}` | `ws://${string}`;
53
+ when: "never";
54
+ };