cojson 0.15.8 → 0.15.10

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 (218) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/dist/IncomingMessagesQueue.d.ts +27 -0
  4. package/dist/IncomingMessagesQueue.d.ts.map +1 -0
  5. package/dist/IncomingMessagesQueue.js +114 -0
  6. package/dist/IncomingMessagesQueue.js.map +1 -0
  7. package/dist/PeerState.d.ts +2 -10
  8. package/dist/PeerState.d.ts.map +1 -1
  9. package/dist/PeerState.js +9 -90
  10. package/dist/PeerState.js.map +1 -1
  11. package/dist/PriorityBasedMessageQueue.d.ts +2 -1
  12. package/dist/PriorityBasedMessageQueue.d.ts.map +1 -1
  13. package/dist/PriorityBasedMessageQueue.js +9 -6
  14. package/dist/PriorityBasedMessageQueue.js.map +1 -1
  15. package/dist/SyncStateManager.d.ts +1 -0
  16. package/dist/SyncStateManager.d.ts.map +1 -1
  17. package/dist/SyncStateManager.js +1 -1
  18. package/dist/SyncStateManager.js.map +1 -1
  19. package/dist/coValue.d.ts +1 -1
  20. package/dist/coValueCore/coValueCore.d.ts +9 -17
  21. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  22. package/dist/coValueCore/coValueCore.js +75 -50
  23. package/dist/coValueCore/coValueCore.js.map +1 -1
  24. package/dist/coValueCore/verifiedState.d.ts +10 -3
  25. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  26. package/dist/coValueCore/verifiedState.js +73 -14
  27. package/dist/coValueCore/verifiedState.js.map +1 -1
  28. package/dist/coValues/coMap.d.ts +3 -3
  29. package/dist/coValues/coStream.d.ts +2 -2
  30. package/dist/coValues/group.d.ts +1 -1
  31. package/dist/coValues/group.d.ts.map +1 -1
  32. package/dist/coValues/group.js +2 -4
  33. package/dist/coValues/group.js.map +1 -1
  34. package/dist/config.d.ts +19 -0
  35. package/dist/config.d.ts.map +1 -0
  36. package/dist/config.js +23 -0
  37. package/dist/config.js.map +1 -0
  38. package/dist/crypto/WasmCrypto.d.ts.map +1 -1
  39. package/dist/crypto/WasmCrypto.js +2 -1
  40. package/dist/crypto/WasmCrypto.js.map +1 -1
  41. package/dist/exports.d.ts +18 -7
  42. package/dist/exports.d.ts.map +1 -1
  43. package/dist/exports.js +11 -8
  44. package/dist/exports.js.map +1 -1
  45. package/dist/localNode.d.ts +8 -2
  46. package/dist/localNode.d.ts.map +1 -1
  47. package/dist/localNode.js +19 -12
  48. package/dist/localNode.js.map +1 -1
  49. package/dist/storage/StoreQueue.d.ts +15 -0
  50. package/dist/storage/StoreQueue.d.ts.map +1 -0
  51. package/dist/storage/StoreQueue.js +35 -0
  52. package/dist/storage/StoreQueue.js.map +1 -0
  53. package/dist/storage/index.d.ts +6 -0
  54. package/dist/storage/index.d.ts.map +1 -0
  55. package/dist/storage/index.js +6 -0
  56. package/dist/storage/index.js.map +1 -0
  57. package/dist/storage/knownState.d.ts +18 -0
  58. package/dist/storage/knownState.d.ts.map +1 -0
  59. package/dist/storage/knownState.js +63 -0
  60. package/dist/storage/knownState.js.map +1 -0
  61. package/dist/storage/sqlite/client.d.ts +37 -0
  62. package/dist/storage/sqlite/client.d.ts.map +1 -0
  63. package/dist/storage/sqlite/client.js +89 -0
  64. package/dist/storage/sqlite/client.js.map +1 -0
  65. package/dist/storage/sqlite/index.d.ts +5 -0
  66. package/dist/storage/sqlite/index.d.ts.map +1 -0
  67. package/dist/storage/sqlite/index.js +13 -0
  68. package/dist/storage/sqlite/index.js.map +1 -0
  69. package/dist/storage/sqlite/sqliteMigrations.d.ts +3 -0
  70. package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -0
  71. package/dist/storage/sqlite/sqliteMigrations.js +44 -0
  72. package/dist/storage/sqlite/sqliteMigrations.js.map +1 -0
  73. package/dist/storage/sqlite/types.d.ts +8 -0
  74. package/dist/storage/sqlite/types.d.ts.map +1 -0
  75. package/dist/storage/sqlite/types.js +2 -0
  76. package/dist/storage/sqlite/types.js.map +1 -0
  77. package/dist/storage/sqliteAsync/client.d.ts +37 -0
  78. package/dist/storage/sqliteAsync/client.d.ts.map +1 -0
  79. package/dist/storage/sqliteAsync/client.js +88 -0
  80. package/dist/storage/sqliteAsync/client.js.map +1 -0
  81. package/dist/storage/sqliteAsync/index.d.ts +6 -0
  82. package/dist/storage/sqliteAsync/index.d.ts.map +1 -0
  83. package/dist/storage/sqliteAsync/index.js +15 -0
  84. package/dist/storage/sqliteAsync/index.js.map +1 -0
  85. package/dist/storage/sqliteAsync/types.d.ts +9 -0
  86. package/dist/storage/sqliteAsync/types.d.ts.map +1 -0
  87. package/dist/storage/sqliteAsync/types.js +2 -0
  88. package/dist/storage/sqliteAsync/types.js.map +1 -0
  89. package/dist/storage/storageAsync.d.ts +22 -0
  90. package/dist/storage/storageAsync.d.ts.map +1 -0
  91. package/dist/storage/storageAsync.js +214 -0
  92. package/dist/storage/storageAsync.js.map +1 -0
  93. package/dist/storage/storageSync.d.ts +21 -0
  94. package/dist/storage/storageSync.d.ts.map +1 -0
  95. package/dist/storage/storageSync.js +206 -0
  96. package/dist/storage/storageSync.js.map +1 -0
  97. package/dist/storage/syncUtils.d.ts +13 -0
  98. package/dist/storage/syncUtils.d.ts.map +1 -0
  99. package/dist/storage/syncUtils.js +25 -0
  100. package/dist/storage/syncUtils.js.map +1 -0
  101. package/dist/storage/types.d.ts +82 -0
  102. package/dist/storage/types.d.ts.map +1 -0
  103. package/dist/storage/types.js +2 -0
  104. package/dist/storage/types.js.map +1 -0
  105. package/dist/streamUtils.d.ts +13 -9
  106. package/dist/streamUtils.d.ts.map +1 -1
  107. package/dist/streamUtils.js +46 -13
  108. package/dist/streamUtils.js.map +1 -1
  109. package/dist/sync.d.ts +22 -14
  110. package/dist/sync.d.ts.map +1 -1
  111. package/dist/sync.js +143 -125
  112. package/dist/sync.js.map +1 -1
  113. package/dist/tests/IncomingMessagesQueue.test.d.ts +2 -0
  114. package/dist/tests/IncomingMessagesQueue.test.d.ts.map +1 -0
  115. package/dist/tests/IncomingMessagesQueue.test.js +437 -0
  116. package/dist/tests/IncomingMessagesQueue.test.js.map +1 -0
  117. package/dist/tests/PeerState.test.js +6 -94
  118. package/dist/tests/PeerState.test.js.map +1 -1
  119. package/dist/tests/PriorityBasedMessageQueue.test.js +14 -14
  120. package/dist/tests/PriorityBasedMessageQueue.test.js.map +1 -1
  121. package/dist/tests/StoreQueue.test.d.ts +2 -0
  122. package/dist/tests/StoreQueue.test.d.ts.map +1 -0
  123. package/dist/tests/StoreQueue.test.js +208 -0
  124. package/dist/tests/StoreQueue.test.js.map +1 -0
  125. package/dist/tests/SyncStateManager.test.js +3 -1
  126. package/dist/tests/SyncStateManager.test.js.map +1 -1
  127. package/dist/tests/account.test.js +9 -9
  128. package/dist/tests/account.test.js.map +1 -1
  129. package/dist/tests/coStream.test.js +1 -1
  130. package/dist/tests/coStream.test.js.map +1 -1
  131. package/dist/tests/coValueCore.test.js +208 -1
  132. package/dist/tests/coValueCore.test.js.map +1 -1
  133. package/dist/tests/coValueCoreLoadingState.test.js +2 -2
  134. package/dist/tests/coValueCoreLoadingState.test.js.map +1 -1
  135. package/dist/tests/group.addMember.test.js.map +1 -1
  136. package/dist/tests/group.removeMember.test.js +1 -1
  137. package/dist/tests/group.removeMember.test.js.map +1 -1
  138. package/dist/tests/messagesTestUtils.js +1 -1
  139. package/dist/tests/messagesTestUtils.js.map +1 -1
  140. package/dist/tests/sync.auth.test.js +23 -15
  141. package/dist/tests/sync.auth.test.js.map +1 -1
  142. package/dist/tests/sync.invite.test.js +10 -16
  143. package/dist/tests/sync.invite.test.js.map +1 -1
  144. package/dist/tests/sync.load.test.js +52 -50
  145. package/dist/tests/sync.load.test.js.map +1 -1
  146. package/dist/tests/sync.mesh.test.js +173 -56
  147. package/dist/tests/sync.mesh.test.js.map +1 -1
  148. package/dist/tests/sync.peerReconciliation.test.js +42 -32
  149. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  150. package/dist/tests/sync.storage.test.js +162 -62
  151. package/dist/tests/sync.storage.test.js.map +1 -1
  152. package/dist/tests/sync.storageAsync.test.d.ts +2 -0
  153. package/dist/tests/sync.storageAsync.test.d.ts.map +1 -0
  154. package/dist/tests/sync.storageAsync.test.js +361 -0
  155. package/dist/tests/sync.storageAsync.test.js.map +1 -0
  156. package/dist/tests/sync.test.js +16 -21
  157. package/dist/tests/sync.test.js.map +1 -1
  158. package/dist/tests/sync.upload.test.js +28 -25
  159. package/dist/tests/sync.upload.test.js.map +1 -1
  160. package/dist/tests/testStorage.d.ts +12 -0
  161. package/dist/tests/testStorage.d.ts.map +1 -0
  162. package/dist/tests/testStorage.js +151 -0
  163. package/dist/tests/testStorage.js.map +1 -0
  164. package/dist/tests/testUtils.d.ts +20 -15
  165. package/dist/tests/testUtils.d.ts.map +1 -1
  166. package/dist/tests/testUtils.js +79 -45
  167. package/dist/tests/testUtils.js.map +1 -1
  168. package/package.json +2 -2
  169. package/src/IncomingMessagesQueue.ts +142 -0
  170. package/src/PeerState.ts +11 -110
  171. package/src/PriorityBasedMessageQueue.ts +13 -5
  172. package/src/SyncStateManager.ts +1 -1
  173. package/src/coValueCore/coValueCore.ts +100 -66
  174. package/src/coValueCore/verifiedState.ts +91 -21
  175. package/src/coValues/group.ts +2 -4
  176. package/src/config.ts +26 -0
  177. package/src/crypto/WasmCrypto.ts +3 -1
  178. package/src/exports.ts +20 -27
  179. package/src/localNode.ts +27 -12
  180. package/src/storage/StoreQueue.ts +56 -0
  181. package/src/storage/index.ts +5 -0
  182. package/src/storage/knownState.ts +88 -0
  183. package/src/storage/sqlite/client.ts +180 -0
  184. package/src/storage/sqlite/index.ts +19 -0
  185. package/src/storage/sqlite/sqliteMigrations.ts +44 -0
  186. package/src/storage/sqlite/types.ts +7 -0
  187. package/src/storage/sqliteAsync/client.ts +179 -0
  188. package/src/storage/sqliteAsync/index.ts +25 -0
  189. package/src/storage/sqliteAsync/types.ts +8 -0
  190. package/src/storage/storageAsync.ts +367 -0
  191. package/src/storage/storageSync.ts +343 -0
  192. package/src/storage/syncUtils.ts +50 -0
  193. package/src/storage/types.ts +162 -0
  194. package/src/streamUtils.ts +61 -19
  195. package/src/sync.ts +191 -160
  196. package/src/tests/IncomingMessagesQueue.test.ts +626 -0
  197. package/src/tests/PeerState.test.ts +6 -118
  198. package/src/tests/PriorityBasedMessageQueue.test.ts +18 -14
  199. package/src/tests/StoreQueue.test.ts +283 -0
  200. package/src/tests/SyncStateManager.test.ts +4 -1
  201. package/src/tests/account.test.ts +11 -12
  202. package/src/tests/coStream.test.ts +1 -3
  203. package/src/tests/coValueCore.test.ts +270 -1
  204. package/src/tests/coValueCoreLoadingState.test.ts +2 -2
  205. package/src/tests/group.addMember.test.ts +1 -0
  206. package/src/tests/group.removeMember.test.ts +2 -8
  207. package/src/tests/messagesTestUtils.ts +2 -2
  208. package/src/tests/sync.auth.test.ts +24 -14
  209. package/src/tests/sync.invite.test.ts +11 -17
  210. package/src/tests/sync.load.test.ts +53 -49
  211. package/src/tests/sync.mesh.test.ts +198 -56
  212. package/src/tests/sync.peerReconciliation.test.ts +44 -34
  213. package/src/tests/sync.storage.test.ts +231 -64
  214. package/src/tests/sync.storageAsync.test.ts +486 -0
  215. package/src/tests/sync.test.ts +17 -23
  216. package/src/tests/sync.upload.test.ts +29 -24
  217. package/src/tests/testStorage.ts +216 -0
  218. package/src/tests/testUtils.ts +89 -54
@@ -0,0 +1,486 @@
1
+ import { assert, beforeEach, describe, expect, test, vi } from "vitest";
2
+
3
+ import { emptyKnownState } from "../exports";
4
+ import {
5
+ SyncMessagesLog,
6
+ TEST_NODE_CONFIG,
7
+ loadCoValueOrFail,
8
+ setupTestNode,
9
+ waitFor,
10
+ } from "./testUtils";
11
+
12
+ // We want to simulate a real world communication that happens asynchronously
13
+ TEST_NODE_CONFIG.withAsyncPeers = true;
14
+
15
+ describe("client with storage syncs with server", () => {
16
+ let jazzCloud: ReturnType<typeof setupTestNode>;
17
+
18
+ beforeEach(async () => {
19
+ SyncMessagesLog.clear();
20
+ jazzCloud = setupTestNode({
21
+ isSyncServer: true,
22
+ });
23
+ });
24
+
25
+ test("coValue loading (empty storage)", async () => {
26
+ const client = setupTestNode();
27
+
28
+ client.connectToSyncServer();
29
+ await client.addAsyncStorage();
30
+
31
+ const group = jazzCloud.node.createGroup();
32
+ const map = group.createMap();
33
+ map.set("hello", "world", "trusting");
34
+
35
+ const mapOnClient = await loadCoValueOrFail(client.node, map.id);
36
+ expect(mapOnClient.get("hello")).toEqual("world");
37
+
38
+ expect(
39
+ SyncMessagesLog.getMessages({
40
+ Group: group.core,
41
+ Map: map.core,
42
+ }),
43
+ ).toMatchInlineSnapshot(`
44
+ [
45
+ "client -> storage | LOAD Map sessions: empty",
46
+ "storage -> client | KNOWN Map sessions: empty",
47
+ "client -> server | LOAD Map sessions: empty",
48
+ "server -> client | CONTENT Group header: true new: After: 0 New: 3",
49
+ "server -> client | CONTENT Map header: true new: After: 0 New: 1",
50
+ "client -> server | KNOWN Group sessions: header/3",
51
+ "client -> storage | CONTENT Group header: true new: After: 0 New: 3",
52
+ "client -> server | KNOWN Map sessions: header/1",
53
+ "client -> storage | CONTENT Map header: true new: After: 0 New: 1",
54
+ ]
55
+ `);
56
+ });
57
+
58
+ test("coValue loading (synced storage)", async () => {
59
+ const client = setupTestNode();
60
+
61
+ client.connectToSyncServer();
62
+ const { storage } = await client.addAsyncStorage();
63
+
64
+ const group = jazzCloud.node.createGroup();
65
+ const map = group.createMap();
66
+ map.set("hello", "world", "trusting");
67
+
68
+ const firstLoad = await loadCoValueOrFail(client.node, map.id);
69
+ await firstLoad.core.waitForSync(); // Need to wait for sync with storage
70
+
71
+ client.restart();
72
+
73
+ client.connectToSyncServer();
74
+ client.addStorage({
75
+ ourName: "client",
76
+ storage,
77
+ });
78
+
79
+ SyncMessagesLog.clear();
80
+
81
+ const mapOnClient = await loadCoValueOrFail(client.node, map.id);
82
+ expect(mapOnClient.get("hello")).toEqual("world");
83
+
84
+ expect(
85
+ SyncMessagesLog.getMessages({
86
+ Group: group.core,
87
+ Map: map.core,
88
+ }),
89
+ ).toMatchInlineSnapshot(`
90
+ [
91
+ "client -> storage | LOAD Map sessions: empty",
92
+ "storage -> client | CONTENT Group header: true new: After: 0 New: 3",
93
+ "client -> server | LOAD Group sessions: header/3",
94
+ "storage -> client | CONTENT Map header: true new: After: 0 New: 1",
95
+ "client -> server | LOAD Map sessions: header/1",
96
+ ]
97
+ `);
98
+ });
99
+
100
+ test("coValue with parent groups loading", async () => {
101
+ const client = setupTestNode();
102
+
103
+ client.connectToSyncServer();
104
+ await client.addAsyncStorage();
105
+
106
+ const group = jazzCloud.node.createGroup();
107
+ const parentGroup = jazzCloud.node.createGroup();
108
+ parentGroup.addMember("everyone", "reader");
109
+
110
+ group.extend(parentGroup);
111
+
112
+ const map = group.createMap();
113
+ map.set("hello", "world");
114
+
115
+ const mapOnClient = await loadCoValueOrFail(client.node, map.id);
116
+ expect(mapOnClient.get("hello")).toEqual("world");
117
+
118
+ expect(
119
+ SyncMessagesLog.getMessages({
120
+ ParentGroup: parentGroup.core,
121
+ Group: group.core,
122
+ Map: map.core,
123
+ }),
124
+ ).toMatchInlineSnapshot(`
125
+ [
126
+ "client -> storage | LOAD Map sessions: empty",
127
+ "storage -> client | KNOWN Map sessions: empty",
128
+ "client -> server | LOAD Map sessions: empty",
129
+ "server -> client | CONTENT ParentGroup header: true new: After: 0 New: 6",
130
+ "server -> client | CONTENT Group header: true new: After: 0 New: 5",
131
+ "server -> client | CONTENT Map header: true new: After: 0 New: 1",
132
+ "client -> server | KNOWN ParentGroup sessions: header/6",
133
+ "client -> storage | CONTENT ParentGroup header: true new: After: 0 New: 6",
134
+ "client -> server | KNOWN Group sessions: header/5",
135
+ "client -> storage | CONTENT Group header: true new: After: 0 New: 5",
136
+ "client -> server | KNOWN Map sessions: header/1",
137
+ "client -> storage | CONTENT Map header: true new: After: 0 New: 1",
138
+ ]
139
+ `);
140
+ });
141
+
142
+ test("updating a coValue while offline", async () => {
143
+ const client = setupTestNode();
144
+
145
+ client.connectToSyncServer();
146
+ await client.addAsyncStorage();
147
+
148
+ const group = jazzCloud.node.createGroup();
149
+ const map = group.createMap();
150
+ map.set("hello", "world", "trusting");
151
+
152
+ const mapOnClient = await loadCoValueOrFail(client.node, map.id);
153
+ expect(mapOnClient.get("hello")).toEqual("world");
154
+
155
+ client.node.syncManager.getPeers()[0]?.gracefulShutdown();
156
+
157
+ SyncMessagesLog.clear();
158
+ map.set("hello", "updated", "trusting");
159
+
160
+ client.connectToSyncServer();
161
+
162
+ await map.core.waitForSync();
163
+
164
+ expect(mapOnClient.get("hello")).toEqual("updated");
165
+
166
+ await mapOnClient.core.waitForSync();
167
+
168
+ expect(
169
+ SyncMessagesLog.getMessages({
170
+ Group: group.core,
171
+ Map: map.core,
172
+ }),
173
+ ).toMatchInlineSnapshot(`
174
+ [
175
+ "client -> server | LOAD Group sessions: header/3",
176
+ "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
+ "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",
182
+ "client -> server | KNOWN Map sessions: header/2",
183
+ "client -> storage | CONTENT Map header: true new: After: 0 New: 2",
184
+ "client -> server | KNOWN Map sessions: header/2",
185
+ "client -> storage | CONTENT Map header: false new: After: 1 New: 1",
186
+ ]
187
+ `);
188
+ });
189
+ });
190
+
191
+ describe("client syncs with a server with storage", () => {
192
+ let jazzCloud: ReturnType<typeof setupTestNode>;
193
+
194
+ beforeEach(async () => {
195
+ SyncMessagesLog.clear();
196
+ jazzCloud = setupTestNode({
197
+ isSyncServer: true,
198
+ });
199
+ jazzCloud.addStorage({
200
+ ourName: "server",
201
+ });
202
+ });
203
+
204
+ test("coValue uploading", async () => {
205
+ const client = setupTestNode();
206
+
207
+ client.connectToSyncServer();
208
+
209
+ const group = client.node.createGroup();
210
+ const map = group.createMap();
211
+ map.set("hello", "world", "trusting");
212
+
213
+ await map.core.waitForSync();
214
+
215
+ const mapOnServer = await loadCoValueOrFail(jazzCloud.node, map.id);
216
+ expect(mapOnServer.get("hello")).toEqual("world");
217
+
218
+ expect(
219
+ SyncMessagesLog.getMessages({
220
+ Group: group.core,
221
+ Map: map.core,
222
+ }),
223
+ ).toMatchInlineSnapshot(`
224
+ [
225
+ "client -> server | CONTENT Group header: true new: After: 0 New: 3",
226
+ "client -> server | CONTENT Map header: true new: After: 0 New: 1",
227
+ "server -> client | KNOWN Group sessions: header/3",
228
+ "server -> storage | CONTENT Group header: true new: After: 0 New: 3",
229
+ "server -> client | KNOWN Map sessions: header/1",
230
+ "server -> storage | CONTENT Map header: true new: After: 0 New: 1",
231
+ ]
232
+ `);
233
+ });
234
+
235
+ test("large coValue streaming", async () => {
236
+ const client = setupTestNode();
237
+
238
+ client.connectToSyncServer({
239
+ syncServer: jazzCloud.node,
240
+ });
241
+
242
+ const { storage } = await client.addAsyncStorage({
243
+ ourName: "client",
244
+ });
245
+
246
+ const group = client.node.createGroup();
247
+ group.addMember("everyone", "writer");
248
+
249
+ const largeMap = group.createMap();
250
+
251
+ // Generate a large amount of data (about 100MB)
252
+ const dataSize = 1 * 200 * 1024;
253
+ const chunkSize = 1024; // 1KB chunks
254
+ const chunks = dataSize / chunkSize;
255
+
256
+ const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
257
+
258
+ for (let i = 0; i < chunks; i++) {
259
+ const key = `key${i}`;
260
+ largeMap.set(key, value, "trusting");
261
+ }
262
+
263
+ await largeMap.core.waitForSync();
264
+
265
+ expect(
266
+ SyncMessagesLog.getMessages({
267
+ Group: group.core,
268
+ Map: largeMap.core,
269
+ }),
270
+ ).toMatchInlineSnapshot(`
271
+ [
272
+ "client -> storage | CONTENT Group header: true new: After: 0 New: 5",
273
+ "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",
276
+ "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
+ "client -> server | CONTENT Map header: false new: After: 73 New: 73",
281
+ "client -> server | CONTENT Map header: false new: After: 146 New: 54",
282
+ "server -> client | KNOWN Group sessions: header/5",
283
+ "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
+ "server -> client | KNOWN Map sessions: header/73",
287
+ "server -> storage | CONTENT Map header: false new: After: 0 New: 73",
288
+ "server -> client | KNOWN Map sessions: header/146",
289
+ "server -> storage | CONTENT Map header: false new: After: 73 New: 73",
290
+ "server -> client | KNOWN Map sessions: header/200",
291
+ "server -> storage | CONTENT Map header: false new: After: 146 New: 54",
292
+ ]
293
+ `);
294
+
295
+ SyncMessagesLog.clear();
296
+
297
+ client.restart();
298
+
299
+ client.connectToSyncServer({
300
+ ourName: "client",
301
+ syncServer: jazzCloud.node,
302
+ });
303
+
304
+ client.addStorage({
305
+ ourName: "client",
306
+ storage,
307
+ });
308
+
309
+ const mapOnClient2 = await loadCoValueOrFail(client.node, largeMap.id);
310
+
311
+ await mapOnClient2.core.waitForSync();
312
+
313
+ expect(
314
+ SyncMessagesLog.getMessages({
315
+ Group: group.core,
316
+ Map: largeMap.core,
317
+ }),
318
+ ).toMatchInlineSnapshot(`
319
+ [
320
+ "client -> storage | LOAD Map sessions: empty",
321
+ "storage -> client | CONTENT Group header: true new: After: 0 New: 5",
322
+ "client -> server | LOAD Group sessions: header/5",
323
+ "storage -> client | CONTENT Map header: true new: After: 0 New: 73 expectContentUntil: header/200",
324
+ "client -> server | LOAD Map sessions: header/200",
325
+ "storage -> client | CONTENT Map header: true new: After: 73 New: 73",
326
+ "storage -> client | CONTENT Map header: true new: After: 146 New: 54",
327
+ "server -> client | KNOWN Group sessions: header/5",
328
+ "server -> client | KNOWN Map sessions: header/200",
329
+ ]
330
+ `);
331
+ });
332
+
333
+ test("storing stale data should not compromise the signatures", async () => {
334
+ const client = setupTestNode();
335
+
336
+ client.connectToSyncServer({
337
+ syncServer: jazzCloud.node,
338
+ });
339
+
340
+ const { storage } = await client.addAsyncStorage({
341
+ ourName: "client",
342
+ });
343
+
344
+ const group = client.node.createGroup();
345
+ group.addMember("everyone", "writer");
346
+
347
+ const largeMap = group.createMap();
348
+
349
+ // Generate a large amount of data (about 100MB)
350
+ const dataSize = 1 * 200 * 1024;
351
+ const chunkSize = 1024; // 1KB chunks
352
+ const chunks = dataSize / chunkSize;
353
+
354
+ const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
355
+
356
+ for (let i = 0; i < chunks; i++) {
357
+ const key = `key${i}`;
358
+ largeMap.set(key, value, "trusting");
359
+ }
360
+
361
+ await largeMap.core.waitForSync();
362
+
363
+ const newContentChunks = largeMap.core.verified.newContentSince(
364
+ emptyKnownState(largeMap.id),
365
+ );
366
+
367
+ assert(newContentChunks);
368
+ assert(newContentChunks.length > 1);
369
+
370
+ const correctionSpy = vi.fn();
371
+
372
+ client.node.storage?.store(newContentChunks.slice(1, 2), correctionSpy);
373
+
374
+ // Wait for the content to be stored in the storage
375
+ // We can't use waitForSync because we are trying to store stale data
376
+ await new Promise((resolve) => setTimeout(resolve, 100));
377
+
378
+ expect(correctionSpy).not.toHaveBeenCalled();
379
+
380
+ client.restart();
381
+
382
+ client.connectToSyncServer({
383
+ ourName: "client",
384
+ syncServer: jazzCloud.node,
385
+ });
386
+
387
+ client.addStorage({
388
+ ourName: "client",
389
+ storage,
390
+ });
391
+
392
+ const mapOnClient2 = await loadCoValueOrFail(client.node, largeMap.id);
393
+
394
+ await waitFor(async () => {
395
+ expect(mapOnClient2.core.knownState()).toEqual(
396
+ largeMap.core.knownState(),
397
+ );
398
+ });
399
+ });
400
+
401
+ test("large coValue streaming from cold server", async () => {
402
+ const server = setupTestNode({
403
+ isSyncServer: true,
404
+ });
405
+ const { storage: serverStorage } = server.addStorage({
406
+ ourName: "server",
407
+ });
408
+
409
+ const client = setupTestNode();
410
+
411
+ client.connectToSyncServer({
412
+ syncServer: server.node,
413
+ });
414
+
415
+ const { storage } = await client.addAsyncStorage({
416
+ ourName: "client",
417
+ });
418
+
419
+ const group = client.node.createGroup();
420
+ group.addMember("everyone", "writer");
421
+
422
+ const largeMap = group.createMap();
423
+
424
+ // Generate a large amount of data (about 100MB)
425
+ const dataSize = 1 * 200 * 1024;
426
+ const chunkSize = 1024; // 1KB chunks
427
+ const chunks = dataSize / chunkSize;
428
+
429
+ const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
430
+
431
+ for (let i = 0; i < chunks; i++) {
432
+ const key = `key${i}`;
433
+ largeMap.set(key, value, "trusting");
434
+ }
435
+
436
+ await largeMap.core.waitForSync();
437
+
438
+ SyncMessagesLog.clear();
439
+
440
+ server.restart();
441
+
442
+ server.addStorage({
443
+ ourName: "server",
444
+ storage: serverStorage,
445
+ });
446
+
447
+ client.restart();
448
+
449
+ client.connectToSyncServer({
450
+ ourName: "client",
451
+ syncServer: server.node,
452
+ });
453
+
454
+ client.addStorage({
455
+ ourName: "client",
456
+ storage,
457
+ });
458
+
459
+ const mapOnClient2 = await loadCoValueOrFail(client.node, largeMap.id);
460
+
461
+ await mapOnClient2.core.waitForSync();
462
+
463
+ expect(
464
+ SyncMessagesLog.getMessages({
465
+ Group: group.core,
466
+ Map: largeMap.core,
467
+ }),
468
+ ).toMatchInlineSnapshot(`
469
+ [
470
+ "client -> storage | LOAD Map sessions: empty",
471
+ "storage -> client | CONTENT Group header: true new: After: 0 New: 5",
472
+ "client -> server | LOAD Group sessions: header/5",
473
+ "storage -> client | CONTENT Map header: true new: After: 0 New: 73 expectContentUntil: header/200",
474
+ "client -> server | LOAD Map sessions: header/200",
475
+ "storage -> client | CONTENT Map header: true new: After: 73 New: 73",
476
+ "storage -> client | CONTENT Map header: true new: After: 146 New: 54",
477
+ "server -> storage | LOAD Group sessions: empty",
478
+ "storage -> server | CONTENT Group header: true new: After: 0 New: 5",
479
+ "server -> client | KNOWN Group sessions: header/5",
480
+ "server -> storage | LOAD Map sessions: empty",
481
+ "storage -> server | CONTENT Map header: true new: After: 0 New: 73 expectContentUntil: header/200",
482
+ "server -> client | KNOWN Map sessions: header/200",
483
+ ]
484
+ `);
485
+ });
486
+ });
@@ -9,30 +9,29 @@ import {
9
9
  } from "vitest";
10
10
  import { expectMap } from "../coValue.js";
11
11
  import { RawCoMap } from "../coValues/coMap.js";
12
- import type { RawGroup } from "../coValues/group.js";
13
12
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
14
- import { LocalNode } from "../localNode.js";
15
13
  import { connectedPeers, newQueuePair } from "../streamUtils.js";
16
14
  import type { LoadMessage } from "../sync.js";
17
15
  import {
16
+ TEST_NODE_CONFIG,
18
17
  blockMessageTypeOnOutgoingPeer,
19
18
  connectTwoPeers,
20
19
  createTestMetricReader,
21
20
  createTestNode,
22
21
  loadCoValueOrFail,
23
22
  nodeWithRandomAgentAndSessionID,
24
- randomAgentAndSessionID,
25
23
  setupTestAccount,
26
24
  setupTestNode,
27
25
  tearDownTestMetricReader,
28
26
  waitFor,
29
27
  } from "./testUtils.js";
30
28
 
29
+ // We want to simulate a real world communication that happens asynchronously
30
+ TEST_NODE_CONFIG.withAsyncPeers = true;
31
+
31
32
  const Crypto = await WasmCrypto.create();
32
33
 
33
- let jazzCloud = setupTestNode({
34
- isSyncServer: true,
35
- });
34
+ let jazzCloud: ReturnType<typeof setupTestNode>;
36
35
 
37
36
  beforeEach(async () => {
38
37
  jazzCloud = setupTestNode({
@@ -47,16 +46,14 @@ test("If we add a client peer, but it never subscribes to a coValue, it won't ge
47
46
 
48
47
  const map = group.createMap();
49
48
 
50
- const [inRx, _inTx] = newQueuePair();
51
- const [outRx, outTx] = newQueuePair();
52
- const outRxQ = outRx[Symbol.asyncIterator]();
49
+ const [inRx] = newQueuePair();
50
+ const [, outTx] = newQueuePair();
53
51
 
54
52
  node.syncManager.addPeer({
55
53
  id: "test",
56
54
  incoming: inRx,
57
55
  outgoing: outTx,
58
56
  role: "client",
59
- crashOnClose: true,
60
57
  });
61
58
 
62
59
  map.set("hello", "world", "trusting");
@@ -66,7 +63,9 @@ test("If we add a client peer, but it never subscribes to a coValue, it won't ge
66
63
  );
67
64
 
68
65
  const result = await Promise.race([
69
- outRxQ.next().then((value) => value.value),
66
+ new Promise((resolve) => {
67
+ inRx.onMessage((msg) => resolve(msg));
68
+ }),
70
69
  timeoutPromise,
71
70
  ]);
72
71
 
@@ -115,7 +114,7 @@ test("Can sync a coValue with private transactions through a server to another c
115
114
  test("should keep the peer state when the peer closes", async () => {
116
115
  const client = setupTestNode();
117
116
 
118
- const { peer, peerState } = client.connectToSyncServer();
117
+ const { peer, peerState, peerOnServer } = client.connectToSyncServer();
119
118
 
120
119
  const group = jazzCloud.node.createGroup();
121
120
  const map = group.createMap();
@@ -125,10 +124,11 @@ test("should keep the peer state when the peer closes", async () => {
125
124
 
126
125
  const syncManager = client.node.syncManager;
127
126
 
128
- // @ts-expect-error Simulating a peer closing, leveraging the direct connection between the client/server peers
129
- await peer.outgoing.push("Disconnected");
127
+ peerOnServer.outgoing.push("Disconnected");
130
128
 
131
- await waitFor(() => peerState?.closed);
129
+ await waitFor(() => {
130
+ return peerState.closed;
131
+ });
132
132
 
133
133
  expect(syncManager.peers[peer.id]).not.toBeUndefined();
134
134
  });
@@ -136,7 +136,7 @@ test("should keep the peer state when the peer closes", async () => {
136
136
  test("should delete the peer state when the peer closes if deletePeerStateOnClose is true", async () => {
137
137
  const client = setupTestNode();
138
138
 
139
- const { peer, peerState } = client.connectToSyncServer();
139
+ const { peer, peerState, peerOnServer } = client.connectToSyncServer();
140
140
 
141
141
  peer.deletePeerStateOnClose = true;
142
142
 
@@ -148,8 +148,7 @@ test("should delete the peer state when the peer closes if deletePeerStateOnClos
148
148
 
149
149
  const syncManager = client.node.syncManager;
150
150
 
151
- // @ts-expect-error Simulating a peer closing, leveraging the direct connection between the client/server peers
152
- await peer.outgoing.push("Disconnected");
151
+ peerOnServer.outgoing.push("Disconnected");
153
152
 
154
153
  await waitFor(() => peerState?.closed);
155
154
 
@@ -929,7 +928,6 @@ describe("metrics", () => {
929
928
  incoming: inPeer1,
930
929
  outgoing: outPeer1,
931
930
  role: "client",
932
- crashOnClose: false,
933
931
  });
934
932
 
935
933
  connectedPeers = await metricReader.getMetricValue("jazz.peers", {
@@ -944,7 +942,6 @@ describe("metrics", () => {
944
942
  incoming: inPeer2,
945
943
  outgoing: outPeer2,
946
944
  role: "client",
947
- crashOnClose: false,
948
945
  });
949
946
 
950
947
  connectedPeers = await metricReader.getMetricValue("jazz.peers", {
@@ -959,7 +956,6 @@ describe("metrics", () => {
959
956
  incoming: inServer1,
960
957
  outgoing: outServer1,
961
958
  role: "server",
962
- crashOnClose: false,
963
959
  });
964
960
  connectedServerPeers = await metricReader.getMetricValue("jazz.peers", {
965
961
  role: "server",
@@ -970,7 +966,6 @@ describe("metrics", () => {
970
966
  });
971
967
  expect(connectedPeers).toBe(2);
972
968
 
973
- // @ts-expect-error Simulating peer-1 closing
974
969
  await outPeer1.push("Disconnected");
975
970
  await waitFor(() => node.syncManager.peers["peer-1"]?.closed);
976
971
 
@@ -979,7 +974,6 @@ describe("metrics", () => {
979
974
  });
980
975
  expect(connectedPeers).toBe(1);
981
976
 
982
- // @ts-expect-error Simulating server-1 closing
983
977
  await outServer1.push("Disconnected");
984
978
 
985
979
  await waitFor(() => node.syncManager.peers["server-1"]?.closed);