cojson 0.13.27 → 0.13.29

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 (52) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/dist/coValueCore/coValueCore.d.ts +5 -3
  4. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  5. package/dist/coValueCore/coValueCore.js +95 -67
  6. package/dist/coValueCore/coValueCore.js.map +1 -1
  7. package/dist/coValues/group.d.ts.map +1 -1
  8. package/dist/coValues/group.js +1 -1
  9. package/dist/coValues/group.js.map +1 -1
  10. package/dist/crypto/PureJSCrypto.d.ts +1 -1
  11. package/dist/crypto/PureJSCrypto.js +3 -3
  12. package/dist/crypto/PureJSCrypto.js.map +1 -1
  13. package/dist/exports.d.ts +1 -0
  14. package/dist/exports.d.ts.map +1 -1
  15. package/dist/exports.js +3 -0
  16. package/dist/exports.js.map +1 -1
  17. package/dist/localNode.d.ts.map +1 -1
  18. package/dist/localNode.js +7 -1
  19. package/dist/localNode.js.map +1 -1
  20. package/dist/sync.d.ts +6 -6
  21. package/dist/sync.d.ts.map +1 -1
  22. package/dist/sync.js +100 -66
  23. package/dist/sync.js.map +1 -1
  24. package/dist/tests/SyncStateManager.test.js +1 -1
  25. package/dist/tests/SyncStateManager.test.js.map +1 -1
  26. package/dist/tests/crypto.test.js +5 -5
  27. package/dist/tests/crypto.test.js.map +1 -1
  28. package/dist/tests/group.addMember.test.d.ts +2 -0
  29. package/dist/tests/group.addMember.test.d.ts.map +1 -0
  30. package/dist/tests/group.addMember.test.js +272 -0
  31. package/dist/tests/group.addMember.test.js.map +1 -0
  32. package/dist/tests/sync.mesh.test.js +47 -3
  33. package/dist/tests/sync.mesh.test.js.map +1 -1
  34. package/dist/tests/sync.peerReconciliation.test.js +71 -2
  35. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  36. package/dist/tests/testUtils.d.ts +1 -1
  37. package/dist/tests/testUtils.d.ts.map +1 -1
  38. package/dist/tests/testUtils.js +1 -0
  39. package/dist/tests/testUtils.js.map +1 -1
  40. package/package.json +4 -4
  41. package/src/coValueCore/coValueCore.ts +126 -81
  42. package/src/coValues/group.ts +3 -6
  43. package/src/crypto/PureJSCrypto.ts +3 -3
  44. package/src/exports.ts +3 -0
  45. package/src/localNode.ts +11 -3
  46. package/src/sync.ts +117 -71
  47. package/src/tests/SyncStateManager.test.ts +1 -1
  48. package/src/tests/crypto.test.ts +5 -5
  49. package/src/tests/group.addMember.test.ts +432 -0
  50. package/src/tests/sync.mesh.test.ts +60 -2
  51. package/src/tests/sync.peerReconciliation.test.ts +90 -2
  52. package/src/tests/testUtils.ts +7 -6
@@ -0,0 +1,432 @@
1
+ import { beforeEach, describe, expect, test } from "vitest";
2
+ import { expectMap } from "../coValue.js";
3
+ import {
4
+ SyncMessagesLog,
5
+ loadCoValueOrFail,
6
+ setupTestAccount,
7
+ setupTestNode,
8
+ waitFor,
9
+ } from "./testUtils.js";
10
+
11
+ beforeEach(async () => {
12
+ SyncMessagesLog.clear();
13
+ setupTestNode({ isSyncServer: true });
14
+ });
15
+
16
+ describe("Group.addMember", () => {
17
+ test("an admin should be able to grant read access", async () => {
18
+ const admin = await setupTestAccount({
19
+ connected: true,
20
+ });
21
+
22
+ const reader = await setupTestAccount({
23
+ connected: true,
24
+ });
25
+
26
+ const group = admin.node.createGroup();
27
+ const person = group.createMap({
28
+ name: "John Doe",
29
+ });
30
+
31
+ const personOnReaderNode = await loadCoValueOrFail(reader.node, person.id);
32
+
33
+ expect(personOnReaderNode.get("name")).toEqual(undefined);
34
+
35
+ const readerOnAdminNode = await loadCoValueOrFail(
36
+ admin.node,
37
+ reader.accountID,
38
+ );
39
+ group.addMember(readerOnAdminNode, "reader");
40
+
41
+ expect(group.roleOf(reader.accountID)).toEqual("reader");
42
+
43
+ await waitFor(() => {
44
+ expect(
45
+ expectMap(personOnReaderNode.core.getCurrentContent()).get("name"),
46
+ ).toEqual("John Doe");
47
+ });
48
+ });
49
+
50
+ test("an admin should be able to grant write access", async () => {
51
+ const admin = await setupTestAccount({
52
+ connected: true,
53
+ });
54
+
55
+ const writer = await setupTestAccount({
56
+ connected: true,
57
+ });
58
+
59
+ const group = admin.node.createGroup();
60
+ const person = group.createMap({
61
+ name: "John Doe",
62
+ });
63
+
64
+ const personOnWriterNode = await loadCoValueOrFail(writer.node, person.id);
65
+
66
+ expect(personOnWriterNode.get("name")).toEqual(undefined);
67
+
68
+ const writerOnAdminNode = await loadCoValueOrFail(
69
+ admin.node,
70
+ writer.accountID,
71
+ );
72
+ group.addMember(writerOnAdminNode, "writer");
73
+ expect(group.roleOf(writer.accountID)).toEqual("writer");
74
+
75
+ await waitFor(() => {
76
+ expect(
77
+ expectMap(personOnWriterNode.core.getCurrentContent()).get("name"),
78
+ ).toEqual("John Doe");
79
+ });
80
+
81
+ personOnWriterNode.set("name", "Jane Doe");
82
+ expect(personOnWriterNode.get("name")).toEqual("Jane Doe");
83
+ });
84
+
85
+ test("an admin should be able to grant writeOnly access", async () => {
86
+ const admin = await setupTestAccount({
87
+ connected: true,
88
+ });
89
+
90
+ const writeOnlyUser = await setupTestAccount({
91
+ connected: true,
92
+ });
93
+
94
+ const group = admin.node.createGroup();
95
+ const person = group.createMap({
96
+ name: "John Doe",
97
+ });
98
+
99
+ const writeOnlyUserOnAdminNode = await loadCoValueOrFail(
100
+ admin.node,
101
+ writeOnlyUser.accountID,
102
+ );
103
+ group.addMember(writeOnlyUserOnAdminNode, "writeOnly");
104
+ expect(group.roleOf(writeOnlyUser.accountID)).toEqual("writeOnly");
105
+ const personOnWriteOnlyNode = await loadCoValueOrFail(
106
+ writeOnlyUser.node,
107
+ person.id,
108
+ );
109
+
110
+ // Should not be able to read
111
+ expect(personOnWriteOnlyNode.get("name")).toEqual(undefined);
112
+
113
+ // Should be able to write
114
+ personOnWriteOnlyNode.set("name", "Jane Doe");
115
+
116
+ expect(personOnWriteOnlyNode.get("name")).toEqual("Jane Doe");
117
+ });
118
+
119
+ test("an admin should be able to grant admin access", async () => {
120
+ const admin = await setupTestAccount({
121
+ connected: true,
122
+ });
123
+
124
+ const otherAdmin = await setupTestAccount({
125
+ connected: true,
126
+ });
127
+
128
+ const reader = await setupTestAccount({
129
+ connected: true,
130
+ });
131
+
132
+ const group = admin.node.createGroup();
133
+ const person = group.createMap({
134
+ name: "John Doe",
135
+ });
136
+
137
+ const otherAdminOnAdminNode = await loadCoValueOrFail(
138
+ admin.node,
139
+ otherAdmin.accountID,
140
+ );
141
+ group.addMember(otherAdminOnAdminNode, "admin");
142
+
143
+ const personOnReaderNode = await loadCoValueOrFail(reader.node, person.id);
144
+
145
+ expect(personOnReaderNode.get("name")).toEqual(undefined);
146
+
147
+ const readerOnOtherAdminNode = await loadCoValueOrFail(
148
+ otherAdmin.node,
149
+ reader.accountID,
150
+ );
151
+ group.addMember(readerOnOtherAdminNode, "reader");
152
+
153
+ await waitFor(() => {
154
+ expect(
155
+ expectMap(personOnReaderNode.core.getCurrentContent()).get("name"),
156
+ ).toEqual("John Doe");
157
+ });
158
+ });
159
+
160
+ test("an admin should be able downgrade a writer to reader", async () => {
161
+ const admin = await setupTestAccount({
162
+ connected: true,
163
+ });
164
+
165
+ const writer = await setupTestAccount({
166
+ connected: true,
167
+ });
168
+
169
+ const group = admin.node.createGroup();
170
+ const person = group.createMap({
171
+ name: "John Doe",
172
+ });
173
+
174
+ const writerOnAdminNode = await loadCoValueOrFail(
175
+ admin.node,
176
+ writer.accountID,
177
+ );
178
+ group.addMember(writerOnAdminNode, "writer");
179
+ group.addMember(writerOnAdminNode, "reader");
180
+
181
+ expect(group.roleOf(writer.accountID)).toEqual("reader");
182
+
183
+ // Verify writer can read and write
184
+ const personOnWriterNode = await loadCoValueOrFail(writer.node, person.id);
185
+
186
+ // Should not be able to write
187
+ personOnWriterNode.set("name", "Jane Doe");
188
+
189
+ expect(personOnWriterNode.get("name")).toEqual("John Doe");
190
+ });
191
+
192
+ test("an admin should be able downgrade a reader to writeOnly", async () => {
193
+ const admin = await setupTestAccount({
194
+ connected: true,
195
+ });
196
+
197
+ const reader = await setupTestAccount({
198
+ connected: true,
199
+ });
200
+
201
+ const group = admin.node.createGroup();
202
+
203
+ const readerOnAdminNode = await loadCoValueOrFail(
204
+ admin.node,
205
+ reader.accountID,
206
+ );
207
+ group.addMember(readerOnAdminNode, "reader");
208
+ group.addMember(readerOnAdminNode, "writeOnly");
209
+
210
+ expect(group.roleOf(reader.accountID)).toEqual("writeOnly");
211
+
212
+ const person = group.createMap({
213
+ name: "John Doe",
214
+ });
215
+
216
+ // Verify reader can read
217
+ const personOnReaderNode = await loadCoValueOrFail(reader.node, person.id);
218
+
219
+ expect(personOnReaderNode.get("name")).toEqual(undefined);
220
+ });
221
+
222
+ test("an admin should not be able downgrade an admin", async () => {
223
+ const admin = await setupTestAccount({
224
+ connected: true,
225
+ });
226
+
227
+ const otherAdmin = await setupTestAccount({
228
+ connected: true,
229
+ });
230
+
231
+ const group = admin.node.createGroup();
232
+ const person = group.createMap({
233
+ name: "John Doe",
234
+ });
235
+
236
+ const otherAdminOnAdminNode = await loadCoValueOrFail(
237
+ admin.node,
238
+ otherAdmin.accountID,
239
+ );
240
+ group.addMember(otherAdminOnAdminNode, "admin");
241
+
242
+ // Try to downgrade other admin
243
+ try {
244
+ group.addMember(otherAdminOnAdminNode, "writer");
245
+ } catch (e) {
246
+ expect(e).toBeDefined();
247
+ }
248
+
249
+ expect(group.roleOf(otherAdmin.accountID)).toEqual("admin");
250
+
251
+ // Verify other admin still has admin access by adding a new member
252
+ const reader = await setupTestAccount({
253
+ connected: true,
254
+ });
255
+
256
+ const readerOnOtherAdminNode = await loadCoValueOrFail(
257
+ otherAdmin.node,
258
+ reader.accountID,
259
+ );
260
+ group.addMember(readerOnOtherAdminNode, "reader");
261
+
262
+ const personOnReaderNode = await loadCoValueOrFail(reader.node, person.id);
263
+
264
+ await waitFor(() => {
265
+ expect(
266
+ expectMap(personOnReaderNode.core.getCurrentContent()).get("name"),
267
+ ).toEqual("John Doe");
268
+ });
269
+ });
270
+
271
+ test("an admin should be able downgrade themselves", async () => {
272
+ const admin = await setupTestAccount({
273
+ connected: true,
274
+ });
275
+
276
+ const group = admin.node.createGroup();
277
+
278
+ const account = await loadCoValueOrFail(admin.node, admin.accountID);
279
+
280
+ // Downgrade self to writer
281
+ group.addMember(account, "writer");
282
+ expect(group.roleOf(admin.accountID)).toEqual("writer");
283
+ });
284
+
285
+ test("an admin should be able downgrade a writeOnly to reader", async () => {
286
+ const admin = await setupTestAccount({
287
+ connected: true,
288
+ });
289
+
290
+ const writeOnlyUser = await setupTestAccount({
291
+ connected: true,
292
+ });
293
+
294
+ const group = admin.node.createGroup();
295
+ const person = group.createMap({
296
+ name: "John Doe",
297
+ });
298
+
299
+ const writeOnlyUserOnAdminNode = await loadCoValueOrFail(
300
+ admin.node,
301
+ writeOnlyUser.accountID,
302
+ );
303
+ group.addMember(writeOnlyUserOnAdminNode, "writeOnly");
304
+ group.addMember(writeOnlyUserOnAdminNode, "reader");
305
+
306
+ expect(group.roleOf(writeOnlyUser.accountID)).toEqual("reader");
307
+
308
+ const personOnWriteOnlyNode = await loadCoValueOrFail(
309
+ writeOnlyUser.node,
310
+ person.id,
311
+ );
312
+
313
+ expect(personOnWriteOnlyNode.get("name")).toEqual("John Doe");
314
+ });
315
+
316
+ test("a reader should not be able to add a member", async () => {
317
+ const admin = await setupTestAccount({
318
+ connected: true,
319
+ });
320
+
321
+ const reader = await setupTestAccount({
322
+ connected: true,
323
+ });
324
+
325
+ const newUser = await setupTestAccount({
326
+ connected: true,
327
+ });
328
+
329
+ const group = admin.node.createGroup();
330
+ const readerOnAdminNode = await loadCoValueOrFail(
331
+ admin.node,
332
+ reader.accountID,
333
+ );
334
+ group.addMember(readerOnAdminNode, "reader");
335
+
336
+ const newUserOnReaderNode = await loadCoValueOrFail(
337
+ reader.node,
338
+ newUser.accountID,
339
+ );
340
+
341
+ const groupOnReaderNode = await loadCoValueOrFail(reader.node, group.id);
342
+
343
+ // Try to add member as reader
344
+ try {
345
+ groupOnReaderNode.addMember(newUserOnReaderNode, "reader");
346
+ throw new Error("Should not be able to add member as reader");
347
+ } catch (e) {
348
+ expect(e).toBeDefined();
349
+ }
350
+
351
+ expect(groupOnReaderNode.roleOf(newUser.accountID)).toBeUndefined();
352
+ });
353
+
354
+ test("a writer should not be able to add a member", async () => {
355
+ const admin = await setupTestAccount({
356
+ connected: true,
357
+ });
358
+
359
+ const writer = await setupTestAccount({
360
+ connected: true,
361
+ });
362
+
363
+ const newUser = await setupTestAccount({
364
+ connected: true,
365
+ });
366
+
367
+ const group = admin.node.createGroup();
368
+ const writerOnAdminNode = await loadCoValueOrFail(
369
+ admin.node,
370
+ writer.accountID,
371
+ );
372
+ group.addMember(writerOnAdminNode, "writer");
373
+
374
+ const newUserOnWriterNode = await loadCoValueOrFail(
375
+ writer.node,
376
+ newUser.accountID,
377
+ );
378
+
379
+ const groupOnWriterNode = await loadCoValueOrFail(writer.node, group.id);
380
+
381
+ // Try to add member as writer
382
+ try {
383
+ groupOnWriterNode.addMember(newUserOnWriterNode, "reader");
384
+ throw new Error("Should not be able to add member as writer");
385
+ } catch (e) {
386
+ expect(e).toBeDefined();
387
+ }
388
+
389
+ expect(groupOnWriterNode.roleOf(newUser.accountID)).toBeUndefined();
390
+ });
391
+
392
+ test("a writeOnly should not be able to add a member", async () => {
393
+ const admin = await setupTestAccount({
394
+ connected: true,
395
+ });
396
+
397
+ const writeOnlyUser = await setupTestAccount({
398
+ connected: true,
399
+ });
400
+
401
+ const newUser = await setupTestAccount({
402
+ connected: true,
403
+ });
404
+
405
+ const group = admin.node.createGroup();
406
+ const writeOnlyUserOnAdminNode = await loadCoValueOrFail(
407
+ admin.node,
408
+ writeOnlyUser.accountID,
409
+ );
410
+ group.addMember(writeOnlyUserOnAdminNode, "writeOnly");
411
+
412
+ const newUserOnWriteOnlyNode = await loadCoValueOrFail(
413
+ writeOnlyUser.node,
414
+ newUser.accountID,
415
+ );
416
+
417
+ const groupOnWriteOnlyNode = await loadCoValueOrFail(
418
+ writeOnlyUser.node,
419
+ group.id,
420
+ );
421
+
422
+ // Try to add member as writeOnly user
423
+ try {
424
+ groupOnWriteOnlyNode.addMember(newUserOnWriteOnlyNode, "reader");
425
+ throw new Error("Should not be able to add member as writeOnly user");
426
+ } catch (e) {
427
+ expect(e).toBeDefined();
428
+ }
429
+
430
+ expect(groupOnWriteOnlyNode.roleOf(newUser.accountID)).toBeUndefined();
431
+ });
432
+ });
@@ -4,6 +4,7 @@ import { expectMap } from "../coValue";
4
4
  import {
5
5
  SyncMessagesLog,
6
6
  blockMessageTypeOnOutgoingPeer,
7
+ connectedPeersWithMessagesTracking,
7
8
  loadCoValueOrFail,
8
9
  setupTestNode,
9
10
  waitFor,
@@ -76,8 +77,8 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
76
77
  "core -> storage | CONTENT Group header: true new: After: 0 New: 3",
77
78
  "storage -> core | KNOWN Group sessions: header/3",
78
79
  "core -> storage | CONTENT Map header: true new: After: 0 New: 1",
79
- "storage -> core | KNOWN Map sessions: header/1",
80
80
  "client -> edge-italy | LOAD Map sessions: empty",
81
+ "storage -> core | KNOWN Map sessions: header/1",
81
82
  "edge-italy -> core | LOAD Map sessions: empty",
82
83
  "core -> edge-italy | CONTENT Group header: true new: After: 0 New: 3",
83
84
  "edge-italy -> core | KNOWN Group sessions: header/3",
@@ -138,8 +139,8 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
138
139
  "core -> storage | CONTENT Group header: true new: After: 0 New: 5",
139
140
  "storage -> core | KNOWN Group sessions: header/5",
140
141
  "core -> storage | CONTENT Map header: true new: After: 0 New: 1",
141
- "storage -> core | KNOWN Map sessions: header/1",
142
142
  "client -> edge-italy | LOAD Map sessions: empty",
143
+ "storage -> core | KNOWN Map sessions: header/1",
143
144
  "edge-italy -> core | LOAD Map sessions: empty",
144
145
  "core -> edge-italy | CONTENT ParentGroup header: true new: After: 0 New: 6",
145
146
  "edge-italy -> core | KNOWN ParentGroup sessions: header/6",
@@ -355,6 +356,7 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
355
356
  syncServer: storage.node,
356
357
  });
357
358
 
359
+ storagePeer.role = "storage";
358
360
  storagePeer.priority = 100;
359
361
 
360
362
  const group = coreServer.node.createGroup();
@@ -402,4 +404,60 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
402
404
 
403
405
  expect(mapOnClient.get("hello")).toEqual("world");
404
406
  });
407
+
408
+ test("a stuck server peer should not block the load from other server peers", async () => {
409
+ const client = setupTestNode();
410
+ const coreServer = setupTestNode({
411
+ isSyncServer: true,
412
+ });
413
+
414
+ const anotherServer = setupTestNode({});
415
+
416
+ const { peer: peerToCoreServer } = client.connectToSyncServer({
417
+ syncServerName: "core",
418
+ syncServer: coreServer.node,
419
+ });
420
+
421
+ const { peer1, peer2 } = connectedPeersWithMessagesTracking({
422
+ peer1: {
423
+ id: anotherServer.node.getCurrentAgent().id,
424
+ role: "server",
425
+ name: "another-server",
426
+ },
427
+ peer2: {
428
+ id: client.node.getCurrentAgent().id,
429
+ role: "client",
430
+ name: "client",
431
+ },
432
+ });
433
+
434
+ blockMessageTypeOnOutgoingPeer(peerToCoreServer, "load");
435
+
436
+ client.node.syncManager.addPeer(peer1);
437
+ anotherServer.node.syncManager.addPeer(peer2);
438
+
439
+ const group = anotherServer.node.createGroup();
440
+ const map = group.createMap();
441
+
442
+ map.set("hello", "world", "trusting");
443
+
444
+ const mapOnClient = await loadCoValueOrFail(client.node, map.id);
445
+
446
+ expect(
447
+ SyncMessagesLog.getMessages({
448
+ Group: group.core,
449
+ Map: map.core,
450
+ }),
451
+ ).toMatchInlineSnapshot(`
452
+ [
453
+ "client -> another-server | LOAD Map sessions: empty",
454
+ "another-server -> client | CONTENT Group header: true new: After: 0 New: 3",
455
+ "client -> another-server | KNOWN Group sessions: header/3",
456
+ "another-server -> client | CONTENT Map header: true new: After: 0 New: 1",
457
+ "client -> another-server | KNOWN Map sessions: header/1",
458
+ ]
459
+ `);
460
+
461
+ expect(mapOnClient.get("hello")).toEqual("world");
462
+ });
405
463
  });
@@ -1,7 +1,13 @@
1
1
  import { assert, beforeEach, describe, expect, test } from "vitest";
2
2
  import { expectMap } from "../coValue";
3
3
  import { WasmCrypto } from "../crypto/WasmCrypto";
4
- import { SyncMessagesLog, setupTestNode, waitFor } from "./testUtils";
4
+ import {
5
+ SyncMessagesLog,
6
+ loadCoValueOrFail,
7
+ setupTestAccount,
8
+ setupTestNode,
9
+ waitFor,
10
+ } from "./testUtils";
5
11
 
6
12
  let jazzCloud = setupTestNode({ isSyncServer: true });
7
13
 
@@ -146,7 +152,9 @@ describe("peer reconciliation", () => {
146
152
  });
147
153
 
148
154
  test("correctly handle server restarts in the middle of a sync", async () => {
149
- const client = setupTestNode();
155
+ const client = setupTestNode({
156
+ connected: true,
157
+ });
150
158
 
151
159
  const group = client.node.createGroup();
152
160
  const map = group.createMap();
@@ -182,10 +190,90 @@ describe("peer reconciliation", () => {
182
190
  "server -> client | KNOWN Group sessions: empty",
183
191
  "client -> server | LOAD Map sessions: header/2",
184
192
  "server -> client | KNOWN Map sessions: empty",
193
+ "client -> server | CONTENT Map header: false new: After: 1 New: 1",
194
+ "server -> client | KNOWN CORRECTION Map sessions: empty",
195
+ "client -> server | CONTENT Map header: true new: After: 0 New: 2",
196
+ "server -> client | LOAD Group sessions: empty",
185
197
  "client -> server | CONTENT Group header: true new: After: 0 New: 3",
198
+ "server -> client | KNOWN CORRECTION Map sessions: empty",
199
+ "client -> server | CONTENT Map header: true new: After: 0 New: 2",
200
+ "server -> client | KNOWN Group sessions: header/3",
201
+ "server -> client | KNOWN Map sessions: header/2",
202
+ "client -> server | LOAD Group sessions: header/3",
186
203
  "server -> client | KNOWN Group sessions: header/3",
204
+ "client -> server | LOAD Map sessions: header/2",
205
+ "server -> client | KNOWN Map sessions: header/2",
206
+ ]
207
+ `);
208
+ });
209
+
210
+ test("correctly handle server restarts in the middle of a sync (2 - account)", async () => {
211
+ const client = await setupTestAccount({
212
+ connected: true,
213
+ });
214
+
215
+ const group = client.node.createGroup();
216
+ const map = group.createMap();
217
+
218
+ map.set("hello", "world", "trusting");
219
+
220
+ await map.core.waitForSync();
221
+
222
+ jazzCloud.restart();
223
+ SyncMessagesLog.clear();
224
+ client.connectToSyncServer();
225
+
226
+ map.set("hello", "updated", "trusting");
227
+
228
+ await new Promise((resolve) => setTimeout(resolve, 0));
229
+
230
+ client.connectToSyncServer();
231
+
232
+ await waitFor(() => {
233
+ const mapOnSyncServer = jazzCloud.node.getCoValue(map.id);
234
+
235
+ expect(mapOnSyncServer.loadingState).toBe("available");
236
+ });
237
+
238
+ expect(
239
+ SyncMessagesLog.getMessages({
240
+ Account: client.node.expectCurrentAccount("client account").core,
241
+ Profile: client.node.expectProfileLoaded(client.accountID).core,
242
+ ProfileGroup: client.node.expectProfileLoaded(client.accountID).group
243
+ .core,
244
+ Group: group.core,
245
+ Map: map.core,
246
+ }),
247
+ ).toMatchInlineSnapshot(`
248
+ [
249
+ "client -> server | LOAD Account sessions: header/4",
250
+ "server -> client | KNOWN Account sessions: empty",
251
+ "client -> server | LOAD ProfileGroup sessions: header/5",
252
+ "server -> client | KNOWN ProfileGroup sessions: empty",
253
+ "client -> server | LOAD Profile sessions: header/1",
254
+ "server -> client | KNOWN Profile sessions: empty",
255
+ "client -> server | LOAD Group sessions: header/3",
256
+ "server -> client | KNOWN Group sessions: empty",
257
+ "client -> server | LOAD Map sessions: header/2",
258
+ "server -> client | KNOWN Map sessions: empty",
259
+ "client -> server | CONTENT Map header: false new: After: 1 New: 1",
260
+ "server -> client | KNOWN CORRECTION Map sessions: empty",
187
261
  "client -> server | CONTENT Map header: true new: After: 0 New: 2",
262
+ "server -> client | LOAD Account sessions: empty",
263
+ "client -> server | CONTENT Account header: true new: After: 0 New: 4",
264
+ "server -> client | LOAD Group sessions: empty",
265
+ "client -> server | CONTENT Group header: true new: After: 0 New: 3",
266
+ "server -> client | KNOWN CORRECTION Map sessions: empty",
267
+ "client -> server | CONTENT Map header: true new: After: 0 New: 2",
268
+ "server -> client | KNOWN Account sessions: header/4",
269
+ "server -> client | KNOWN Group sessions: header/3",
188
270
  "server -> client | KNOWN Map sessions: header/2",
271
+ "client -> server | LOAD Account sessions: header/4",
272
+ "server -> client | KNOWN Account sessions: header/4",
273
+ "client -> server | LOAD ProfileGroup sessions: header/5",
274
+ "server -> client | KNOWN ProfileGroup sessions: empty",
275
+ "client -> server | LOAD Profile sessions: header/1",
276
+ "server -> client | KNOWN Profile sessions: empty",
189
277
  "client -> server | LOAD Group sessions: header/3",
190
278
  "server -> client | KNOWN Group sessions: header/3",
191
279
  "client -> server | LOAD Map sessions: header/2",
@@ -8,12 +8,12 @@ import {
8
8
  import { expect, onTestFinished, vi } from "vitest";
9
9
  import { ControlledAccount, ControlledAgent } from "../coValues/account.js";
10
10
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
11
- import type {
12
- AgentSecret,
13
- CoID,
14
- CoValueCore,
15
- RawAccount,
16
- RawCoValue,
11
+ import {
12
+ type AgentSecret,
13
+ type CoID,
14
+ type CoValueCore,
15
+ type RawAccount,
16
+ type RawCoValue,
17
17
  } from "../exports.js";
18
18
  import type { SessionID } from "../ids.js";
19
19
  import { LocalNode } from "../localNode.js";
@@ -463,6 +463,7 @@ export function createMockStoragePeer(opts: {
463
463
  },
464
464
  });
465
465
 
466
+ peer1.role = "storage";
466
467
  peer1.priority = 100;
467
468
 
468
469
  storage.syncManager.addPeer(peer2);