cojson 0.16.3 → 0.16.5

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 (138) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +20 -0
  3. package/dist/coValue.d.ts +1 -1
  4. package/dist/coValue.d.ts.map +1 -1
  5. package/dist/coValue.js.map +1 -1
  6. package/dist/coValueContentMessage.d.ts +10 -0
  7. package/dist/coValueContentMessage.d.ts.map +1 -0
  8. package/dist/coValueContentMessage.js +46 -0
  9. package/dist/coValueContentMessage.js.map +1 -0
  10. package/dist/coValueCore/coValueCore.d.ts +6 -10
  11. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  12. package/dist/coValueCore/coValueCore.js +20 -125
  13. package/dist/coValueCore/coValueCore.js.map +1 -1
  14. package/dist/coValueCore/verifiedState.d.ts +1 -0
  15. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  16. package/dist/coValueCore/verifiedState.js +14 -27
  17. package/dist/coValueCore/verifiedState.js.map +1 -1
  18. package/dist/coValues/group.d.ts +18 -10
  19. package/dist/coValues/group.d.ts.map +1 -1
  20. package/dist/coValues/group.js +237 -67
  21. package/dist/coValues/group.js.map +1 -1
  22. package/dist/ids.d.ts +3 -3
  23. package/dist/ids.d.ts.map +1 -1
  24. package/dist/ids.js.map +1 -1
  25. package/dist/localNode.d.ts +11 -6
  26. package/dist/localNode.d.ts.map +1 -1
  27. package/dist/localNode.js +7 -2
  28. package/dist/localNode.js.map +1 -1
  29. package/dist/queue/LocalTransactionsSyncQueue.d.ts +24 -0
  30. package/dist/queue/LocalTransactionsSyncQueue.d.ts.map +1 -0
  31. package/dist/queue/LocalTransactionsSyncQueue.js +55 -0
  32. package/dist/queue/LocalTransactionsSyncQueue.js.map +1 -0
  33. package/dist/queue/StoreQueue.d.ts +9 -6
  34. package/dist/queue/StoreQueue.d.ts.map +1 -1
  35. package/dist/queue/StoreQueue.js +10 -2
  36. package/dist/queue/StoreQueue.js.map +1 -1
  37. package/dist/storage/storageAsync.d.ts +11 -3
  38. package/dist/storage/storageAsync.d.ts.map +1 -1
  39. package/dist/storage/storageAsync.js +59 -46
  40. package/dist/storage/storageAsync.js.map +1 -1
  41. package/dist/storage/storageSync.d.ts +9 -3
  42. package/dist/storage/storageSync.d.ts.map +1 -1
  43. package/dist/storage/storageSync.js +48 -35
  44. package/dist/storage/storageSync.js.map +1 -1
  45. package/dist/storage/syncUtils.d.ts +2 -1
  46. package/dist/storage/syncUtils.d.ts.map +1 -1
  47. package/dist/storage/syncUtils.js +4 -0
  48. package/dist/storage/syncUtils.js.map +1 -1
  49. package/dist/storage/types.d.ts +3 -2
  50. package/dist/storage/types.d.ts.map +1 -1
  51. package/dist/sync.d.ts +6 -6
  52. package/dist/sync.d.ts.map +1 -1
  53. package/dist/sync.js +33 -56
  54. package/dist/sync.js.map +1 -1
  55. package/dist/tests/StorageApiAsync.test.d.ts +2 -0
  56. package/dist/tests/StorageApiAsync.test.d.ts.map +1 -0
  57. package/dist/tests/StorageApiAsync.test.js +574 -0
  58. package/dist/tests/StorageApiAsync.test.js.map +1 -0
  59. package/dist/tests/StorageApiSync.test.d.ts +2 -0
  60. package/dist/tests/StorageApiSync.test.d.ts.map +1 -0
  61. package/dist/tests/StorageApiSync.test.js +426 -0
  62. package/dist/tests/StorageApiSync.test.js.map +1 -0
  63. package/dist/tests/StoreQueue.test.js +9 -21
  64. package/dist/tests/StoreQueue.test.js.map +1 -1
  65. package/dist/tests/SyncStateManager.test.js +18 -8
  66. package/dist/tests/SyncStateManager.test.js.map +1 -1
  67. package/dist/tests/group.inheritance.test.js +274 -2
  68. package/dist/tests/group.inheritance.test.js.map +1 -1
  69. package/dist/tests/group.removeMember.test.js +152 -1
  70. package/dist/tests/group.removeMember.test.js.map +1 -1
  71. package/dist/tests/group.roleOf.test.js +2 -2
  72. package/dist/tests/group.roleOf.test.js.map +1 -1
  73. package/dist/tests/group.test.js +81 -3
  74. package/dist/tests/group.test.js.map +1 -1
  75. package/dist/tests/sync.auth.test.js +22 -10
  76. package/dist/tests/sync.auth.test.js.map +1 -1
  77. package/dist/tests/sync.load.test.js +30 -25
  78. package/dist/tests/sync.load.test.js.map +1 -1
  79. package/dist/tests/sync.mesh.test.js +12 -6
  80. package/dist/tests/sync.mesh.test.js.map +1 -1
  81. package/dist/tests/sync.peerReconciliation.test.js +6 -4
  82. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  83. package/dist/tests/sync.storage.test.js +8 -14
  84. package/dist/tests/sync.storage.test.js.map +1 -1
  85. package/dist/tests/sync.storageAsync.test.js +31 -14
  86. package/dist/tests/sync.storageAsync.test.js.map +1 -1
  87. package/dist/tests/sync.test.js +5 -9
  88. package/dist/tests/sync.test.js.map +1 -1
  89. package/dist/tests/sync.upload.test.js +31 -1
  90. package/dist/tests/sync.upload.test.js.map +1 -1
  91. package/dist/tests/testStorage.d.ts +2 -3
  92. package/dist/tests/testStorage.d.ts.map +1 -1
  93. package/dist/tests/testStorage.js +16 -8
  94. package/dist/tests/testStorage.js.map +1 -1
  95. package/dist/tests/testUtils.d.ts +4 -0
  96. package/dist/tests/testUtils.d.ts.map +1 -1
  97. package/dist/tests/testUtils.js +22 -4
  98. package/dist/tests/testUtils.js.map +1 -1
  99. package/dist/typeUtils/accountOrAgentIDfromSessionID.d.ts +2 -2
  100. package/dist/typeUtils/accountOrAgentIDfromSessionID.d.ts.map +1 -1
  101. package/dist/typeUtils/expectGroup.d.ts.map +1 -1
  102. package/dist/typeUtils/expectGroup.js +6 -5
  103. package/dist/typeUtils/expectGroup.js.map +1 -1
  104. package/package.json +1 -1
  105. package/src/coValue.ts +1 -4
  106. package/src/coValueContentMessage.ts +73 -0
  107. package/src/coValueCore/coValueCore.ts +36 -192
  108. package/src/coValueCore/verifiedState.ts +28 -35
  109. package/src/coValues/group.ts +329 -99
  110. package/src/ids.ts +3 -3
  111. package/src/localNode.ts +15 -10
  112. package/src/queue/LocalTransactionsSyncQueue.ts +96 -0
  113. package/src/queue/StoreQueue.ts +22 -12
  114. package/src/storage/storageAsync.ts +78 -56
  115. package/src/storage/storageSync.ts +66 -45
  116. package/src/storage/syncUtils.ts +9 -1
  117. package/src/storage/types.ts +6 -5
  118. package/src/sync.ts +47 -67
  119. package/src/tests/StorageApiAsync.test.ts +829 -0
  120. package/src/tests/StorageApiSync.test.ts +628 -0
  121. package/src/tests/StoreQueue.test.ts +10 -24
  122. package/src/tests/SyncStateManager.test.ts +22 -21
  123. package/src/tests/group.inheritance.test.ts +415 -1
  124. package/src/tests/group.removeMember.test.ts +244 -1
  125. package/src/tests/group.roleOf.test.ts +2 -2
  126. package/src/tests/group.test.ts +105 -5
  127. package/src/tests/sync.auth.test.ts +22 -10
  128. package/src/tests/sync.load.test.ts +32 -26
  129. package/src/tests/sync.mesh.test.ts +12 -6
  130. package/src/tests/sync.peerReconciliation.test.ts +6 -4
  131. package/src/tests/sync.storage.test.ts +8 -14
  132. package/src/tests/sync.storageAsync.test.ts +39 -14
  133. package/src/tests/sync.test.ts +6 -14
  134. package/src/tests/sync.upload.test.ts +38 -1
  135. package/src/tests/testStorage.ts +19 -13
  136. package/src/tests/testUtils.ts +29 -5
  137. package/src/typeUtils/accountOrAgentIDfromSessionID.ts +2 -2
  138. package/src/typeUtils/expectGroup.ts +8 -5
@@ -142,19 +142,26 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
142
142
  }),
143
143
  ).toMatchInlineSnapshot(`
144
144
  [
145
- "edge-france -> storage | CONTENT Group header: true new: After: 0 New: 5",
146
- "edge-france -> core | CONTENT ParentGroup header: true new: After: 0 New: 6",
147
- "edge-france -> core | CONTENT Group header: true new: After: 0 New: 5",
145
+ "edge-france -> storage | CONTENT Group header: true new: After: 0 New: 3",
146
+ "edge-france -> core | CONTENT Group header: true new: After: 0 New: 3",
148
147
  "edge-france -> storage | CONTENT ParentGroup header: true new: After: 0 New: 6",
148
+ "edge-france -> core | CONTENT ParentGroup header: true new: After: 0 New: 6",
149
+ "edge-france -> storage | CONTENT Group header: false new: After: 3 New: 2",
150
+ "edge-france -> core | CONTENT Group header: false new: After: 3 New: 2",
149
151
  "edge-france -> storage | CONTENT Map header: true new: After: 0 New: 1",
150
152
  "edge-france -> core | CONTENT Map header: true new: After: 0 New: 1",
153
+ "core -> edge-france | KNOWN Group sessions: header/3",
154
+ "core -> storage | CONTENT Group header: true new: After: 0 New: 3",
151
155
  "core -> edge-france | KNOWN ParentGroup sessions: header/6",
152
156
  "core -> storage | CONTENT ParentGroup header: true new: After: 0 New: 6",
153
157
  "core -> edge-france | KNOWN Group sessions: header/5",
154
- "core -> storage | CONTENT Group header: true new: After: 0 New: 5",
158
+ "core -> storage | CONTENT Group header: false new: After: 3 New: 2",
155
159
  "core -> edge-france | KNOWN Map sessions: header/1",
156
160
  "core -> storage | CONTENT Map header: true new: After: 0 New: 1",
161
+ "edge-france -> core | CONTENT ParentGroup header: true new: ",
157
162
  "client -> edge-italy | LOAD Map sessions: empty",
163
+ "core -> edge-france | KNOWN ParentGroup sessions: header/6",
164
+ "core -> storage | CONTENT ParentGroup header: true new: ",
158
165
  "edge-italy -> storage | LOAD Map sessions: empty",
159
166
  "storage -> edge-italy | KNOWN Map sessions: empty",
160
167
  "edge-italy -> core | LOAD Map sessions: empty",
@@ -509,8 +516,7 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
509
516
  ).toMatchInlineSnapshot(`
510
517
  [
511
518
  "edge -> storage | CONTENT Group header: true new: After: 0 New: 5",
512
- "edge -> storage | CONTENT Map header: true new: expectContentUntil: header/200",
513
- "edge -> storage | CONTENT Map header: false new: After: 0 New: 73",
519
+ "edge -> storage | CONTENT Map header: true new: After: 0 New: 73",
514
520
  "edge -> storage | CONTENT Map header: false new: After: 73 New: 73",
515
521
  "edge -> storage | CONTENT Map header: false new: After: 146 New: 54",
516
522
  ]
@@ -47,6 +47,8 @@ describe("peer reconciliation", () => {
47
47
  "server -> client | KNOWN Map sessions: empty",
48
48
  "server -> client | KNOWN Group sessions: header/3",
49
49
  "server -> client | KNOWN Map sessions: header/1",
50
+ "client -> server | CONTENT Group header: true new: ",
51
+ "client -> server | CONTENT Map header: true new: ",
50
52
  ]
51
53
  `);
52
54
  });
@@ -203,7 +205,7 @@ describe("peer reconciliation", () => {
203
205
  "server -> client | KNOWN CORRECTION Map sessions: empty",
204
206
  "client -> server | CONTENT Map header: true new: After: 0 New: 2",
205
207
  "server -> client | LOAD Group sessions: empty",
206
- "server -> client | KNOWN Map sessions: empty",
208
+ "server -> client | KNOWN Map sessions: header/2",
207
209
  "client -> server | CONTENT Group header: true new: After: 0 New: 3",
208
210
  "server -> client | KNOWN Group sessions: header/3",
209
211
  ]
@@ -276,8 +278,8 @@ describe("peer reconciliation", () => {
276
278
  "client -> server | CONTENT Profile header: true new: After: 0 New: 1",
277
279
  "client -> server | CONTENT Map header: false new: After: 1 New: 1",
278
280
  "server -> client | LOAD Account sessions: empty",
279
- "server -> client | KNOWN ProfileGroup sessions: empty",
280
- "server -> client | KNOWN Profile sessions: empty",
281
+ "server -> client | KNOWN ProfileGroup sessions: header/0",
282
+ "server -> client | KNOWN Profile sessions: header/0",
281
283
  "server -> client | KNOWN CORRECTION Map sessions: empty",
282
284
  "client -> server | CONTENT Account header: true new: After: 0 New: 4",
283
285
  "client -> server | CONTENT Map header: true new: After: 0 New: 2",
@@ -285,7 +287,7 @@ describe("peer reconciliation", () => {
285
287
  "server -> client | KNOWN ProfileGroup sessions: header/5",
286
288
  "server -> client | KNOWN Profile sessions: header/1",
287
289
  "server -> client | LOAD Group sessions: empty",
288
- "server -> client | KNOWN Map sessions: empty",
290
+ "server -> client | KNOWN Map sessions: header/2",
289
291
  "client -> server | CONTENT Group header: true new: After: 0 New: 3",
290
292
  "server -> client | KNOWN Group sessions: header/3",
291
293
  ]
@@ -181,13 +181,11 @@ describe("client with storage syncs with server", () => {
181
181
  [
182
182
  "client -> server | LOAD Group sessions: header/3",
183
183
  "client -> server | LOAD Map sessions: header/1",
184
- "server -> client | CONTENT Group header: true new: After: 0 New: 3",
185
- "server -> client | CONTENT Map header: true new: After: 0 New: 2",
186
184
  "server -> client | CONTENT Map header: false new: After: 1 New: 1",
187
- "client -> server | KNOWN Group sessions: header/3",
188
- "client -> storage | CONTENT Group header: true new: After: 0 New: 3",
185
+ "server -> client | KNOWN Group sessions: header/3",
186
+ "server -> client | CONTENT Map header: false new: After: 1 New: 1",
189
187
  "client -> server | KNOWN Map sessions: header/2",
190
- "client -> storage | CONTENT Map header: true new: After: 0 New: 2",
188
+ "client -> storage | CONTENT Map header: false new: After: 1 New: 1",
191
189
  "client -> server | KNOWN Map sessions: header/2",
192
190
  "client -> storage | CONTENT Map header: false new: After: 1 New: 1",
193
191
  ]
@@ -291,20 +289,16 @@ describe("client syncs with a server with storage", () => {
291
289
  [
292
290
  "client -> storage | CONTENT Group header: true new: After: 0 New: 5",
293
291
  "client -> server | CONTENT Group header: true new: After: 0 New: 5",
294
- "client -> storage | CONTENT Map header: true new: expectContentUntil: header/200",
295
- "client -> storage | CONTENT Map header: false new: After: 0 New: 73",
292
+ "client -> storage | CONTENT Map header: true new: After: 0 New: 73",
293
+ "client -> server | CONTENT Map header: true new: After: 0 New: 73",
296
294
  "client -> storage | CONTENT Map header: false new: After: 73 New: 73",
297
- "client -> storage | CONTENT Map header: false new: After: 146 New: 54",
298
- "client -> server | CONTENT Map header: true new: expectContentUntil: header/200",
299
- "client -> server | CONTENT Map header: false new: After: 0 New: 73",
300
295
  "client -> server | CONTENT Map header: false new: After: 73 New: 73",
296
+ "client -> storage | CONTENT Map header: false new: After: 146 New: 54",
301
297
  "client -> server | CONTENT Map header: false new: After: 146 New: 54",
302
298
  "server -> client | KNOWN Group sessions: header/5",
303
299
  "server -> storage | CONTENT Group header: true new: After: 0 New: 5",
304
- "server -> client | KNOWN Map sessions: header/0",
305
- "server -> storage | CONTENT Map header: true new: expectContentUntil: header/200",
306
300
  "server -> client | KNOWN Map sessions: header/73",
307
- "server -> storage | CONTENT Map header: false new: After: 0 New: 73",
301
+ "server -> storage | CONTENT Map header: true new: After: 0 New: 73",
308
302
  "server -> client | KNOWN Map sessions: header/146",
309
303
  "server -> storage | CONTENT Map header: false new: After: 73 New: 73",
310
304
  "server -> client | KNOWN Map sessions: header/200",
@@ -410,7 +404,7 @@ describe("client syncs with a server with storage", () => {
410
404
 
411
405
  const correctionSpy = vi.fn();
412
406
 
413
- client.node.storage?.store(newContentChunks.slice(1, 2), correctionSpy);
407
+ client.node.storage?.store(newContentChunks[1]!, correctionSpy);
414
408
 
415
409
  expect(correctionSpy).not.toHaveBeenCalled();
416
410
 
@@ -16,6 +16,7 @@ describe("client with storage syncs with server", () => {
16
16
  let jazzCloud: ReturnType<typeof setupTestNode>;
17
17
 
18
18
  beforeEach(async () => {
19
+ vi.resetAllMocks();
19
20
  SyncMessagesLog.clear();
20
21
  jazzCloud = setupTestNode({
21
22
  isSyncServer: true,
@@ -174,18 +175,46 @@ describe("client with storage syncs with server", () => {
174
175
  [
175
176
  "client -> server | LOAD Group sessions: header/3",
176
177
  "client -> server | LOAD Map sessions: header/1",
177
- "server -> client | CONTENT Group header: true new: After: 0 New: 3",
178
- "server -> client | CONTENT Map header: true new: After: 0 New: 2",
179
178
  "server -> client | CONTENT Map header: false new: After: 1 New: 1",
180
- "client -> server | KNOWN Group sessions: header/3",
181
- "client -> storage | CONTENT Group header: true new: After: 0 New: 3",
179
+ "server -> client | KNOWN Group sessions: header/3",
180
+ "server -> client | CONTENT Map header: false new: After: 1 New: 1",
182
181
  "client -> server | KNOWN Map sessions: header/2",
183
- "client -> storage | CONTENT Map header: true new: After: 0 New: 2",
182
+ "client -> storage | CONTENT Map header: false new: After: 1 New: 1",
184
183
  "client -> server | KNOWN Map sessions: header/2",
185
184
  "client -> storage | CONTENT Map header: false new: After: 1 New: 1",
186
185
  ]
187
186
  `);
188
187
  });
188
+
189
+ test("the order of updates between CoValues should be preserved to ensure consistency in case of shutdown in the middle of sync", async () => {
190
+ const client = setupTestNode();
191
+
192
+ await client.addAsyncStorage();
193
+
194
+ const group = client.node.createGroup();
195
+ const initialMap = group.createMap();
196
+
197
+ const child = group.createMap();
198
+ child.set("parent", initialMap.id);
199
+ initialMap.set("child", child.id);
200
+
201
+ await initialMap.core.waitForSync();
202
+
203
+ expect(
204
+ SyncMessagesLog.getMessages({
205
+ Group: group.core,
206
+ InitialMap: initialMap.core,
207
+ ChildMap: child.core,
208
+ }),
209
+ ).toMatchInlineSnapshot(`
210
+ [
211
+ "client -> storage | CONTENT Group header: true new: After: 0 New: 3",
212
+ "client -> storage | CONTENT InitialMap header: true new: ",
213
+ "client -> storage | CONTENT ChildMap header: true new: After: 0 New: 1",
214
+ "client -> storage | CONTENT InitialMap header: false new: After: 0 New: 1",
215
+ ]
216
+ `);
217
+ });
189
218
  });
190
219
 
191
220
  describe("client syncs with a server with storage", () => {
@@ -271,20 +300,16 @@ describe("client syncs with a server with storage", () => {
271
300
  [
272
301
  "client -> storage | CONTENT Group header: true new: After: 0 New: 5",
273
302
  "client -> server | CONTENT Group header: true new: After: 0 New: 5",
274
- "client -> storage | CONTENT Map header: true new: expectContentUntil: header/200",
275
- "client -> storage | CONTENT Map header: false new: After: 0 New: 73",
303
+ "client -> storage | CONTENT Map header: true new: After: 0 New: 73",
304
+ "client -> server | CONTENT Map header: true new: After: 0 New: 73",
276
305
  "client -> storage | CONTENT Map header: false new: After: 73 New: 73",
277
- "client -> storage | CONTENT Map header: false new: After: 146 New: 54",
278
- "client -> server | CONTENT Map header: true new: expectContentUntil: header/200",
279
- "client -> server | CONTENT Map header: false new: After: 0 New: 73",
280
306
  "client -> server | CONTENT Map header: false new: After: 73 New: 73",
307
+ "client -> storage | CONTENT Map header: false new: After: 146 New: 54",
281
308
  "client -> server | CONTENT Map header: false new: After: 146 New: 54",
282
309
  "server -> client | KNOWN Group sessions: header/5",
283
310
  "server -> storage | CONTENT Group header: true new: After: 0 New: 5",
284
- "server -> client | KNOWN Map sessions: header/0",
285
- "server -> storage | CONTENT Map header: true new: expectContentUntil: header/200",
286
311
  "server -> client | KNOWN Map sessions: header/73",
287
- "server -> storage | CONTENT Map header: false new: After: 0 New: 73",
312
+ "server -> storage | CONTENT Map header: true new: After: 0 New: 73",
288
313
  "server -> client | KNOWN Map sessions: header/146",
289
314
  "server -> storage | CONTENT Map header: false new: After: 73 New: 73",
290
315
  "server -> client | KNOWN Map sessions: header/200",
@@ -369,7 +394,7 @@ describe("client syncs with a server with storage", () => {
369
394
 
370
395
  const correctionSpy = vi.fn();
371
396
 
372
- client.node.storage?.store(newContentChunks.slice(1, 2), correctionSpy);
397
+ client.node.storage?.store(newContentChunks[1]!, correctionSpy);
373
398
 
374
399
  // Wait for the content to be stored in the storage
375
400
  // We can't use waitForSync because we are trying to store stale data
@@ -100,15 +100,18 @@ test("Can sync a coValue with private transactions through a server to another c
100
100
 
101
101
  const map = group.createMap();
102
102
  map.set("hello", "world", "private");
103
+
103
104
  group.addMember("everyone", "reader");
104
105
 
105
106
  const { node: client2 } = await setupTestAccount({
106
107
  connected: true,
107
108
  });
108
109
 
109
- const mapOnClient2 = await loadCoValueOrFail(client2, map.id);
110
+ await waitFor(async () => {
111
+ const loadedMap = await loadCoValueOrFail(client2, map.id);
110
112
 
111
- expect(mapOnClient2.get("hello")).toEqual("world");
113
+ expect(loadedMap.get("hello")).toEqual("world");
114
+ });
112
115
  });
113
116
 
114
117
  test("should keep the peer state when the peer closes if persistent is true", async () => {
@@ -563,8 +566,6 @@ describe("SyncManager - knownStates vs optimisticKnownStates", () => {
563
566
  const mapOnClient = group.createMap();
564
567
  mapOnClient.set("key1", "value1", "trusting");
565
568
 
566
- await client.syncManager.syncCoValue(mapOnClient.core);
567
-
568
569
  // Wait for the full sync to complete
569
570
  await mapOnClient.core.waitForSync();
570
571
 
@@ -594,7 +595,6 @@ describe("SyncManager - knownStates vs optimisticKnownStates", () => {
594
595
  const map = group.createMap();
595
596
  map.set("key1", "value1", "trusting");
596
597
 
597
- await client.node.syncManager.syncCoValue(map.core);
598
598
  await map.core.waitForSync();
599
599
 
600
600
  // Block the content messages
@@ -606,7 +606,7 @@ describe("SyncManager - knownStates vs optimisticKnownStates", () => {
606
606
 
607
607
  map.set("key2", "value2", "trusting");
608
608
 
609
- await client.node.syncManager.syncCoValue(map.core);
609
+ await new Promise<void>(queueMicrotask);
610
610
 
611
611
  expect(peerState.optimisticKnownStates.get(map.core.id)).not.toEqual(
612
612
  peerState.knownStates.get(map.core.id),
@@ -638,8 +638,6 @@ describe("SyncManager.addPeer", () => {
638
638
  const map = group.createMap();
639
639
  map.set("key1", "value1", "trusting");
640
640
 
641
- await client.node.syncManager.syncCoValue(map.core);
642
-
643
641
  // Wait for initial sync
644
642
  await map.core.waitForSync();
645
643
 
@@ -671,8 +669,6 @@ describe("SyncManager.addPeer", () => {
671
669
  const map = group.createMap();
672
670
  map.set("key1", "value1", "trusting");
673
671
 
674
- await client.node.syncManager.syncCoValue(map.core);
675
-
676
672
  // Wait for initial sync
677
673
  await map.core.waitForSync();
678
674
 
@@ -843,8 +839,6 @@ describe("waitForSyncWithPeer", () => {
843
839
  const map = group.createMap();
844
840
  map.set("key1", "value1", "trusting");
845
841
 
846
- await client.node.syncManager.syncCoValue(map.core);
847
-
848
842
  await expect(
849
843
  client.node.syncManager.waitForSyncWithPeer(
850
844
  peerState.id,
@@ -868,8 +862,6 @@ describe("waitForSyncWithPeer", () => {
868
862
  return Promise.resolve();
869
863
  });
870
864
 
871
- await client.node.syncManager.syncCoValue(map.core);
872
-
873
865
  await expect(
874
866
  client.node.syncManager.waitForSyncWithPeer(
875
867
  peerState.id,
@@ -78,12 +78,15 @@ describe("client to server upload", () => {
78
78
  }),
79
79
  ).toMatchInlineSnapshot(`
80
80
  [
81
+ "client -> server | CONTENT Group header: true new: After: 0 New: 3",
81
82
  "client -> server | CONTENT ParentGroup header: true new: After: 0 New: 6",
82
- "client -> server | CONTENT Group header: true new: After: 0 New: 5",
83
+ "client -> server | CONTENT Group header: false new: After: 3 New: 2",
83
84
  "client -> server | CONTENT Map header: true new: After: 0 New: 1",
85
+ "server -> client | KNOWN Group sessions: header/3",
84
86
  "server -> client | KNOWN ParentGroup sessions: header/6",
85
87
  "server -> client | KNOWN Group sessions: header/5",
86
88
  "server -> client | KNOWN Map sessions: header/1",
89
+ "client -> server | CONTENT ParentGroup header: true new: ",
87
90
  ]
88
91
  `);
89
92
  });
@@ -253,6 +256,40 @@ describe("client to server upload", () => {
253
256
  `);
254
257
  });
255
258
 
259
+ test("local updates batching", async () => {
260
+ const client = setupTestNode({
261
+ connected: true,
262
+ });
263
+
264
+ const group = client.node.createGroup();
265
+ const initialMap = group.createMap();
266
+
267
+ const child = group.createMap();
268
+ child.set("parent", initialMap.id);
269
+ initialMap.set("child", child.id);
270
+
271
+ await initialMap.core.waitForSync();
272
+
273
+ expect(
274
+ SyncMessagesLog.getMessages({
275
+ Group: group.core,
276
+ InitialMap: initialMap.core,
277
+ ChildMap: child.core,
278
+ }),
279
+ ).toMatchInlineSnapshot(`
280
+ [
281
+ "client -> server | CONTENT Group header: true new: After: 0 New: 3",
282
+ "client -> server | CONTENT InitialMap header: true new: ",
283
+ "client -> server | CONTENT ChildMap header: true new: After: 0 New: 1",
284
+ "client -> server | CONTENT InitialMap header: false new: After: 0 New: 1",
285
+ "server -> client | KNOWN Group sessions: header/3",
286
+ "server -> client | KNOWN InitialMap sessions: header/0",
287
+ "server -> client | KNOWN ChildMap sessions: header/1",
288
+ "server -> client | KNOWN InitialMap sessions: header/1",
289
+ ]
290
+ `);
291
+ });
292
+
256
293
  test("large coValue upload streaming", async () => {
257
294
  const client = setupTestNode({
258
295
  connected: true,
@@ -5,11 +5,7 @@ import { join } from "node:path";
5
5
  import Database, { type Database as DatabaseT } from "libsql";
6
6
  import { onTestFinished } from "vitest";
7
7
  import { RawCoID, StorageAPI } from "../exports";
8
- import {
9
- SQLiteDatabaseDriver,
10
- StorageApiAsync,
11
- StorageApiSync,
12
- } from "../storage";
8
+ import { SQLiteDatabaseDriver } from "../storage";
13
9
  import { getSqliteStorage } from "../storage/sqlite";
14
10
  import {
15
11
  SQLiteDatabaseDriverAsync,
@@ -148,13 +144,11 @@ function trackStorageMessages(
148
144
  const originalLoad = storage.load;
149
145
 
150
146
  storage.store = function (data, correctionCallback) {
151
- for (const msg of data ?? []) {
152
- SyncMessagesLog.add({
153
- from: nodeName,
154
- to: storageName,
155
- msg,
156
- });
157
- }
147
+ SyncMessagesLog.add({
148
+ from: nodeName,
149
+ to: storageName,
150
+ msg: data,
151
+ });
158
152
 
159
153
  return originalStore.call(storage, data, (correction) => {
160
154
  SyncMessagesLog.add({
@@ -167,7 +161,19 @@ function trackStorageMessages(
167
161
  },
168
162
  });
169
163
 
170
- return correctionCallback(correction);
164
+ const correctionMessages = correctionCallback(correction);
165
+
166
+ if (correctionMessages) {
167
+ for (const msg of correctionMessages) {
168
+ SyncMessagesLog.add({
169
+ from: nodeName,
170
+ to: storageName,
171
+ msg,
172
+ });
173
+ }
174
+ }
175
+
176
+ return correctionMessages;
171
177
  });
172
178
  };
173
179
 
@@ -40,6 +40,14 @@ export function randomAgentAndSessionID(): [ControlledAgent, SessionID] {
40
40
  return [new ControlledAgent(agentSecret, Crypto), sessionID];
41
41
  }
42
42
 
43
+ export function agentAndSessionIDFromSecret(
44
+ secret: AgentSecret,
45
+ ): [ControlledAgent, SessionID] {
46
+ const sessionID = Crypto.newRandomSessionID(Crypto.getAgentID(secret));
47
+
48
+ return [new ControlledAgent(secret, Crypto), sessionID];
49
+ }
50
+
43
51
  export function nodeWithRandomAgentAndSessionID() {
44
52
  const [agent, session] = randomAgentAndSessionID();
45
53
  return new LocalNode(agent.agentSecret, session, Crypto);
@@ -154,8 +162,8 @@ export function connectTwoPeers(
154
162
  bRole: "client" | "server",
155
163
  ) {
156
164
  const [aAsPeer, bAsPeer] = connectedPeers(
157
- "peer:" + a.getCurrentAgent().id,
158
- "peer:" + b.getCurrentAgent().id,
165
+ "peer:" + a.currentSessionID,
166
+ "peer:" + b.currentSessionID,
159
167
  {
160
168
  peer1role: aRole,
161
169
  peer2role: bRole,
@@ -443,7 +451,7 @@ export function getSyncServerConnectedPeer(opts: {
443
451
 
444
452
  const { peer1, peer2 } = connectedPeersWithMessagesTracking({
445
453
  peer1: {
446
- id: currentSyncServer.getCurrentAgent().id,
454
+ id: currentSyncServer.currentSessionID,
447
455
  role: "server",
448
456
  name: opts.syncServerName,
449
457
  },
@@ -472,9 +480,13 @@ export function setupTestNode(
472
480
  opts: {
473
481
  isSyncServer?: boolean;
474
482
  connected?: boolean;
483
+ secret?: AgentSecret;
475
484
  } = {},
476
485
  ) {
477
- const [admin, session] = randomAgentAndSessionID();
486
+ const [admin, session] = opts.secret
487
+ ? agentAndSessionIDFromSecret(opts.secret)
488
+ : randomAgentAndSessionID();
489
+
478
490
  let node = new LocalNode(admin.agentSecret, session, Crypto);
479
491
 
480
492
  if (opts.isSyncServer) {
@@ -489,7 +501,7 @@ export function setupTestNode(
489
501
  }) {
490
502
  const { peer, peerStateOnServer, peerOnServer } =
491
503
  getSyncServerConnectedPeer({
492
- peerId: node.getCurrentAgent().id,
504
+ peerId: session,
493
505
  syncServerName: opts?.syncServerName,
494
506
  ourName: opts?.ourName,
495
507
  syncServer: opts?.syncServer,
@@ -551,6 +563,13 @@ export function setupTestNode(
551
563
 
552
564
  return node;
553
565
  },
566
+ spawnNewSession: () => {
567
+ return setupTestNode({
568
+ secret: node.agentSecret,
569
+ connected: opts.connected,
570
+ isSyncServer: opts.isSyncServer,
571
+ });
572
+ },
554
573
  };
555
574
 
556
575
  return ctx;
@@ -638,6 +657,11 @@ export async function setupTestAccount(
638
657
  connectToSyncServer,
639
658
  addStorage,
640
659
  addAsyncStorage,
660
+ disconnect: () => {
661
+ ctx.node.syncManager.getPeers().forEach((peer) => {
662
+ peer.gracefulShutdown();
663
+ });
664
+ },
641
665
  };
642
666
  }
643
667
 
@@ -1,5 +1,5 @@
1
- import { RawAccountID } from "../coValues/account.js";
2
- import { AgentID, SessionID } from "../ids.js";
1
+ import type { RawAccountID } from "../coValues/account.js";
2
+ import type { AgentID, SessionID } from "../ids.js";
3
3
 
4
4
  export function accountOrAgentIDfromSessionID(
5
5
  sessionID: SessionID,
@@ -1,15 +1,18 @@
1
- import { type RawCoValue, expectMap } from "../coValue.js";
1
+ import { type RawCoValue } from "../coValue.js";
2
2
  import { RawGroup } from "../coValues/group.js";
3
3
 
4
4
  export function expectGroup(content: RawCoValue): RawGroup {
5
- const map = expectMap(content);
6
- if (map.core.verified.header.ruleset.type !== "group") {
5
+ if (content.type !== "comap") {
6
+ throw new Error("Expected group");
7
+ }
8
+
9
+ if (content.core.verified.header.ruleset.type !== "group") {
7
10
  throw new Error("Expected group ruleset in group");
8
11
  }
9
12
 
10
- if (!(map instanceof RawGroup)) {
13
+ if (!(content instanceof RawGroup)) {
11
14
  throw new Error("Expected group");
12
15
  }
13
16
 
14
- return map;
17
+ return content;
15
18
  }