cojson 0.18.32 → 0.18.34

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 (120) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +18 -0
  3. package/dist/SyncStateManager.d.ts.map +1 -1
  4. package/dist/SyncStateManager.js +2 -2
  5. package/dist/SyncStateManager.js.map +1 -1
  6. package/dist/coValueCore/SessionMap.d.ts +1 -0
  7. package/dist/coValueCore/SessionMap.d.ts.map +1 -1
  8. package/dist/coValueCore/SessionMap.js +22 -12
  9. package/dist/coValueCore/SessionMap.js.map +1 -1
  10. package/dist/coValueCore/coValueCore.d.ts +14 -9
  11. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  12. package/dist/coValueCore/coValueCore.js +65 -51
  13. package/dist/coValueCore/coValueCore.js.map +1 -1
  14. package/dist/coValueCore/verifiedState.d.ts +5 -3
  15. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  16. package/dist/coValueCore/verifiedState.js +93 -76
  17. package/dist/coValueCore/verifiedState.js.map +1 -1
  18. package/dist/coValues/group.d.ts +1 -0
  19. package/dist/coValues/group.d.ts.map +1 -1
  20. package/dist/coValues/group.js +24 -4
  21. package/dist/coValues/group.js.map +1 -1
  22. package/dist/knownState.d.ts +9 -1
  23. package/dist/knownState.d.ts.map +1 -1
  24. package/dist/knownState.js +29 -3
  25. package/dist/knownState.js.map +1 -1
  26. package/dist/localNode.d.ts.map +1 -1
  27. package/dist/localNode.js +3 -3
  28. package/dist/localNode.js.map +1 -1
  29. package/dist/queue/LocalTransactionsSyncQueue.d.ts +10 -9
  30. package/dist/queue/LocalTransactionsSyncQueue.d.ts.map +1 -1
  31. package/dist/queue/LocalTransactionsSyncQueue.js +53 -32
  32. package/dist/queue/LocalTransactionsSyncQueue.js.map +1 -1
  33. package/dist/storage/knownState.js +2 -2
  34. package/dist/storage/knownState.js.map +1 -1
  35. package/dist/sync.d.ts +1 -2
  36. package/dist/sync.d.ts.map +1 -1
  37. package/dist/sync.js +8 -3
  38. package/dist/sync.js.map +1 -1
  39. package/dist/tests/PureJSCrypto.test.js +1 -1
  40. package/dist/tests/PureJSCrypto.test.js.map +1 -1
  41. package/dist/tests/StorageApiAsync.test.js +11 -11
  42. package/dist/tests/StorageApiAsync.test.js.map +1 -1
  43. package/dist/tests/StorageApiSync.test.js +3 -3
  44. package/dist/tests/StorageApiSync.test.js.map +1 -1
  45. package/dist/tests/WasmCrypto.test.js +1 -1
  46. package/dist/tests/WasmCrypto.test.js.map +1 -1
  47. package/dist/tests/coPlainText.test.js +13 -14
  48. package/dist/tests/coPlainText.test.js.map +1 -1
  49. package/dist/tests/coStream.test.js +12 -12
  50. package/dist/tests/coStream.test.js.map +1 -1
  51. package/dist/tests/coValueCore.isCompletelyDownloaded.test.d.ts +2 -0
  52. package/dist/tests/coValueCore.isCompletelyDownloaded.test.d.ts.map +1 -0
  53. package/dist/tests/coValueCore.isCompletelyDownloaded.test.js +422 -0
  54. package/dist/tests/coValueCore.isCompletelyDownloaded.test.js.map +1 -0
  55. package/dist/tests/coValueCore.isStreaming.test.d.ts +2 -0
  56. package/dist/tests/coValueCore.isStreaming.test.d.ts.map +1 -0
  57. package/dist/tests/coValueCore.isStreaming.test.js +232 -0
  58. package/dist/tests/coValueCore.isStreaming.test.js.map +1 -0
  59. package/dist/tests/coValueCore.newContentSince.test.d.ts +2 -0
  60. package/dist/tests/coValueCore.newContentSince.test.d.ts.map +1 -0
  61. package/dist/tests/coValueCore.newContentSince.test.js +808 -0
  62. package/dist/tests/coValueCore.newContentSince.test.js.map +1 -0
  63. package/dist/tests/coreWasm.test.js +2 -2
  64. package/dist/tests/coreWasm.test.js.map +1 -1
  65. package/dist/tests/group.childKeyRotation.test.d.ts +2 -0
  66. package/dist/tests/group.childKeyRotation.test.d.ts.map +1 -0
  67. package/dist/tests/group.childKeyRotation.test.js +261 -0
  68. package/dist/tests/group.childKeyRotation.test.js.map +1 -0
  69. package/dist/tests/group.removeMember.test.js +1 -114
  70. package/dist/tests/group.removeMember.test.js.map +1 -1
  71. package/dist/tests/knownState.test.js +83 -11
  72. package/dist/tests/knownState.test.js.map +1 -1
  73. package/dist/tests/sync.auth.test.js +6 -6
  74. package/dist/tests/sync.load.test.js +67 -4
  75. package/dist/tests/sync.load.test.js.map +1 -1
  76. package/dist/tests/sync.mesh.test.js +41 -40
  77. package/dist/tests/sync.mesh.test.js.map +1 -1
  78. package/dist/tests/sync.peerReconciliation.test.js +1 -1
  79. package/dist/tests/sync.storage.test.js +29 -28
  80. package/dist/tests/sync.storage.test.js.map +1 -1
  81. package/dist/tests/sync.storageAsync.test.js +26 -25
  82. package/dist/tests/sync.storageAsync.test.js.map +1 -1
  83. package/dist/tests/sync.upload.test.js +96 -40
  84. package/dist/tests/sync.upload.test.js.map +1 -1
  85. package/dist/tests/testUtils.d.ts +12 -8
  86. package/dist/tests/testUtils.d.ts.map +1 -1
  87. package/dist/tests/testUtils.js +39 -8
  88. package/dist/tests/testUtils.js.map +1 -1
  89. package/package.json +3 -3
  90. package/src/SyncStateManager.ts +5 -2
  91. package/src/coValueCore/SessionMap.ts +39 -12
  92. package/src/coValueCore/coValueCore.ts +81 -66
  93. package/src/coValueCore/verifiedState.ts +139 -109
  94. package/src/coValues/group.ts +27 -4
  95. package/src/knownState.ts +49 -5
  96. package/src/localNode.ts +7 -5
  97. package/src/queue/LocalTransactionsSyncQueue.ts +77 -68
  98. package/src/storage/knownState.ts +2 -2
  99. package/src/sync.ts +7 -3
  100. package/src/tests/PureJSCrypto.test.ts +1 -2
  101. package/src/tests/StorageApiAsync.test.ts +11 -11
  102. package/src/tests/StorageApiSync.test.ts +3 -3
  103. package/src/tests/WasmCrypto.test.ts +1 -2
  104. package/src/tests/coPlainText.test.ts +13 -14
  105. package/src/tests/coStream.test.ts +12 -12
  106. package/src/tests/coValueCore.isCompletelyDownloaded.test.ts +590 -0
  107. package/src/tests/coValueCore.isStreaming.test.ts +353 -0
  108. package/src/tests/coValueCore.newContentSince.test.ts +966 -0
  109. package/src/tests/coreWasm.test.ts +2 -2
  110. package/src/tests/group.childKeyRotation.test.ts +431 -0
  111. package/src/tests/group.removeMember.test.ts +1 -184
  112. package/src/tests/knownState.test.ts +108 -11
  113. package/src/tests/sync.auth.test.ts +6 -6
  114. package/src/tests/sync.load.test.ts +79 -4
  115. package/src/tests/sync.mesh.test.ts +41 -40
  116. package/src/tests/sync.peerReconciliation.test.ts +1 -1
  117. package/src/tests/sync.storage.test.ts +29 -28
  118. package/src/tests/sync.storageAsync.test.ts +26 -25
  119. package/src/tests/sync.upload.test.ts +106 -40
  120. package/src/tests/testUtils.ts +43 -9
@@ -0,0 +1,353 @@
1
+ import { assert, beforeEach, describe, expect, test } from "vitest";
2
+ import {
3
+ SyncMessagesLog,
4
+ TEST_NODE_CONFIG,
5
+ loadCoValueOrFail,
6
+ setupTestAccount,
7
+ setupTestNode,
8
+ waitFor,
9
+ } from "./testUtils";
10
+ import type { RawCoMap } from "../exports";
11
+ import type { CoID } from "../coValue";
12
+
13
+ let jazzCloud: ReturnType<typeof setupTestNode>;
14
+
15
+ beforeEach(async () => {
16
+ // We want to simulate a real world communication that happens asynchronously
17
+ TEST_NODE_CONFIG.withAsyncPeers = true;
18
+
19
+ SyncMessagesLog.clear();
20
+ jazzCloud = setupTestNode({ isSyncServer: true });
21
+ });
22
+
23
+ describe("isStreaming", () => {
24
+ test("loading a small value should not be streaming", async () => {
25
+ const client = setupTestNode({
26
+ connected: true,
27
+ });
28
+
29
+ const group = jazzCloud.node.createGroup();
30
+ group.addMember("everyone", "writer");
31
+
32
+ const map = group.createMap();
33
+ map.set("hello", "world", "trusting");
34
+
35
+ const newSession = client.spawnNewSession();
36
+ const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
37
+
38
+ // For a small value, isStreaming should always be false
39
+ expect(mapInNewSession.core.isStreaming()).toBe(false);
40
+ });
41
+
42
+ test("loading a large value should be streaming until all chunks are sent", async () => {
43
+ const client = setupTestNode({
44
+ connected: true,
45
+ });
46
+
47
+ const group = client.node.createGroup();
48
+
49
+ await group.core.waitForSync();
50
+ client.disconnect();
51
+
52
+ const map = group.createMap();
53
+
54
+ // Generate a large amount of data that requires multiple chunks
55
+ const dataSize = 1 * 1024 * 100;
56
+ const chunkSize = 1024; // 1KB chunks
57
+ const chunks = dataSize / chunkSize;
58
+
59
+ const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
60
+
61
+ for (let i = 0; i < chunks; i++) {
62
+ const key = `key${i}`;
63
+ map.set(key, value, "trusting");
64
+ }
65
+
66
+ const newSession = client.spawnNewSession();
67
+
68
+ await loadCoValueOrFail(client.node, group.id);
69
+
70
+ const content = map.core.verified.newContentSince(undefined);
71
+ assert(content);
72
+ const lastChunk = content.pop();
73
+ assert(lastChunk);
74
+
75
+ for (const chunk of content) {
76
+ newSession.node.syncManager.handleNewContent(chunk, "import");
77
+ }
78
+
79
+ const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
80
+
81
+ expect(mapInNewSession.core.isStreaming()).toBe(true);
82
+
83
+ newSession.node.syncManager.handleNewContent(lastChunk, "import");
84
+
85
+ expect(mapInNewSession.core.isStreaming()).toBe(false);
86
+ });
87
+
88
+ test("loading a large content update should be streaming until all chunks are sent", async () => {
89
+ const client = setupTestNode({
90
+ connected: true,
91
+ });
92
+
93
+ const group = client.node.createGroup();
94
+ const map = group.createMap();
95
+
96
+ map.set("hello", "world", "trusting");
97
+
98
+ const knownState = map.core.knownState();
99
+
100
+ await map.core.waitForSync();
101
+ client.disconnect();
102
+
103
+ // Generate a large amount of data that requires multiple chunks
104
+ const dataSize = 1 * 1024 * 100;
105
+ const chunkSize = 1024; // 1KB chunks
106
+ const chunks = dataSize / chunkSize;
107
+
108
+ const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
109
+
110
+ for (let i = 0; i < chunks; i++) {
111
+ const key = `key${i}`;
112
+ map.set(key, value, "trusting");
113
+ }
114
+
115
+ const newSession = client.spawnNewSession();
116
+
117
+ await loadCoValueOrFail(client.node, group.id);
118
+
119
+ const content = map.core.verified.newContentSince(knownState);
120
+ assert(content);
121
+ const lastChunk = content.pop();
122
+ assert(lastChunk);
123
+
124
+ const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
125
+
126
+ for (const chunk of content) {
127
+ newSession.node.syncManager.handleNewContent(chunk, "import");
128
+ }
129
+
130
+ expect(mapInNewSession.core.isStreaming()).toBe(true);
131
+
132
+ newSession.node.syncManager.handleNewContent(lastChunk, "import");
133
+
134
+ expect(mapInNewSession.core.isStreaming()).toBe(false);
135
+ });
136
+
137
+ test("streaming a large value between two clients should be streaming until all chunks are sent", async () => {
138
+ const client = setupTestNode();
139
+ client.connectToSyncServer({
140
+ ourName: "initialClient",
141
+ });
142
+ const streamingClient = client.spawnNewSession();
143
+ streamingClient.connectToSyncServer({
144
+ ourName: "streamingClient",
145
+ });
146
+
147
+ const group = client.node.createGroup();
148
+
149
+ await group.core.waitForSync();
150
+ client.disconnect();
151
+
152
+ const map = group.createMap();
153
+
154
+ // Generate a large amount of data that requires multiple chunks
155
+ const dataSize = 1 * 1024 * 100;
156
+ const chunkSize = 1024; // 1KB chunks
157
+ const chunks = dataSize / chunkSize;
158
+
159
+ const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
160
+
161
+ for (let i = 0; i < chunks; i++) {
162
+ const key = `key${i}`;
163
+ map.set(key, value, "trusting");
164
+ }
165
+
166
+ const loadingClient = client.spawnNewSession();
167
+ loadingClient.connectToSyncServer({
168
+ ourName: "loadingClient",
169
+ });
170
+
171
+ await loadCoValueOrFail(loadingClient.node, group.id);
172
+
173
+ const content = map.core.verified.newContentSince(undefined);
174
+ assert(content);
175
+ const lastChunk = content.pop();
176
+ assert(lastChunk);
177
+
178
+ for (const chunk of content) {
179
+ streamingClient.node.syncManager.handleNewContent(chunk, "import");
180
+ }
181
+
182
+ await streamingClient.node.syncManager.waitForAllCoValuesSync();
183
+
184
+ const mapInLoadingClient = await loadCoValueOrFail(
185
+ loadingClient.node,
186
+ map.id,
187
+ );
188
+
189
+ expect(mapInLoadingClient.core.isStreaming()).toBe(true);
190
+
191
+ streamingClient.node.syncManager.handleNewContent(lastChunk, "import");
192
+
193
+ await waitFor(() => {
194
+ expect(mapInLoadingClient.core.knownState()).toEqual(
195
+ map.core.knownState(),
196
+ );
197
+ });
198
+
199
+ expect(mapInLoadingClient.core.isStreaming()).toBe(false);
200
+ });
201
+
202
+ test("should be false when getting streaming content that is already in the known state", async () => {
203
+ const client = setupTestNode({
204
+ connected: true,
205
+ });
206
+
207
+ const group = client.node.createGroup();
208
+
209
+ await group.core.waitForSync();
210
+
211
+ const map = group.createMap();
212
+
213
+ // Generate a large amount of data that requires multiple chunks
214
+ const dataSize = 1 * 1024 * 100;
215
+ const chunkSize = 1024; // 1KB chunks
216
+ const chunks = dataSize / chunkSize;
217
+
218
+ const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
219
+
220
+ for (let i = 0; i < chunks; i++) {
221
+ const key = `key${i}`;
222
+ map.set(key, value, "trusting");
223
+ }
224
+
225
+ await map.core.waitForSync();
226
+ const newSession = client.spawnNewSession();
227
+
228
+ const mapInNewSession1 = await loadCoValueOrFail(newSession.node, map.id);
229
+ await mapInNewSession1.core.waitForFullStreaming();
230
+
231
+ const content = map.core.verified.newContentSince(undefined);
232
+ assert(content);
233
+ const lastChunk = content.pop();
234
+ assert(lastChunk);
235
+
236
+ for (const chunk of content) {
237
+ newSession.node.syncManager.handleNewContent(chunk, "import");
238
+ }
239
+
240
+ const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
241
+
242
+ expect(mapInNewSession.core.isStreaming()).toBe(false);
243
+ });
244
+
245
+ test("should be false when getting streaming content that's not really streaming", async () => {
246
+ const client = setupTestNode({
247
+ connected: true,
248
+ });
249
+
250
+ const group = client.node.createGroup();
251
+
252
+ await group.core.waitForSync();
253
+ client.disconnect();
254
+
255
+ const map = group.createMap();
256
+ map.set("hello", "world", "trusting");
257
+
258
+ const newSession = client.spawnNewSession();
259
+
260
+ await loadCoValueOrFail(client.node, group.id);
261
+
262
+ const content = map.core.verified.newContentSince(undefined);
263
+ assert(content);
264
+
265
+ content[0]!.expectContentUntil = map.core.knownState().sessions;
266
+
267
+ for (const chunk of content) {
268
+ newSession.node.syncManager.handleNewContent(chunk, "import");
269
+ }
270
+
271
+ const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
272
+
273
+ expect(mapInNewSession.core.isStreaming()).toBe(false);
274
+ });
275
+
276
+ test("mixed updates should not leave isStreaming to true (3 sessions)", async () => {
277
+ const aliceLaptop = await setupTestAccount({
278
+ connected: true,
279
+ });
280
+
281
+ const group = aliceLaptop.node.createGroup();
282
+ const map = group.createMap();
283
+
284
+ map.set("count", 0, "trusting");
285
+ map.set("count", 1, "trusting");
286
+
287
+ await map.core.waitForSync();
288
+
289
+ const alicePhone = await aliceLaptop.spawnNewSession();
290
+
291
+ const mapOnPhone = await loadCoValueOrFail(alicePhone.node, map.id);
292
+
293
+ mapOnPhone.set("count", 2, "trusting");
294
+ mapOnPhone.set("count", 3, "trusting");
295
+ mapOnPhone.set("count", 4, "trusting");
296
+
297
+ await mapOnPhone.core.waitForSync();
298
+
299
+ const aliceTablet = await alicePhone.spawnNewSession();
300
+ const mapOnTablet = await loadCoValueOrFail(aliceTablet.node, map.id);
301
+
302
+ mapOnTablet.set("count", 5, "trusting");
303
+ mapOnTablet.set("count", 6, "trusting");
304
+ mapOnTablet.set("count", 7, "trusting");
305
+
306
+ await mapOnTablet.core.waitForSync();
307
+
308
+ map.set("count", 8, "trusting");
309
+ map.set("count", 9, "trusting");
310
+ map.set("count", 10, "trusting");
311
+
312
+ mapOnPhone.set("count", 11, "trusting");
313
+ mapOnTablet.set("count", 12, "trusting");
314
+
315
+ await map.core.waitForSync();
316
+ await mapOnPhone.core.waitForSync();
317
+ await mapOnTablet.core.waitForSync();
318
+
319
+ expect(map.core.isStreaming()).toBe(false);
320
+ expect(mapOnPhone.core.isStreaming()).toBe(false);
321
+ expect(mapOnTablet.core.isStreaming()).toBe(false);
322
+
323
+ const mapBranch = map.core.createBranch("test-branch");
324
+
325
+ const aliceTv = await aliceTablet.spawnNewSession();
326
+
327
+ const mapBranchOnTv = await loadCoValueOrFail(
328
+ aliceTv.node,
329
+ mapBranch.id as unknown as CoID<RawCoMap>,
330
+ );
331
+
332
+ mapBranchOnTv.set("count", 13, "trusting");
333
+ mapBranchOnTv.set("count", 14, "trusting");
334
+ mapBranchOnTv.set("count", 15, "trusting");
335
+
336
+ await mapBranchOnTv.core.waitForSync();
337
+
338
+ group.addMember("everyone", "reader");
339
+
340
+ const bob = await setupTestAccount({
341
+ connected: true,
342
+ });
343
+
344
+ const mapBranchOnBob = await loadCoValueOrFail(bob.node, mapBranchOnTv.id);
345
+
346
+ expect(mapBranchOnBob.core.isStreaming()).toBe(false);
347
+ expect(map.core.isStreaming()).toBe(false);
348
+ expect(mapOnPhone.core.isStreaming()).toBe(false);
349
+ expect(mapOnTablet.core.isStreaming()).toBe(false);
350
+
351
+ expect(mapBranchOnBob.get("count")).toBe(15);
352
+ });
353
+ });