jazz-tools 0.8.3 → 0.8.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/native/coValues/interfaces.js +15 -0
  3. package/dist/native/coValues/interfaces.js.map +1 -1
  4. package/dist/native/exports.js +11 -0
  5. package/dist/native/exports.js.map +1 -0
  6. package/dist/native/implementation/refs.js +4 -4
  7. package/dist/native/implementation/refs.js.map +1 -1
  8. package/dist/native/implementation/schema.js +14 -9
  9. package/dist/native/implementation/schema.js.map +1 -1
  10. package/dist/native/implementation/subscriptionScope.js +8 -9
  11. package/dist/native/implementation/subscriptionScope.js.map +1 -1
  12. package/dist/native/index.native.js +1 -10
  13. package/dist/native/index.native.js.map +1 -1
  14. package/dist/web/coValues/interfaces.js +15 -0
  15. package/dist/web/coValues/interfaces.js.map +1 -1
  16. package/dist/web/exports.js +11 -0
  17. package/dist/web/exports.js.map +1 -0
  18. package/dist/web/implementation/refs.js +4 -4
  19. package/dist/web/implementation/refs.js.map +1 -1
  20. package/dist/web/implementation/schema.js +14 -9
  21. package/dist/web/implementation/schema.js.map +1 -1
  22. package/dist/web/implementation/subscriptionScope.js +8 -9
  23. package/dist/web/implementation/subscriptionScope.js.map +1 -1
  24. package/dist/web/index.web.js +1 -10
  25. package/dist/web/index.web.js.map +1 -1
  26. package/package.json +6 -6
  27. package/src/coValues/interfaces.ts +34 -0
  28. package/src/exports.ts +35 -0
  29. package/src/implementation/refs.ts +3 -8
  30. package/src/implementation/schema.ts +21 -13
  31. package/src/implementation/subscriptionScope.ts +10 -11
  32. package/src/index.native.ts +2 -35
  33. package/src/index.web.ts +2 -35
  34. package/src/tests/coMap.test.ts +1 -1
  35. package/src/tests/schema.test.ts +205 -0
  36. package/src/tests/subscribe.test.ts +348 -0
  37. package/.turbo/turbo-build.log +0 -8
  38. package/.turbo/turbo-lint.log +0 -4
  39. package/.turbo/turbo-test.log +0 -144
@@ -0,0 +1,348 @@
1
+ const Crypto = await WasmCrypto.create();
2
+ import { expect, describe, it, vi, onTestFinished } from "vitest";
3
+ import { connectedPeers } from "cojson/src/streamUtils.js";
4
+ import {
5
+ Account,
6
+ CoList,
7
+ CoMap,
8
+ CoStream,
9
+ WasmCrypto,
10
+ co,
11
+ isControlledAccount,
12
+ createJazzContext,
13
+ fixedCredentialsAuth,
14
+ } from "../index.web.js";
15
+ import {
16
+ BinaryCoStream,
17
+ Group,
18
+ randomSessionProvider,
19
+ subscribeToCoValue,
20
+ } from "../internal.js";
21
+
22
+ class ChatRoom extends CoMap {
23
+ messages = co.ref(MessagesList);
24
+ name = co.string;
25
+ }
26
+
27
+ class Message extends CoMap {
28
+ text = co.string;
29
+ reactions = co.ref(ReactionsStream);
30
+ attachment = co.optional.ref(BinaryCoStream);
31
+ }
32
+
33
+ class MessagesList extends CoList.Of(co.ref(Message)) {}
34
+ class ReactionsStream extends CoStream.Of(co.string) {}
35
+
36
+ async function setupAccount() {
37
+ const me = await Account.create({
38
+ creationProps: { name: "Hermes Puggington" },
39
+ crypto: Crypto,
40
+ });
41
+
42
+ const [initialAsPeer, secondPeer] = connectedPeers("initial", "second", {
43
+ peer1role: "server",
44
+ peer2role: "client",
45
+ });
46
+
47
+ if (!isControlledAccount(me)) {
48
+ throw "me is not a controlled account";
49
+ }
50
+ me._raw.core.node.syncManager.addPeer(secondPeer);
51
+ const { account: meOnSecondPeer } = await createJazzContext({
52
+ auth: fixedCredentialsAuth({
53
+ accountID: me.id,
54
+ secret: me._raw.agentSecret,
55
+ }),
56
+ sessionProvider: randomSessionProvider,
57
+ peersToLoadFrom: [initialAsPeer],
58
+ crypto: Crypto,
59
+ });
60
+
61
+ return { me, meOnSecondPeer };
62
+ }
63
+
64
+ function createChatRoom(me: Account | Group, name: string) {
65
+ return ChatRoom.create(
66
+ { messages: MessagesList.create([], { owner: me }), name },
67
+ { owner: me },
68
+ );
69
+ }
70
+
71
+ function createMessage(me: Account | Group, text: string) {
72
+ return Message.create(
73
+ { text, reactions: ReactionsStream.create([], { owner: me }) },
74
+ { owner: me },
75
+ );
76
+ }
77
+
78
+ describe("subscribeToCoValue", () => {
79
+ it("subscribes to a CoMap", async () => {
80
+ const { me, meOnSecondPeer } = await setupAccount();
81
+
82
+ const chatRoom = createChatRoom(me, "General");
83
+ const updateFn = vi.fn();
84
+
85
+ const unsubscribe = subscribeToCoValue(
86
+ ChatRoom,
87
+ chatRoom.id,
88
+ meOnSecondPeer,
89
+ {},
90
+ updateFn,
91
+ );
92
+
93
+ onTestFinished(unsubscribe);
94
+
95
+ await waitFor(() => {
96
+ expect(updateFn).toHaveBeenCalled();
97
+ });
98
+
99
+ expect(updateFn).toHaveBeenCalledWith(
100
+ expect.objectContaining({
101
+ id: chatRoom.id,
102
+ messages: null,
103
+ name: "General",
104
+ }),
105
+ );
106
+
107
+ updateFn.mockClear();
108
+
109
+ await waitFor(() => {
110
+ expect(updateFn).toHaveBeenCalled();
111
+ });
112
+
113
+ expect(updateFn).toHaveBeenCalledWith(
114
+ expect.objectContaining({
115
+ id: chatRoom.id,
116
+ name: "General",
117
+ messages: expect.any(Array),
118
+ }),
119
+ );
120
+
121
+ updateFn.mockClear();
122
+ chatRoom.name = "Lounge";
123
+
124
+ await waitFor(() => {
125
+ expect(updateFn).toHaveBeenCalled();
126
+ });
127
+
128
+ expect(updateFn).toHaveBeenCalledWith(
129
+ expect.objectContaining({
130
+ id: chatRoom.id,
131
+ name: "Lounge",
132
+ messages: expect.any(Array),
133
+ }),
134
+ );
135
+ });
136
+
137
+ it("shouldn't fire updates until the declared load depth isn't reached", async () => {
138
+ const { me, meOnSecondPeer } = await setupAccount();
139
+
140
+ const chatRoom = createChatRoom(me, "General");
141
+ const updateFn = vi.fn();
142
+
143
+ const unsubscribe = subscribeToCoValue(
144
+ ChatRoom,
145
+ chatRoom.id,
146
+ meOnSecondPeer,
147
+ {
148
+ messages: [],
149
+ },
150
+ updateFn,
151
+ );
152
+
153
+ onTestFinished(unsubscribe);
154
+
155
+ await waitFor(() => {
156
+ expect(updateFn).toHaveBeenCalled();
157
+ });
158
+
159
+ expect(updateFn).toHaveBeenCalledTimes(1);
160
+ expect(updateFn).toHaveBeenCalledWith(
161
+ expect.objectContaining({
162
+ id: chatRoom.id,
163
+ name: "General",
164
+ messages: expect.any(Array),
165
+ }),
166
+ );
167
+ });
168
+
169
+ it("should fire updates when a ref entity is updates", async () => {
170
+ const { me, meOnSecondPeer } = await setupAccount();
171
+
172
+ const chatRoom = createChatRoom(me, "General");
173
+ const message = createMessage(me, "Hello Luigi, are you ready to save the princess?");
174
+ chatRoom.messages?.push(message);
175
+
176
+ const updateFn = vi.fn()
177
+
178
+ const unsubscribe = subscribeToCoValue(
179
+ ChatRoom,
180
+ chatRoom.id,
181
+ meOnSecondPeer,
182
+ {
183
+ messages: [{
184
+ }],
185
+ },
186
+ updateFn,
187
+ );
188
+
189
+ onTestFinished(unsubscribe);
190
+
191
+ await waitFor(() => {
192
+ const lastValue = updateFn.mock.lastCall[0];
193
+
194
+ expect(lastValue?.messages?.[0]?.text).toBe(message.text);
195
+ });
196
+
197
+ message.text = "Nevermind, she was gone to the supermarket";
198
+ updateFn.mockClear();
199
+
200
+ await waitFor(() => {
201
+ expect(updateFn).toHaveBeenCalled();
202
+ });
203
+
204
+ const lastValue = updateFn.mock.lastCall[0];
205
+ expect(lastValue?.messages?.[0]?.text).toBe("Nevermind, she was gone to the supermarket");
206
+ });
207
+
208
+ it("should handle the updates as immutable changes", async () => {
209
+ const { me, meOnSecondPeer } = await setupAccount();
210
+
211
+ const chatRoom = createChatRoom(me, "General");
212
+ const message = createMessage(me, "Hello Luigi, are you ready to save the princess?");
213
+ const message2 = createMessage(me, "Let's go!");
214
+ chatRoom.messages?.push(message);
215
+ chatRoom.messages?.push(message2);
216
+
217
+ const updateFn = vi.fn()
218
+
219
+ const unsubscribe = subscribeToCoValue(
220
+ ChatRoom,
221
+ chatRoom.id,
222
+ meOnSecondPeer,
223
+ {
224
+ messages: [{
225
+ reactions: [],
226
+ }],
227
+ },
228
+ updateFn,
229
+ );
230
+
231
+ onTestFinished(unsubscribe);
232
+
233
+ await waitFor(() => {
234
+ const lastValue = updateFn.mock.lastCall[0];
235
+
236
+ expect(lastValue?.messages?.[0]?.text).toBe(message.text);
237
+ });
238
+
239
+ const initialValue = updateFn.mock.lastCall[0];
240
+ const initialMessagesList = initialValue?.messages;
241
+ const initialMessage1 = initialValue?.messages[0];
242
+ const initialMessage2 = initialValue?.messages[1];
243
+ const initialMessageReactions = initialValue?.messages[0].reactions;
244
+
245
+ message.reactions?.push("👍");
246
+
247
+ updateFn.mockClear();
248
+
249
+ await waitFor(() => {
250
+ expect(updateFn).toHaveBeenCalled();
251
+ });
252
+
253
+ const lastValue = updateFn.mock.lastCall[0];
254
+ expect(lastValue).not.toBe(initialValue);
255
+ expect(lastValue.messages).not.toBe(initialMessagesList);
256
+ expect(lastValue.messages[0]).not.toBe(initialMessage1);
257
+ expect(lastValue.messages[0].reactions).not.toBe(initialMessageReactions);
258
+
259
+ // This shouldn't change
260
+ expect(lastValue.messages[1]).toBe(initialMessage2);
261
+
262
+ // TODO: The initial should point at that snapshot in time
263
+ // expect(lastValue.messages).not.toBe(initialValue.messages);
264
+ // expect(lastValue.messages[0]).not.toBe(initialValue.messages[0]);
265
+ // expect(lastValue.messages[1]).toBe(initialValue.messages[1]);
266
+ // expect(lastValue.messages[0].reactions).not.toBe(initialValue.messages[0].reactions);
267
+ });
268
+
269
+ it("should keep the same identity on the ref entities when a property is updated", async () => {
270
+ const { me, meOnSecondPeer } = await setupAccount();
271
+
272
+ const chatRoom = createChatRoom(me, "General");
273
+ const message = createMessage(me, "Hello Luigi, are you ready to save the princess?");
274
+ const message2 = createMessage(me, "Let's go!");
275
+ chatRoom.messages?.push(message);
276
+ chatRoom.messages?.push(message2);
277
+
278
+ const updateFn = vi.fn()
279
+
280
+ const unsubscribe = subscribeToCoValue(
281
+ ChatRoom,
282
+ chatRoom.id,
283
+ meOnSecondPeer,
284
+ {
285
+ messages: [{
286
+ reactions: [],
287
+ }],
288
+ },
289
+ updateFn,
290
+ );
291
+
292
+ onTestFinished(unsubscribe);
293
+
294
+ await waitFor(() => {
295
+ const lastValue = updateFn.mock.lastCall[0];
296
+
297
+ expect(lastValue?.messages?.[0]?.text).toBe(message.text);
298
+ expect(lastValue?.messages?.[1]?.text).toBe(message2.text);
299
+ });
300
+
301
+ const initialValue = updateFn.mock.lastCall[0];
302
+ chatRoom.name = "Me and Luigi";
303
+
304
+ updateFn.mockClear();
305
+
306
+ await waitFor(() => {
307
+ expect(updateFn).toHaveBeenCalled();
308
+ });
309
+
310
+ const lastValue = updateFn.mock.lastCall[0];
311
+ expect(lastValue).not.toBe(initialValue);
312
+ expect(lastValue.name).toBe("Me and Luigi");
313
+ expect(initialValue.name).toBe("General");
314
+
315
+ expect(lastValue.messages).toBe(initialValue.messages);
316
+ expect(lastValue.messages[0]).toBe(initialValue.messages[0]);
317
+ expect(lastValue.messages[1]).toBe(initialValue.messages[1]);
318
+ });
319
+ });
320
+
321
+
322
+ function waitFor(callback: () => boolean | void) {
323
+ return new Promise<void>((resolve, reject) => {
324
+ const checkPassed = () => {
325
+ try {
326
+ return { ok: callback(), error: null };
327
+ } catch (error) {
328
+ return { ok: false, error };
329
+ }
330
+ };
331
+
332
+ let retries = 0;
333
+
334
+ const interval = setInterval(() => {
335
+ const { ok, error } = checkPassed();
336
+
337
+ if (ok !== false) {
338
+ clearInterval(interval);
339
+ resolve();
340
+ }
341
+
342
+ if (++retries > 10) {
343
+ clearInterval(interval);
344
+ reject(error);
345
+ }
346
+ }, 100);
347
+ });
348
+ }
@@ -1,8 +0,0 @@
1
-
2
- > jazz-tools@0.8.1 build /Users/anselm/jazz/jazz/packages/jazz-tools
3
- > npm run lint && rm -rf ./dist && tsc --sourceMap --outDir dist
4
-
5
-
6
- > jazz-tools@0.8.1 lint
7
- > eslint . --ext ts,tsx
8
-
@@ -1,4 +0,0 @@
1
-
2
- > jazz-tools@0.7.35-guest-auth.5 lint /Users/anselm/jazz/jazz/packages/jazz-tools
3
- > eslint . --ext ts,tsx
4
-
@@ -1,144 +0,0 @@
1
-
2
- > jazz-tools@0.7.35-unique.2 test /Users/anselm/jazz/jazz/packages/jazz-tools
3
- > vitest --run
4
-
5
-
6
- RUN v0.34.6 /Users/anselm/jazz/jazz/packages/jazz-tools
7
-
8
- ✓ src/tests/groupsAndAccounts.test.ts (1 test) 64ms
9
- stdout | unknown test
10
- TestMap schema undefined
11
-
12
- ✓ src/tests/deepLoading.test.ts (3 tests) 83ms
13
- stdout | src/tests/coList.test.ts > Simple CoList operations > Mutation > splice
14
- [ 'bread', 'onion' ] 0
15
- [ 'bread', 'salt', 'onion' ] 1
16
-
17
- stdout | src/tests/coList.test.ts > CoList resolution > Subscription & auto-resolution
18
- subscribedList?.[0]?.[0]?.[0] undefined
19
- subscribedList?.[0]?.[0]?.[0] undefined
20
- subscribedList?.[0]?.[0]?.[0] a
21
- subscribedList?.[0]?.[0]?.[0] x
22
- subscribedList?.[0]?.[0]?.[0] y
23
- subscribedList?.[0]?.[0]?.[0] w
24
-
25
- ✓ src/tests/coList.test.ts (10 tests) 190ms
26
- stdout | src/tests/coStream.test.ts > CoStream resolution > Subscription & auto-resolution
27
- subscribedStream[me.id] {
28
- value: [Getter],
29
- ref: [Getter],
30
- by: [Getter],
31
- madeAt: 2024-09-05T17:35:38.414Z,
32
- tx: {
33
- sessionID: 'co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_ziAMdGVstAxF',
34
- txIndex: 0
35
- }
36
- }
37
- subscribedStream[me.id]?.value?.[me.id]?.value undefined
38
- subscribedStream[me.id]?.value?.[me.id]?.value?.[me.id]?.value undefined
39
- subscribedStream[me.id] {
40
- value: [Getter],
41
- ref: [Getter],
42
- by: [Getter],
43
- madeAt: 2024-09-05T17:35:38.414Z,
44
- tx: {
45
- sessionID: 'co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_ziAMdGVstAxF',
46
- txIndex: 0
47
- }
48
- }
49
- subscribedStream[me.id]?.value?.[me.id]?.value null
50
- subscribedStream[me.id]?.value?.[me.id]?.value?.[me.id]?.value undefined
51
- subscribedStream[me.id] {
52
- value: [Getter],
53
- ref: [Getter],
54
- by: [Getter],
55
- madeAt: 2024-09-05T17:35:38.414Z,
56
- tx: {
57
- sessionID: 'co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_ziAMdGVstAxF',
58
- txIndex: 0
59
- }
60
- }
61
- subscribedStream[me.id]?.value?.[me.id]?.value {
62
- id: 'co_zHELVqRmAN6YuGDpKqVrSXJ5qe1',
63
- _type: 'CoStream',
64
- perSession: undefined,
65
- co_zURWEpvoH1e35FGHVxadgXKU8Ri: 'milk',
66
- in: { co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_ziAMdGVstAxF: 'milk' }
67
- }
68
- subscribedStream[me.id]?.value?.[me.id]?.value?.[me.id]?.value milk
69
- subscribedStream[me.id] {
70
- value: [Getter],
71
- ref: [Getter],
72
- by: [Getter],
73
- madeAt: 2024-09-05T17:35:38.414Z,
74
- tx: {
75
- sessionID: 'co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_ziAMdGVstAxF',
76
- txIndex: 0
77
- }
78
- }
79
- subscribedStream[me.id]?.value?.[me.id]?.value {
80
- id: 'co_zHELVqRmAN6YuGDpKqVrSXJ5qe1',
81
- _type: 'CoStream',
82
- perSession: undefined,
83
- co_zURWEpvoH1e35FGHVxadgXKU8Ri: 'bread',
84
- in: {
85
- co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_ziAMdGVstAxF: 'milk',
86
- co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_zgik2JjD86Qa: 'bread'
87
- }
88
- }
89
- subscribedStream[me.id]?.value?.[me.id]?.value?.[me.id]?.value bread
90
- subscribedStream[me.id] {
91
- value: [Getter],
92
- ref: [Getter],
93
- by: [Getter],
94
- madeAt: 2024-09-05T17:35:38.424Z,
95
- tx: {
96
- sessionID: 'co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_zgik2JjD86Qa',
97
- txIndex: 0
98
- }
99
- }
100
- subscribedStream[me.id]?.value?.[me.id]?.value {
101
- id: 'co_zJmdvJY15XJkWF3n3rwPgGfebxa',
102
- _type: 'CoStream',
103
- perSession: undefined,
104
- co_zURWEpvoH1e35FGHVxadgXKU8Ri: 'butter',
105
- in: { co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_zgik2JjD86Qa: 'butter' }
106
- }
107
- subscribedStream[me.id]?.value?.[me.id]?.value?.[me.id]?.value butter
108
- subscribedStream[me.id] {
109
- value: [Getter],
110
- ref: [Getter],
111
- by: [Getter],
112
- madeAt: 2024-09-05T17:35:38.424Z,
113
- tx: {
114
- sessionID: 'co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_zgik2JjD86Qa',
115
- txIndex: 0
116
- }
117
- }
118
- subscribedStream[me.id]?.value?.[me.id]?.value {
119
- id: 'co_zJmdvJY15XJkWF3n3rwPgGfebxa',
120
- _type: 'CoStream',
121
- perSession: undefined,
122
- co_zURWEpvoH1e35FGHVxadgXKU8Ri: 'jam',
123
- in: { co_zURWEpvoH1e35FGHVxadgXKU8Ri_session_zgik2JjD86Qa: 'jam' }
124
- }
125
- subscribedStream[me.id]?.value?.[me.id]?.value?.[me.id]?.value jam
126
-
127
- ✓ src/tests/coStream.test.ts (10 tests) 205ms
128
- stdout | src/tests/coMap.test.ts > CoMap resolution > Subscription & auto-resolution
129
- subscribedMap.nested?.twiceNested?.taste undefined
130
- subscribedMap.nested?.twiceNested?.taste undefined
131
- subscribedMap.nested?.twiceNested?.taste sour
132
- subscribedMap.nested?.twiceNested?.taste sour
133
- subscribedMap.nested?.twiceNested?.taste sweet
134
- subscribedMap.nested?.twiceNested?.taste sweet
135
- subscribedMap.nested?.twiceNested?.taste salty
136
- subscribedMap.nested?.twiceNested?.taste umami
137
-
138
- ✓ src/tests/coMap.test.ts (20 tests) 257ms
139
-
140
- Test Files 5 passed (5)
141
- Tests 44 passed (44)
142
- Start at 18:35:37
143
- Duration 929ms (transform 419ms, setup 0ms, collect 2.29s, tests 799ms, environment 0ms, prepare 298ms)
144
-